Files
photoanalyzer/delivery_backlog/E09-documentation.md
domverse 370f966d29
Some checks failed
Test / suites (push) Failing after 3m12s
Test / container (push) Failing after 4m23s
E09: Product documentation backlog (#101)
2026-08-23 15:04:45 +02:00

4.5 KiB

E09 — Product Documentation

Concept phase: none. Like E08, this epic is a delivery addition rather than a product-scope change: the same application, documented well enough that somebody who did not build it can install it, understand it, and operate it without reading the source.

It does not change the product scope in INTEGRATED_PIPELINE_CONCEPT.md. No safety invariant moves, no schema changes, no new runtime capability. The one change to the running application is a documentation view and the static assets it needs.

Why the application serves its own documentation

The manuals describe an application that is reached over HTTP behind an access secret. Documentation that lives only in the repository is unreachable from the deployment it describes: an operator who has just been handed a URL and a secret has no Gitea account in front of them. So the same markdown files are both the repository's documentation and the deployment's /docs view, and neither is a copy of the other.

Decisions made in this epic

Markdown is the source. Everything is written as markdown under docs/, so it is reviewable in a diff, readable on Gitea, and renderable in the app. No documentation format that only a tool can read.

The renderer is vendored, not written and not fetched. marked (MIT, no dependencies, ships an ES module) is pinned and committed under frontend/js/vendor/. A CDN is not an option: the application is deployed to a network whose outbound access is not assumed, and default-src 'self' forbids it.

Diagrams are mermaid, rendered client-side, with the script boundary intact. The claim that mermaid requires 'unsafe-eval' was tested rather than believed: mermaid 11's bundle contains no eval( and no new Function — only a lodash Function("return this") global-detection fallback that short-circuits on globalThis and never executes. Rendered under this application's exact CSP, a flowchart produced a 15.8 KB SVG and raised no script-src violation. What it does raise is style-src: mermaid styles its output with an injected <style> element and style= attributes.

So script-src 'self' stays exactly as US07-02 and US08-01 left it, and style-src gains 'unsafe-inline'. That relaxation is bounded on purpose: with script-src intact no injected markup can execute, and with img-src, connect-src, and font-src all still 'self', the CSS-based exfiltration channels stay closed. What remains is defacement of a page the operator is already authenticated on.

The rejected alternative is recorded because it may become the better trade later: pre-rendering each diagram to a committed SVG with the already-installed Playwright (no Node toolchain needed) and serving that, which would leave CSP untouched entirely at the cost of a generator and a freshness check. If style-src 'unsafe-inline' is ever judged too much, that is the migration, and it does not change a single markdown file.

Screenshots are produced, not pasted. Every screenshot in the user manual is captured by Playwright from the real application against a temporary fixture library, by the same kind of code that already drives the browser suites. A screenshot nobody can regenerate is a screenshot that silently stops being true.

The documentation is held to the code by tests. Configuration keys, CLI commands, exit codes, API error codes, job and journal states, and module names all appear in both the code and the manuals. Each of those is cross-checked, so the failure mode of stale documentation is a red test rather than a misled operator.

Stories

  1. US09-01 — Serve the documentation inside the application
  2. US09-02 — Write the installation and operations manual
  3. US09-03 — Write the architecture overview
  4. US09-04 — Write the user manual with generated screenshots
  5. US09-05 — Automate documentation acceptance

Epic outcome

A deployed instance serves its own installation manual, architecture overview, and illustrated user manual at /app/#/docs, rendered from the same markdown files that are readable in the repository. Screenshots are regenerated from the running application, every documented command, setting, state, and error code is cross-checked against the code that implements it, and one gate fails the build when documentation and application disagree.