Documentation toolchain โ
How the documentation under docs/ is produced, and which parts of it you may edit by hand.
This covers the documentation toolchain only. For the general development loop โ lint, static analysis, tests, and what to run before a pull request โ see CONTRIBUTING.md.
Generated pages โ do not edit โ
These pages are written by composer docs:gen. Editing them directly is wasted work: the next docs:gen overwrites your changes, and so does composer docs:build, which runs docs:gen first.
| Page | Built from |
|---|---|
reference/annotations.md | src/Annotations/ docblocks + snippets/preamble_annotations.md |
reference/attributes.md | src/Attributes/ docblocks + snippets/preamble_attributes.md |
reference/spec-attributes.md | src/Spec/ docblocks + snippets/preamble_spec-attributes.md |
reference/processors.md | src/Processors/ docblocks + snippets/preamble_processors.md + prose in ProcessorGenerator |
reference/augmenters.md | src/Augmenter/ docblocks + snippets/preamble_augmenters.md + prose in AugmenterGenerator |
guide/examples.md | example sources + the per-example docs/examples/specs/*/Readme.md |
To change one of these, change its source and re-run composer docs:gen.
Note that some prose lives inside the generators rather than in any markdown file โ the -c and -D explanations in the "Configuration" sections are string literals in tools/src/Docs/Reference/{Augmenter,Processor}Generator.php.
Every generator renders through tools/src/Docs/Sections/; Renderer builds only the page frame โ preamble, headers, and the example blocks. Parameters render as a definition list: a description is prose with its own paragraph breaks and a type arrives HTML-escaped, so both need markup that reaches the page without the markdown parser reading it again.
Everything else under docs/ is hand-written โ including the top-level docs/examples/Readme.md, since only the per-example ones feed the generated page.
docs:gen is a drift check โ
The generators are deterministic, and committed output is expected to match its source. So:
composer docs:gen && git status --porcelain docs/Any output means the committed pages have drifted, and the regenerated version is the correct one. Worth running before opening a pull request.
Run it with an otherwise-clean tree, or scope the git status to the generated pages โ docs/reference/ holds hand-written ones too, so a directory-wide check reports your own edits as drift.
Commands โ
| Command | Notes |
|---|---|
composer docs:gen | regenerate the pages listed above |
composer docs:build | runs docs:gen, then builds the static site |
composer docs:dev | local preview โ long-running, prints its URL on startup and does not return |
What counts as a documented config setting โ
reference/augmenters.md and reference/processors.md list the options accepted by -c name.option=value. A setting qualifies when it is a constructor parameter that is not object typed:
- constructor parameters are the public configuration contract, by convention
- object typed parameters โ factories, resolvers, the generator โ are collaborators, not settings, and cannot be expressed as a CLI value
This is implemented once, in DocGenerator::configurableParameters(), and mirrors what Utils\Pipeline::getConfig() reports at runtime. The two are aligned by hand rather than by construction; if you change one, check the other. The check is that ./bin/openapi --mode spec -D src lists exactly the settings the reference page documents.
CLI behavior worth knowing โ
-D/--defaultsprints the resolved default config, but still requires thepathsargument:./bin/openapi --mode spec -D src, not./bin/openapi --mode spec -D.--versionsets the target OpenAPI version, not the tool version.- Help output is standard Symfony Console format, from
./bin/openapi -h. - Unknown
-ckeys are reported as warnings in spec mode, viaPipeline::configure(). In classic mode they are silently ignored, because configuration is routed throughGenerator::setConfig()instead. A typo in a classic-ckey fails invisibly.
Known rough edge โ
AugmenterGenerator::renderConfigSection() and ProcessorGenerator::renderConfigSection() are the same method twice, differing in the noun, the mode flag, and the programmatic example. A fix to one usually needs applying to the other.