Convert docs to AsciiDoc, localise into all five languages, add open-govpress rendering and publishing pipeline - #21
Open
736-c41-2c1-e464fc974 wants to merge 53 commits into
Open
736-c41-2c1-e464fc974 wants to merge 53 commits into
736-c41-2c1-e464fc974 wants to merge 53 commits into
Conversation
Migrates all guideline documents, the index page, and the four OSS checklists from Markdown/ODT to AsciiDoc. Internal cross-links and .odt checklist filenames were normalized to kebab-case during the conversion. Along the way this also fixes several pre-existing content issues: a duplicated content block in em002-1, broken .odt link filenames (typos/trailing spaces), and a couple of malformed footnote markers.
Adds a devenv.nix/.envrc setup providing git, gh, asciidoctor, pandoc, and an open-govpress wrapper (FHS-wrapped since the packaged Electron binary can't run natively on NixOS) plus a render-docs script that renders every tracked .adoc file to PDF. The open-govpress build is downloaded on demand from a GitHub Release and verified against a pinned sha256 rather than committed to the repo: at ~112MiB it clears GitHub's 100MB hard limit on regular git objects, and this repo is a fork, which GitHub's LFS policy blocks from uploading new LFS objects at all. Also expands each generated .adoc document's header with the full set of open-govpress front-block attributes (govpress-style, classification, title-logo-*, url-repo) that the render CLI reads directly from the document. Verified: devenv shell -- render-docs renders all 15 tracked .adoc files to valid PDFs with zero errors.
Renders every tracked .adoc file to PDF via open-govpress, downloaded from the same GitHub Release referenced in devenv.nix and verified against the same pinned sha256, under Xvfb (Electron has no headless mode). Runs on every push to main and on pull requests; PDFs are always uploaded as a build artifact, and on push to main they're also published to GitHub Pages with a generated index page. The download is cached across runs by its checksum. Requires a one-time manual step: enable Pages in repo Settings with source set to "GitHub Actions".
Converts all 87 checkbox glyphs (☐) across the four OSS checklist documents into AsciiDoc's [%interactive] checklist syntax. This renders as real, clickable <input type="checkbox"> elements in HTML, and open-govpress carries that through to genuine fillable PDF AcroForm widgets (/Widget /Btn annotations with /TU tooltips matching each label) rather than a static glyph. Where a checkbox and its label lived in separate table columns (the original ODT layout), merged them into a single list item so the checkbox has real label text to attach to -- AsciiDoc's checklist syntax doesn't register a bare "* [ ]" with no following text as a checklist item at all. Verified: checkbox counts match the original glyph counts exactly per file (25/14/9/39), all four files render with zero errors, and the actual PDF output via open-govpress contains real AcroForm checkbox widgets, not just visual squares.
Per the AsciiDoc documentation, footnotes are not officially supported in heading text and can produce incorrect/duplicate numbering. This was confirmed in practice: three headings had a footnote macro appended directly to the title (em002-4.adoc x2, em002-6.adoc x1), which caused Asciidoctor to emit duplicate `id="_footnoteref_N"` attributes (footnote 1 and 2 in em002-4.adoc were assigned twice) and leaked a stray "[N]" into the auto-generated table-of-contents entry for those sections. Relocated each footnote to the first natural mention of the same term in the following body text, which resolves to a clean, unique, sequential footnote numbering with no ID collisions and no TOC leakage. Also fixed a stray literal backslash before an en-dash in em002.adoc (`\–`) that was rendering verbatim instead of being silently dropped, since backslash-escaping only has meaning before AsciiDoc markup characters (confirmed correct usage of the two neighboring `\|` escapes, which legitimately escape a literal pipe inside a table cell -- left those as-is).
Every document is about to carry all five languages in one file, so a per-language directory has nothing left to name. Assets move with the documents, which keeps every `image::./assets/...` macro valid without touching a single one. Intra-repo `link:` targets switch from .adoc to .pdf: these links are read inside the rendered PDFs, sitting next to their siblings in the published per-language directory, where an .adoc target resolves to nothing. README.adoc keeps its .adoc target, being read on GitHub. publiccode.yml gains rm-CH, and its screenshot follows the new layout on main rather than a path pinned to a commit that predates the move. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Proves the mechanism the remaining documents will follow. `ifeval::` on
{lang} selects one body block of five; `open-govpress render --lang`
pins {lang} as a locked API attribute, so it outranks the document's own
:lang: and drives the guards. Verified: five 7-page PDFs, each with its
own title, TOC heading and admonition labels, and no leaked markup.
`:lang: en` moves to line 1, above the title. Attribute entries may
precede a doctitle, and this one has to: without it nothing defines
{lang} before the conditional title is read, so "auto" mode -- GitHub's
.adoc preview, the desktop app, a bare asciidoctor run -- would produce
a document with no title at all.
The four :title-logo-base:/-line1/-line2/-line3 attributes are deleted
rather than translated. Their presence is precisely what suppresses
open-govpress's own localisation (sniffStatesLogo), which is why the
title page read "Federal Chancellery FCh" in all five languages before
and now reads Departement/Amt, Département/Office, Dipartimento/Ufficio
and Departament/Uffizi. Note this trades the FCh naming for the tool's
generic placeholder; restoring it means putting that one string back
into the per-language chain by hand.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The render job becomes a matrix over en/de/fr/it/rm. Each renders the same sources with its own `--lang` into out/<lang>/, which is what keeps the outputs apart: `-o <dir>` names every PDF `<basename>.pdf` with the language nowhere in the filename. A new collect job merges the five artifacts into site/<lang>/ -- `merge-multiple: true` is load-bearing, without it each archive lands under its own pdfs-<lang>/ -- and writes the language chooser. Pages now deploys that tree instead of a flat pdfs/ directory. The index pages move out of the workflow into tools/site-index.sh and tools/site-root.sh so `render-docs` can build byte-identical ones locally; an index reimplemented in the workflow is an index that stops matching what you see before you push. site-index.sh labels each entry with the document's title read from that language's ifeval:: block, and lists only the PDFs actually present -- which is how a document that opts out of a language with `:l10n-languages:` stays absent from it rather than appearing as a blank PDF. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The index also drops the "(ODT, 92 kB)" annotations from its entries: they described the ODT downloads on the FCh website, and these links now point at the PDFs this repository renders. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Checkbox items keep their `[%interactive]` blocks and `* [ ]` markers so they stay clickable in every language, and the NCSC and Wikipedia footnote URLs follow the language where a translated page exists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bibliography entries keep their original-language titles, as citations should; only the descriptive glosses, the act names and the abbreviation table are translated, so EMOTA/EMBAG/LMETA/LMeCA and FOBL/BBL/OFCL/UFCL each read correctly in their own version. Fedlex and admin.ch URLs follow the language; the German-only intranet links do not. Also fixes two artefacts of the original Markdown conversion that were carried into the English text: a doubled letter in "aApplication owner" and a stray footnote number in "open repository14". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Also repairs three artefacts the ODT-to-Markdown conversion left in the English text: an untranslated "Section Fehler: Verweis nicht gefunden" where the Open Hub section was meant, an empty "in Section )" cross- reference, and a stray "-2" before a link macro. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ages Also fixes two typos carried over from the ODT conversion: "the roject is Free" in a cited section title, and a missing space after a full stop. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Also fixes four artefacts in the English text: two "Contribution Contributing" headings missing their colon, a doubled "in principle", the misspelt opdencode.de, and an eoperation.ch link that contradicted the eoperations.ch used elsewhere in the same document. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Also fixes a missing full stop that ran two sentences together in the multi-licensing note, and a stray section number left in the "(L) GPL-3.0 and IoT 7.4.4" heading. Note for FCh, left unchanged because it is a content error rather than a conversion artefact: annex D.5 attributes the ASTRA.EMSG road-asset application to MeteoSwiss, though both its repository and its description point to the Federal Roads Office. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Restructure docs/em002-4.adoc into the one-file/five-language shape:
a conditional doctitle, the shared attribute header, and one whole-body
ifeval:: block per language (en, de, fr, it, rm).
The embedded Developer Certificate of Origin quote block is verbatim
legal text and is deliberately left in English in all five versions.
Conversion artefacts repaired in the English source along the way:
* "must be uthorised by the project" -> "authorised"
* "Regarding the distribution key, see also Section *Fehler!
Verweisquelle konnte nicht gefunden werden.*." -> a plain reference
to the 'Cost allocation' section
* "Outcomes are feedback continuously to the community" -> "are fed
back continuously"
* "_[IzCab2023]_" -> "_[IZqCab2023]_", matching the reference used
elsewhere in the corpus
* a fedlex link whose display text pointed at /de while the target was
/en -> both /en
Verified with open-govpress render --lang {en,de,fr,it,rm}: 32-34 pages
each, correct localised title, no leaked ifeval::/{lang} markup.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Restructure docs/em002-6.adoc into the one-file/five-language shape:
a conditional doctitle, the shared attribute header, and one whole-body
ifeval:: block per language (en, de, fr, it, rm).
The Q/A table markers are localised along with the text (Q/A -> F/A,
Q/R, D/R, D/R), as are the abbreviation tables, which list the
abbreviations actually used in each language version.
Conversion artefacts repaired in the English source along the way:
* "thus subject to the legal requirements of a?" -> "of Art. 9 EMOTA?"
* a stray asterisk after "Open (Government) Data*"
* a question with no question mark ("On what basis would a third party
have to request the release of source code")
* "the DTI Section of the Federal Chancellery" -> "DTI Sector", the
form used everywhere else in the corpus
* a duplicated sentence about each authority having a strategy /
internal rules, merged into one
* "(at noted in Poledna/Schlauri/Schweizer" -> "(as noted in"
Verified with open-govpress render --lang {en,de,fr,it,rm}: 27-28 pages
each, correct localised title, no leaked ifeval::/{lang} markup.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Romansh block of docs/em002-6.adoc was the only one in the corpus citing the act as LMETA; the other fourteen documents use EMBAG, which is what TRANSLATING.md fixes as the Romansh short title. Align it, and cite the ordinance as OEMBAG to match. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reverts the earlier deletion of :title-logo-base:, :title-logo-line1:,
:title-logo-line2: and :title-logo-line3:. The documents state their own
Kennzeichnung again, as they did before this branch.
Because the documents state it, open-govpress does not localise it, so
lines 1 and 2 move into the per-language ifeval:: blocks alongside the
doctitle; base and the empty line3 stay in the shared header.
line2 now names the DTI sector rather than the generic "Office", per
the Federal Chancellery's own pages:
en Federal Chancellery FCh / Digital Transformation and ICT Steering DTI
de Bundeskanzlei BK / Digitale Transformation und IKT-Lenkung DTI
fr Chancellerie fédérale ChF/ Transformation numérique et gouvernance
de l'informatique TNI
it Cancelleria federale CaF / Trasformazione digitale e governance
delle TIC TDT
rm Chanzlia federala ChF / Transfurmaziun digitala e direcziun
da las TIC TDT
The sector abbreviation differs per language: DTI in German and English,
TNI in French, TDT in Italian and Romansh. The Romansh bodies used TDI
throughout; corrected to TDT to match bk.admin.ch.
TRANSLATING.md documents the table and cites the source page for each
language.
Verified: all 75 title pages (15 documents x 5 languages) carry exactly
the two expected lines, and the longest line (fr, 61 characters) still
sets on one line.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The sovereignty-level scale, the sovereignty matrix and the morphological box were tables drawn as pictures. As PNGs they were German-only inside the English, French, Italian and Romansh renders, and they carried defects that could not be fixed without an image editor: - "Trnsparenz" and "Altrnativoptionen" were baked into the level scale - the morphological box was screenshotted from PowerPoint with an object still selected, so grab handles and a rotate handle were visible Real tables translate, diff and spell-check. They are also announced as tables by a screen reader rather than as one opaque image. The new tables carry no block title: AsciiDoc auto-numbers those as "Table N.", which would have renumbered the existing Table 1 and broken the prose that cites it in five languages. They use the file's own convention of an italic caption below instead. Two content notes: - the morphological box disagreed with its own prose on the lettering of the Support dimension; the prose is followed here - the matrix cells are empty in the original too -- the picture carried only the row and column names Also drops docs/assets/em002-1/media/image2.png, referenced by nothing.
Every document carries all five languages in one file, but the diagrams did
not: they were 21 language-blind PNGs behind 105 `image::` macros, so only the
alt text was ever translated. Six of them shipped German text inside the
English, French, Italian and Romansh PDFs, and several carried defects that
could not be fixed without an image editor:
- Em002-3's extended licence tree had a box in Italian in the English version
("GPL o licenza permissiva") and another reading "Verb sements must flow
back?", a mangling of "Verbesserungen müssen zurückfliessen"
- Em002-2's security-grounds diagram carried an Italian footnote column
- Em002-2's third-party-rights diagram drew two branches both labelled "No"
out of one decision, which cannot be followed. Read as No -> rights
ownership, Yes -> further developments, matching its own footnote 1
- the document map existed in two diverged copies that disagreed about the
checklist file format and about the entire right-hand column
Diagrams are now `[mermaid]` blocks whose labels are translated text: they
diff, they spell-check, and they are reviewable in a pull request. Blocks are
authored bare -- no `theme`, no `%%{init}%%` -- so open-govpress supplies the
project palette. `classDef` appears only where colour carries meaning the
structure does not: the licence-compatibility graph and the document map,
whose legend was a colour key.
Not everything became a diagram. Three pictures were tables all along and are
now tables; the footnote columns and legends drawn inside four diagrams were
always prose and are now prose.
The four-Cs gears cannot be expressed in Mermaid and stay a picture, but a
generated SVG rather than a bitmap: `tools/make-four-cs-svg.sh` emits one file
per language, so the labels are real text. The PNGs it replaces had no editable
source in the repository at all.
The document map appeared 25 times across four documents -- the duplication
that let the two copies drift apart. It now lives in `docs/partials/`, one file
per language, included where it is shown. `render-docs`, the workflow and
`site-index.sh` exclude that directory, because a fragment is not a document
and would otherwise render as a near-empty PDF with a row in the language
index.
Also:
- publiccode.yml pointed at a deleted bitmap through a `/blob/` URL, which
serves HTML rather than an image. It now points at a rendered page.
- Em002-5's figure was captioned "Overview diagram of the OSS Article 9 EMOTA
process" while showing the four Cs; the alt text now describes the figure.
- Em002-4's sole remaining figure was numbered 2 with no 1, since figure 1 is
now a table. Renumbered; no prose cited it.
docs/assets/ goes from 1.45 MB of bitmaps to 233 kB, most of it the one
screenshot. Verified with `render-docs`: 15 documents x 5 languages, 75 PDFs,
no diagram falling back to a source listing and no partial rendered as a
document.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A Pages deployment replaces the whole site each time, and the workflow has no way to read back what is already published, so nothing older than the newest push to main could survive. Keeping earlier builds therefore needs somewhere to remember them between runs: the `site-history` branch, cloned by tools/site-publish.sh, overlaid with the build just rendered, and pushed back. The deployment stays a snapshot; its contents accumulate. A push to main keeps a copy under `commits/<short sha>/` and still owns the site root, so the root goes on meaning "current head of main" as it always has. A `v*` tag keeps a permanent copy under `tags/<tag>/` without disturbing the root -- `open-govpress-v*` tags the renderer binary released from this repo and is excluded by construction. Main builds are pruned to the newest KEEP_COMMITS; releases are never pruned, because a release needs a URL that keeps working. Each publish is a single parentless commit. The PDFs are re-rendered on every run and so are new blobs even when unchanged, so retaining history would add the full weight of the site to the branch every time, and deleting a pruned directory at the tip would reclaim none of it. The branch is derived output, and nothing in it cannot be re-rendered. This all works only because the generated site is position-independent: site-root.sh emits `<lang>/`, site-index.sh emits bare PDF names and `../`. Every link is relative, so a copy of the tree at any depth resolves without rewriting. Pull requests are untouched -- still the `site` artifact, still no deploy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every row on the language chooser, the document listings and the archive was an anchor styled `color: inherit; text-decoration: none`, so nothing marked it as clickable until the pointer was already over it and the hover tint appeared. Keyboard users got no cue at all. Each row now carries an underlined label in a link colour, and the affordance matches what the row actually does: a chevron where the row navigates, and a PDF badge on the document listings, where the row hands over a file instead. `:focus-visible` gets a visible ring, and the palette moves to custom properties with a dark-scheme variant so the link colour stays legible either way. The back arrows are labelled rather than bare glyphs -- "All languages", and its translation in each of the five languages on the document listings -- so the link says where it leads. The archive link in the chooser footer is likewise a coloured, underlined link rather than plain footer text. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This repository had no git-hooks block at all. loom has roughly 25 generic
hooks plus generated per-subdirectory ones, but almost all of those are
language hooks (Python, JavaScript, Helm) with nothing here to act on. What
follows is the subset that has something to check in a tree of AsciiDoc
documents, shell scripts and CI configuration.
Thirteen hooks: dos2unix, trim-trailing-whitespace, end-of-file-fixer,
check-executables-have-shebangs, check-shebang-scripts-are-executable,
check-symlinks, editorconfig-checker, nixfmt, check-json, yamllint,
ripsecrets, shellcheck and a repository-local check-asciidoc.
Deliberately left out: typos and vale. Both are English-language tools, and
four of the five languages here are not English, so they would report the
German, French, Italian and Romansh text as a wall of misspellings.
Three adjustments the hooks forced, each commented where it lives:
- .envrc is excluded from check-shebang-scripts-are-executable. It is
sourced by direnv, never executed; its shebang exists to get shell
highlighting and the shellcheck directive on line 2.
- The x-access-token URL in site-publish.sh carries a ripsecrets pragma.
The token is interpolated at run time, so the literal is a URL shape
rather than a credential.
- yamllint runs with truthy check-keys disabled, because GitHub Actions'
`on:` trigger key parses as the YAML 1.1 boolean true.
shellcheck runs as `-x -o all`, matching loom, which took 171 findings to
zero across the four existing scripts: 129 SC2250 (brace variable
references), 23 SC2312 (masked return values in command substitutions), 17
SC2292 ([[ ]] over [ ]), one SC2249 and one SC2035. The rewrite was checked
against recorded output of the previous revision across ten scenarios,
including the error paths: all ten are byte-identical, and the 31 generated
HTML files differ only in the CSS indentation that editorconfig-checker
required.
.editorconfig is loom's file plus two `indent_size = unset` sections. Strict
adoption flagged 22 violations, and those two cover the ones that are not
ours to reformat: the CC0 LICENCE text with its roman-numeral hanging
indents, and Markdown's list continuations, which align under the marker and
so are naturally odd. The four-space shell indentation needed no change.
check-asciidoc exists because git-hooks.nix ships no AsciiDoc linter of any
kind; the closest are prose linters. It parses every document once per
language rather than once, because the bodies are five ifeval:: blocks and a
conditional left unterminated in the Romansh block is invisible while parsing
as English. Batching one asciidoctor per language instead of one per
file-and-language keeps a full sweep near two seconds.
open-govpress and render-docs move out of devenv.nix into tools/. Shell
inside a Nix string is invisible to shellcheck and editorconfig-checker, and
between them those two scripts held most of the shell in this repository.
The wrappers now pass down only what has to come from Nix.
The lint workflow runs prek directly rather than `devenv test`: devenv
reports only that the git-hooks task failed, without naming the hook or the
reason, even with tasks."devenv:git-hooks:run".showOutput set.
The VS Code bundle is the editor counterpart of the hooks, so that what the
hooks reject is visible while typing. It sits behind env.cicd, which the lint
workflow sets, so CI does not download the editor closure on every push just
to run the hooks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Brings in the git hooks adopted from loom, along with the build archive and
the link styling on the generated pages.
Three conflicts, all of them the same underlying change meeting the same
files from the other side. This branch added `':!docs/partials/*'` to the
`git ls-files '*.adoc'` listings so that include:: fragments are not treated
as documents; main rewrote every one of those listings for `shellcheck -o
all` and moved one of them out of devenv.nix entirely.
- tools/site-index.sh: took main's rewritten loop, which reads the file
list from a variable rather than a process substitution, and moved the
pathspec onto that variable's assignment.
- devenv.nix: took main's side, which is now a two-line wrapper. The
listing this branch edited had moved to tools/render-docs.sh, so the
pathspec and its comment went there instead.
- .github/workflows/render-docs.yml merged without help.
check-asciidoc deliberately keeps listing the partials. The exclusions above
are about what counts as a *document* -- what gets rendered to a PDF and
takes a row in the language index -- whereas check-asciidoc only asks whether
a file parses, which a fragment can be asked as well as a document. All five
partials parse standalone in all five languages.
tools/make-four-cs-svg.sh arrived on this branch after main had already
tightened shellcheck to `-x -o all`, so the merge left it failing with 29
findings (26 SC2250, 3 SC2292) plus one editorconfig continuation indent.
Fixed here. The ten generated SVGs are byte-identical before and after, so
the graphics are untouched.
Verified by rendering English end to end: 15 documents, no partial rendered,
15 rows in the index.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A release tag points at the commit main has just published, so the tag run asked Pages to deploy a build version it had already deployed. Pages accepted it, reported success, and went on serving the earlier build -- the tag's archive reached the site-history branch but never the site. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…grams Feature/localise diagrams
GitHub now forces node20 actions onto the node24 runtime and warns on every run. Bumping to the majors that declare node24 natively -- rather than being coerced into it -- keeps the actions running on the runtime they were built and tested against. upload-pages-artifact has dropped dotfiles from the tar since v4; the published tree is only language directories, index.html, the archive and build-meta.txt, so nothing it carried before goes missing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The tool is hosted on GitLab, under swiss-armed-forces/cyber-command/cea; the comment named a github.com path that does not exist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Published as open-govpress-v0.0.12 on this fork, the same binary-host arrangement 0.0.7 uses: the electron-builder tar.gz is ~112MiB, over GitHub's 100MB object limit, and a fork may not upload new LFS objects. 0.0.7 stays published -- the document tags still pin it, so deleting it would make a historical checkout unrenderable. The version, URL and sha256 are pinned in two places that have to agree. Only the workflow's URL needed editing; devenv.nix interpolates govpressVersion into both the tag and the filename. Rendering is unchanged in shape -- all 15 documents in all five languages, same `<basename>.pdf` naming -- but the new print styling protects page breaks, so some documents grow a page or five (em002-4 goes 32 -> 37). Extracted text is word-for-word identical across the bump; only pagination and the running headers move. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Em002 carried six tables per language that were not tabular data. The objectives/measures matrix and the measure descriptions are label-and-prose pairs, the two reference lists and the abbreviations are key-and-value, and the source note is a single-cell box drawn around one sentence. They are tables because the document came out of Word, not because the content has columns. One of them was actively wrong in the published PDF. A first row followed by a blank line is an implicit header row, and 0.0.12 repeats a table's header on every continuation page -- so "Measure 1: Implement the Practical Guidelines for Open Source Software in the Federal Administration" printed as a heading above Measures 3, 6 and 8. It goes away with the table. Judgement calls: - The measure references inside the objectives -- "1)", "3)", "8)" -- stay literal text with their hard breaks. They point into the measure descriptions below, so a real numbered list would renumber them 1, 2, 3 and silently break every reference. - The two reference lists are plain `::`, not `[horizontal]`. An hdlist has no `cols`, so its term column is auto-laid-out and the longest key sets it for every row: `[KBB-Perimap]` came out wrapped as `[KBB-` / `Perimap]`, which `cols="20,80"` had been preventing. Abbreviations keep `[horizontal]`, where the longest key is `HERMES` and aligning them is the point of the list. - Every value sits on the line after its term. `[Em002]:: link:x[y]` on a single line parses as an unregistered block macro and the *entire list* disappears -- exit 0, no warning, nothing in the output. The glossary in Em002-6 is already written the long way. - `\|` became `|`. The backslash escapes a cell separator; outside a table it prints as a backslash. - Two lead-in sentences per language name the construct, so "the following table" became the following overview / Übersicht / liste / elenco / glista, and the hand-written "Source note:" prefix gave way to the localised NOTE label -- five maintained strings fewer. - The abbreviations list is one entry longer in German and one shorter in French, Italian and Romansh. That is not drift: abbreviations are localised, and it was already true before this change. Verified: the constructs move in lockstep across all five languages (-12 table fences, -6 cols attributes, +1 horizontal list, +1 admonition in each); the per-language word census is unchanged apart from the dropped header rows, the "Source note:" prefixes and the ten reworded lead-ins; 13 footnotes before and after in every language; asciidoctor parses all five. open-govpress render: en 21->17, de 21->18, fr 20->17, it 20->17, rm 20->17 pages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Em002-1 carried nine tables per language that hold no tabular data. Definitions,
the twelve potentials, the twelve challenges, the alternativeTo software types,
the GitHub Insights and Open Hub panels and the three support variants are each
a label and a paragraph of prose about it -- two columns because the document
came out of Word, not because the content has any. The cols="1,3a" split then
squeezed every label into a third of the measure and let no row break across a
page.
Judgement calls:
- The constellation/impact table in 4.1 stays a table. It is the one construct
here that is genuinely two-dimensional: eight parallel condition-and-consequence
pairs under two named axes, both sides full prose of comparable weight.
Converting it would drop the axis labels, and 0.0.12 repeating "Constellation |
Impact" on pages 13, 14 and 15 is the behaviour working. The tallest row is
~16 lines against ~45 usable, so the unrepairable in-cell break paged.css warns
about is not in reach. Its cramped cols="1,2a" split is left alone: widening it
is a rendering change of its own.
- The numbered potentials and challenges keep their authored numbers, guarded by
{empty}. "1. Digital sovereignty::" at line start is an ordered-list item, not
a term -- asciidoctor takes it as an <li> and swallows the definition into it.
"{empty}1. ...::" parses as a term and prints "1. Digital sovereignty"
unchanged. A real ordered list would have moved the numbering from the source
to the renderer, which no diff can show.
- All nine are plain `::`, not [horizontal]. An hdlist's term column is
auto-laid-out and the longest key sets it for every row; here that key is
"Open source software development (OSSD)".
- The one definition ending in a numbered list uses a bare `+` continuation.
Nothing follows the list inside that definition, so there is no paragraph for
the `+` to re-parent and no need for a `--` open block.
- No lead-in in any language names a table and the file contains no `\|`, so no
prose was touched: the word census is identical apart from the guards.
- Two footnotes per language, _[St2024]_ in the digital-sovereignty entry and
_[Gu2024]_ in section 6, close at the bracket of the reference they quote and
spill the remainder into the body. That is in the published PDF today and is a
separate fix.
Contrary to expectation there were no implicit header rows to reclaim here: all
nine tables write a row's two cells on two lines, so the line after |=== is never
followed by a blank. asciidoctor emits one <thead> per language before this
change and one after, both from the constellation table's explicit
options="header".
Verified: the constructs move in lockstep across all five languages (-18 table
fences, -9 cols attributes, +72 description terms, +6 bullets the cell marker had
been hiding); the per-language word census differs only by the 24 {empty} guards,
which render as nothing; asciidoctor parses all five. open-govpress render:
en 23->21, de 24->21 pages.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Annex D lists ten released projects, each as a two-column table with a fixed
six-field record: URL, federal authority, description, year, licence,
justification. Nothing in that is tabular -- there is no shared axis and no
second row to compare against, just one label-and-value pair per line, drawn as
a table because the document came out of Word. The section already says the
list will be replaced by metadata collected from publiccode.yml, and a
description list is a far better starting point for that than a grid: the field
names become terms rather than first-column cells.
Judgement calls:
- Plain `::`, not `[horizontal]`. An hdlist has no `cols`, so its term column is
auto-laid-out and the longest label sets it for every row. The labels here are
words, not abbreviations -- `Autoritad federala` (18), `Federal authority`,
`Autorité fédérale`, `Autorità federale` (17), `Bundesbehörde` (13). Rendered
with `[horizontal]`, English wraps to `Federal` / `authority` and Romansh to
`Autoritad` / `federala`, the term column eats about a third of the text
measure in every row, and the document stays 31 pages instead of 30. An hdlist
is also still a table, so `tr { break-inside: avoid }` would keep D.2's
four-line description and two-paragraph justification unbreakable.
- Every value sits on the line after its term, uniformly, including the bare `-`
used for "no justification given" and the six URLs.
- D.2 is the only multi-block record. Its description keeps its ` +` hard break
verbatim; its justification's second paragraph became a `+` continuation. No
fact-sheet contains a nested list, so no `--` open block was needed.
- The two real tables stay tables. The licence/copyleft/special-features
comparison and the use-case/licence/justification matrix are three columns of
genuinely parallel data, and both carry hand-numbered captions -- Table 1 and
Table 2 in five languages -- that a conversion would have had to renumber.
- Nothing else moved. No lead-in names the construct, there is no `\|` in this
document, and no table here had an implicit header row: D.2's blank line is
inside its last cell, not after its first row, so the header was never
promoted and the published PDF was not affected.
Verified: the constructs move in lockstep across all five languages (-20 table
fences, -10 cols attributes, +60 description-list terms in each); the
per-language word census is byte-identical before and after, so no prose moved
at all; asciidoctor parses all five languages; the five
`licences-for-pure-usage` anchors and their ten references still resolve, and
both numbered table captions are untouched. open-govpress render: en 31->30,
de 31->30 pages.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…n lists
The eight tables in "Detailed content for the community concept" have three
narrow columns naming a combination of fundamental decisions and one wide
column of prose proposing what to write for it. That is a lookup keyed by a
combination, not tabular data: nothing is compared down a column, and the
three key columns exist only to be read across. Laid out as `cols="1,1,1,4"`
they gave each key a quarter of the measure, so "Federal government", "Each
their own" and "Terzas persunas" wrapped to two and three lines beside a
paragraph of prose, and the four-cell header reprinted on every continuation
page -- 17 times for 8 tables in English, 20 in German.
Judgement calls:
- Each row is one description list entry whose term is the decision triple,
joined with " / ". The header row is not dropped but becomes an italic
legend above each list -- "Product management / Supplier as / Development
-- Proposal" -- so the triple stays decodable without a header. Every
localised label survives; the word census is byte-identical in all five
languages.
- The last row of the first table had an empty Proposal cell. It is a vertical
merge the Word conversion lost: that table's third column is shifted one row
down against its own prose, and shifting it back leaves the last row empty.
The two rows are now consecutive terms sharing one description, which is how
AsciiDoc spells a merged cell. A term with no description is not an option --
Asciidoctor silently adopts the next paragraph as its description, which
would have swallowed "The community concept should be a dynamic document".
The shift itself is left alone; it is a content error for the authors.
- The "a)" "b)" "c)" runs inside the proposals stay literal text with their
hard breaks. They are references into the [loweralpha] option lists of
"Fundamental decisions when building a community" -- "Product management c)
Open organisation" is option c there -- so a real list would renumber them
and break every reference. Same for the "*Handling reported errors*" run-in
headings, which mirror the sub-items of the concept structure in Section 4.
- Where a bullet list inside a value is followed by more prose, the list is
wrapped in a `--` open block. A bare `+` after a list re-parents the next
paragraph into the last <li>; it parses clean and the gate exits 0.
- Plain `::`, not `[horizontal]`. An hdlist has no cols and the longest term
sets the term column for every row; "Terzas persunas / Incumbensà / Terzas
persunas" would have set it for all forty.
- The morphological box stays a table. Dimension x options (a)-(e) is a real
matrix: it is read down as well as across, and the cells are deliberately
parallel between rows.
- Two terms per language begin with "- " ("- / Partner / -"). They are safe
because neither is the first item of its list; as a first item the "- "
would have parsed as a bullet.
Verified: the constructs move in lockstep across all five languages (-16 table
fences, -8 cols attributes, +40 dlist terms, +8 open-block fences in each);
the per-language word census is byte-identical before and after, in every
language -- the header labels moved into the legend rather than being deleted;
19 footnotes before and after in each language; asciidoctor parses all five.
open-govpress render: en 37->28, de 41->29, fr 37->29, it 37->29, rm 37->29
pages.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Em002-6 drew its entire body as tables: 48 two-column tables per language whose first column held nothing but a localised literal "Q"/"F"/"D" and "A"/"R", a legend table documenting that convention, and an abbreviation table. None of it is tabular. A question-and-answer pair is what `[qanda]` is for, and a key-and-value list is a description list; they were tables because the document came out of Word. The convention was also applied inconsistently: 190 of the 245 questions were wrapped in `*...*` and 55 were not, so the same construct printed bold or plain depending on which table you landed in. `[qanda]` supplies the emphasis, so the bold markers are gone and the inconsistency with them. Judgement calls: - The Q/A legend table and its lead-in sentence are deleted rather than reworded. They document a convention that ceases to exist, and the paragraph above already stands on its own. - Consecutive questions under one heading became a single list, not one list per question. A new list starts only after a heading, which is what `[qanda]` numbering would key off if it were ever turned on. - Abbreviations keep `[horizontal]`: the longest key is `HERMES`, and aligning them is the point of the list. - Six answers per language carry a nested list. Where content follows the list, the run is wrapped in a `--` open block -- a bare `+` after a list silently re-parents the next paragraph into the last item, and it parses clean. Where the list ends the answer, `+` continuation is enough. - The "two types of violations" answer keeps its real ordered list: the "Answer to 1:" / "Answer to 2:" paragraphs below point at it, and the numbers are genuinely sequential, so nothing is renumbered. - The abbreviation lists stay at 20 entries in German and English and 21 in French, Italian and Romansh. They are alphabetised by the localised abbreviation and some entries exist in one language only; that is not drift. - The glossary is untouched. It was already a plain description list. Verified: 250 tables removed, 120 `[qanda.unnumbered]` lists and 343 description terms added; the constructs move in lockstep across all five languages (-100 table fences, -50 cols attributes, +24 qanda lists, +1 horizontal list, +8 open-block fences each). The only census divergence is the description terms, +68 in English and German against +69 in French, Italian and Romansh, which is exactly the one-entry difference in the localised abbreviation lists. The per-language word census is unchanged apart from the 49 dropped cell markers in each language, the legend table and its lead-in, and the abbreviation header rows; 12 footnotes before and after in every language; asciidoctor parses all five. open-govpress render: en 28->20, de 29->21 pages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…o lists Em002-7 carried eight tables per language, three of which were not tabular data. The chapter's definition of digital sovereignty is one sentence with a box drawn round it, and the Annex A references and the Annex B glossary are key-and-value lists. They are tables because the document came out of Word, not because the content has columns. The five that really are matrices -- the sovereignty perspectives, the sovereignty levels, the perspectives x levels grid, the use-case classification and the WTO-threshold case distinction -- stay tables. Judgement calls: - The definition becomes a sidebar, not a NOTE. It is the chapter's operative definition of digital sovereignty, and an admonition label would demote it to an aside. - Both lists are plain `::`, not `[horizontal]`. An hdlist has no `cols`, so the longest key sets the term column for every row, and keys like `[KBB-Perimap]` and `[BITKOM2024]` are long enough to wrap mid-token. The glossary values also carry hard breaks and several paragraphs, which an hdlist handles badly. - Every value sits on the line after its term. `[A029]:: link:x[y]` on a single line parses as an unregistered block macro and the *entire list* disappears -- exit 0, no warning. The glossary in Em002-6 is already written the long way, and this one now matches it. - `\|` became `|`, twice per language, in [DigiV] and [OSBA-VK]. The backslash escapes a cell separator; outside a table it prints as a backslash. - Nothing was reworded. No lead-in or caption in any language names one of the three converted constructs, so the word census comes out byte-identical. - Tables 1 and 2 are left exactly as they were. Section 5 cites "Table 1" by number in running prose and both tables carry hand-numbered italic captions, so converting either would break the reference. - No implicit header row to fix here: the thead count is 5 before and after, and all five belong to tables that stay. Verified: the constructs move in lockstep across all five languages (-6 table fences, -3 cols attributes, +37 dlist terms, +2 sidebar fences in each); the per-language word census is completely unchanged; asciidoctor parses all five; 8 tables per language become 5. open-govpress render: en 28->27, de 30->28 pages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Em002-5 carries the only block title on a table in the document set. It renders as "Table 1. / Tabelle 1. / Tableau 1. / Tabella 1." in italic above the table, where Em002-2, Em002-3 and Em002-7 hand-write their captions in plain or italic text below. Nothing was wrong with the output -- what was wrong is that the anomaly read as an oversight, so the next pass over these tables would have "fixed" it and quietly lost the construct. Judgement calls: - The block title stays. 4d1056c avoided them because auto-numbering would renumber the tables whose numbers the prose cites -- true of Em002-7, which cites "Table 1" by number in all five languages. Em002-5 has one table, no hand-written number, and no prose in any language that cites one. - It is also the better construct here. Asciidoctor emits it as `<caption class="title">`, the table's accessible name, and open-govpress supplies both the number and the localised word for "Table" from its resource bundle. Converting would buy visual uniformity with five new hand-maintained translatable strings -- the opposite of the five this refactor removed from Em002. - There is no single convention to converge on in any case: Em002-2 writes "Table N - X", Em002-3 "Table N: X", Em002-7 "_Table N: X_", and Em002-1, Em002-6 and the four checklists caption none of their tables at all. - All three tables in Em002-5 and Em002-2 stay tables. The target-group matrix is genuinely two-dimensional -- nine target-group columns against fourteen documents, with X, (X), bold and section references in the cells. The two Em002-2 tables are closer to key-and-value, but their header rows name the columns in a way a description list cannot, and Table 1 already breaks across a page with its header correctly repeating. - The rationale goes in the source rather than only here, as a comment above the English block title with a localised pointer above the other four -- the same shape as the comments in docs/partials/document-map.*.adoc. Verified: comments only, no construct delta in any language; the word census differs only by the words of the five comments; asciidoctor parses all five; open-govpress render en 8 and de 9 pages, unchanged, with pdftotext output identical to the baseline apart from the title-page build hash. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The four checklists drew two constructs out of tables, and neither is tabular. Each `== Checklist` section was one `[cols="1,6"]` table -- `[cols="1,6,1"]` in Em002-2.3 -- whose first column, and third where it had one, was empty in every single row. The table did nothing but indent content that is already an `[%interactive]` checkbox list and prose, at the cost of a seventh of the text block and a border around every question. The other one was actively wrong in the published PDF. The 75 write-in areas are `[cols="1"]` tables holding a single empty `a|` cell, and a first row followed by a blank line is an implicit header row even with no `options="header"` -- so asciidoctor emitted a `<thead>` with one empty `<th>` and no body rows at all, and the theme paints `th` with `--table-header-bg: var(--ch-black)`. Every box in all five languages printed as a solid black bar; three of them on page 2 of the English Em002-2.1 alone. They are now `field::[]`, which stamps a real `/Tx` widget a reader can type into and save. Judgement calls: - `rows` comes from what each prompt asks for: 1 for a name, 3 for a contact or repository block, 4 for a description or for free-text comments. Em002-2.1 gets 1/4/3/4, Em002-2.3 gets 1/3/3/4/4, the other two 1/3/4. - `rows=1` is written out rather than left to the macro's default, which is also 1. The number is the judgement being recorded, and a reader comparing the 75 boxes should not have to know the default to see one was made. - The fields stay anonymous and every prompt stays prose above the box. A label is escaped plain text, so it cannot carry the ` +` hard breaks half these prompts use, and folding the prompts in would have meant 375 new translated strings for no gain. - Nothing inside a cell was re-indented or re-wrapped. The content was already flush left, so promoting it out is a pure deletion of wrapper lines: 75 inserted lines, all of them `field::[rows=N]`, against 995 deleted ones, all of them `|===`, `|`, `a|`, `[cols=...]` or blank. - Em002-4.1's wrapper carried two rows that were empty in both columns. They were spacing, and they go with the table rather than becoming blank paragraphs. - Several checklist questions still tell the reader that something "can be named in the textbox" where that row never had one. Left alone: this converts the 75 boxes that exist, and adding boxes is the document owners' call. Verified: asciidoctor parses all four in all five languages; 10 ifeval and 10 endif per file; compare-census reports the same delta in en/de/fr/it/rm with no DIVERGES -- table fences -10/-8/-12/-8, cols attributes -5/-4/-6/-4, fields +4/+3/+5/+3 per language -- and 06_checkbox, 08_ulist and 17_footnote all unchanged. The word census differs only by the tokens `field`, `rows` and the numerals 1/3/4 the new macros introduce: not one word of prose, in any language. 435 `* [ ]` items before and after. In the rendered PDFs, per language: /Btn widgets 25/14/9/39 before and after, /Tx widgets 0 -> 4/3/5/3, all empty, multiline set exactly on the rows>1 boxes, and pdf.js fills all 15 and reads them back unchanged. open-govpress render, identical in all five languages: Em002-2.1 5->5, Em002-2.2 6->5, Em002-2.3 5->5, Em002-4.1 6->6 pages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Collaborator
|
Thanks for the MR. Great! @elbaer will be doing a review. |
Published as open-govpress-v0.0.13 on this fork, the same binary-host arrangement 0.0.7 and 0.0.12 use: the electron-builder tar.gz is ~112MiB, over GitHub's 100MB object limit, and a fork may not upload new LFS objects. Both predecessors stay published -- commits and document tags still pin them, so deleting either would make a historical checkout unrenderable. The version, URL and sha256 are pinned in two places that have to agree. Only the workflow's URL needed editing; devenv.nix interpolates govpressVersion into both the tag and the filename. Rendering is unchanged in shape: all 15 documents render in en, same `<basename>.pdf` naming, and em002-1 -- the one document rendered under both versions before the rest were overwritten -- comes out at the same 21 pages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three citations in Em002-1 named keys that are defined nowhere in the document set, in any language, so they printed in the published PDFs as bracketed labels with no matching entry in any reference list: BIT-KOM2023 for BITKOM2023, BITCOM2024 for BITKOM2024, and PE1999 for Pe1999. Each was wrong in all five languages, once per language. - BIT-KOM2023 was copied out of the BITKOM2023 entry's own body, which carries a stray "BIT-KOM" from a line break in the source PDF. The following paragraph already cites the same guide correctly. - BITCOM2024 keeps its year: the footnote on the same line gives the URL and section of the 2024 Annex A entry, so BITKOM2024 is the target, not the 2023 entry in the general references. - BITKOM2023 and BITKOM2024 are the same Bitkom guide at the same URL, recorded under two years in two different reference lists. Left as is; harmonising them is an editorial decision, not a broken reference. - BITKOM2024 is defined only in Em002-7's Annex A, so it resolves within the document set but not within Em002-1 alone. Left for the owner. - Em002-1's bare [OSI2019] is also undefined and is left alone here. It does mean the licence list, so OSI2024 is the likely target, but it also wants the italic markers the house style uses and belongs in its own change. - The two footnotes per language that close early on a bracketed citation are a separate known defect and are untouched. Verified: asciidoctor parses all five languages; 10 ifeval and 10 endif; compare-census reports every construct delta zero in all five languages, as a pure key substitution should; the word census differs by five tokens per language and identically across them (bit 1>0, kom2023 1>0, bitcom2024 1>0, bitkom2023 1>2, bitkom2024 0>1) with no other word moved. The Pe1999 fix is case-only and therefore invisible to the census by construction, so it was checked in the rendered PDF text instead. open-govpress render: en 21->21, de 21->21 pages. 15 lines changed, 3 per language, none reflowed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Em002-7 asks the reader to place a technology on a five-by-five grid of
sovereignty perspectives against maturity levels, and then gives them a table
whose twenty-five cells are empty -- not empty as in "fill this in", empty as in
there is nothing there. On paper there is no box to tick and on screen there is
nothing to click, so the one part of the document that is a form was the one part
that could not be used. The two reference tables above it are not tabular either:
a one-character index, a short name and a sentence, with the sentence squeezed
into the last of three columns and the prose reduced to under half the measure.
The grid keeps its table -- it is a real matrix -- but every cell becomes an
`[%interactive]` checkbox, and the header now names each level instead of
printing a bare 0 to 4, so the reader never has to flip back to the scale. The
two reference tables become description lists.
Judgement calls:
- The cell body is `* [ ] {empty}`, not `* [ ]`. Asciidoctor emits no list item
for a checklist entry with empty principal text, so `* [ ]` alone silently
produces no <input> and nothing for the form stamper to find -- a grid of
twenty-five nothings that parses clean and exits 0. `{nbsp}` also works but
leaves a trailing space that pushes the box off-centre.
- Level 0's short name is truncated at the slash in the grid header --
"Externally determined", not "Externally determined / non-sovereign". The
alternative phrasing is a definition, not a column label, and the full form is
one line away in the list above. Every short name is lifted verbatim from its
own language's levels list; none was translated afresh.
- Both lists are plain `::`, not `[horizontal]`. The longest term is
"4 -- Souverain sur le plan systémique", nowhere near short enough for an
hdlist's auto-laid-out term column.
- The standalone caption under the levels table is dropped rather than folded in.
In all five languages the lead-in sentence two lines above already contains it
word for word. The matrix caption stays: that table still exists.
- The widgets carry generic accessible names ("Checkbox 1" ... "Checkbox 25"),
because a bare box has no label text. The meaning is in the row and column
headers, which the table structure already exposes.
- [DigiO] was cited in the English, French and Italian prose and defined nowhere;
the entry has always been [DigiV], which German and Romansh already cited. Only
the citations are corrected here -- the definition lives in Annex A, which is
being centralised separately.
- Left alone: the sentence after the grid still speaks of "the empty cells",
which is now half true. Rewording it in five languages is a prose change, not a
table refactor, and would have hidden in this diff.
Verified: asciidoctor parses all five languages; the constructs move in lockstep
(-4 table fences, -2 cols attributes, +25 checkboxes, +10 description-list terms
in each); the word census differs only by the four dropped header rows, the five
dropped captions, the level short names now appearing a second time in the grid
header, twenty-five {empty} placeholders and the three DigiO citations. Each
rendered PDF carries exactly 25 /Btn widgets where it carried none, all on-page,
all still ticked after pdf.js fills, saves and reopens them, with the header row
repeating across the page break in en, fr, it and rm. open-govpress render:
en 27->27, de 28->28, fr 29->29, it 28->27, rm 28->27 pages.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…entries tools/check-asciidoc.sh passed `--out-file /dev/null`, and asciidoctor special-cases that one path: lib/asciidoctor/convert.rb reads `when '/dev/null' then return load input, options`, so it returned the loaded document without ever converting it. The gate was parse-only by construction and no conversion-stage diagnostic could reach it at any --failure-level, unresolved <<xref>>s above all. The header comment blamed the log level instead -- right about the symptom, wrong about the cause, and wrong in the way that stops the next person from trying. Nothing at all connects a citation to its reference entry. `[St2024]` is literal text and `[St2024]::` is a description-list term; a citation whose entry was never written renders as a bracketed label pointing at nothing, in silence. With the reference lists moving into partials selected by `tags=`, an entry can now also go missing because a selector did not pick it -- silently again, in asciidoctor and open-govpress alike, at every log level. Judgement calls: - `--out-file -` with stdout discarded, plus `--verbose` and `--failure-level=INFO`. An unresolved cross-reference is logged at INFO, so INFO has to be fatal and has to be logged at all; asciidoctor has no --log-level and -v is its only lever on the logger. Converting for real and throwing the HTML away costs nothing measurable -- the parse is the expensive half. - The 175 DEBUG lines per run that -v turns on are filtered, not explained away in a comment. They come from open-govpress's own extensions, which plain asciidoctor does not have; DEBUG sits below INFO and can therefore never change the exit code, so they are noise by construction and enough of it to train people to stop reading this gate. The filter keys on the level label, not the three messages, so a fourth extension needs no change here. - Still one asciidoctor per language, not per file-and-language. Ruby startup dominates at 0.29s an invocation whatever the document's size, so batching costs 2.5s where 100 runs cost 29s. The price is one message: the INFO line for a dangling xref names the key and nothing else. Worth it -- the key is its own locator via `git grep -n '<<key'` -- and WARN and ERROR, which is nearly everything else, still name file and line. - The citation check lives here rather than in a sibling script, because a sibling would need wiring in devenv.nix and an unwired checker is a half-done one. - It flattens the corpus with asciidoctor-reducer rather than matching `ifeval::` and `tags=` by hand. Reducer applies the same tag selection that will silently drop an entry, so a mis-tagged include shows up as a citation losing its definition instead of being reproduced by a second implementation with the same blind spot. - Always the whole tracked corpus, whatever arguments were passed: only Em002 and Em002-7 carry reference lists and every other document defers to them in prose, so a per-document universe of entries would report every citation in Em002-1. - OSI (Em002-3) and OSI2019 (Em002-1) stay in a known_unresolved list rather than making the check advisory. Everything else is fatal today; those two await an owner decision, and an entry that no longer matches is reported as stale, so the list cannot quietly outlive the problem it documents. - TRANSLATING.md's link rule said relative `.pdf` targets are followed inside the rendered PDFs. The rationale holds, the behaviour does not: they serialise as `app://govpress/...` and are dead. Stated as what is true today and what 2271c81 will make true, rather than quietly rewriting 1,100 targets around a bug that is already fixed upstream. Verified: check-asciidoc.sh exits 0 over all 20 tracked .adoc in all five languages in 5.1s, against 2.5s parse-only before; prek run check-asciidoc --all-files passes. In a scratch copy it exits 1 on `<<no-such-anchor>>` in all five languages, where the old invocation exited 0; on a fabricated [Nope2026]; on an entry left out by `tags=x,y`, which the parse half still reports nothing about; and on a stale known_unresolved entry. It finds exactly two unresolved citations, OSI in em002-3 and OSI2019 in em002-1, in all five languages and nowhere else, with no false positives, and the same result under en_US.UTF-8 and C. The tag-separator figures in TRANSLATING.md are measured through open-govpress render: the same fragment, 2 pages with `tags=a,b` and 7 with `tags=a;b`, no warning either time. shellcheck -x -o all and editorconfig-checker pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rtials
Three lists were kept by hand in Em002, Em002-6 and Em002-7, and all three had
drifted. The abbreviation lists agreed in English and in no other language. The
glossary disagreed on five entries in English and on up to eight per translation.
Of the seven reference keys that appear in more than one list -- not three: Annex
A also duplicates four entries from Em002's tools list -- every single one
differed in title, in link form, or in both. Em002 writes link:url[text]; Annex A
writes +url+, which renders as inert text and has lost [DigiV]'s URL entirely.
docs/partials/{abbreviations,glossary,references}.<lang>.adoc now carry the union,
tagged per consuming document. References needs three tags, not two, because Em002
prints two separate reference sections with the document map wedged between them.
Judgement calls:
- Abbreviations needed tags after all. The two lists are identical only in
English; Em002 has 20 en, 21 de, 19 fr/it/rm and Em002-6 has 20 en, 20 de,
21 fr/it/rm, because the two documents genuinely cite different things. A plain
include would have silently changed four of five languages.
- HERMES takes Em002-6's italic book title. OU/OE/UO takes Em002-6's "federal
office", which is what an organisational unit is here. OSSD takes Em002's
capitalisation, matching every neighbour in its list.
- The glossary follows Em002-6, which Em002-7's own Annex B names as the general
glossary. Em002-7 wins only for Branch, which ends its sentence, and Market
analysis, which keeps its hard break.
- [KBB-KV] takes Em002-7's wording: Em002 left it in German in all five languages
and doubled a word. The other six shared references keep Em002's, which carry
the URLs Annex A dropped or made inert.
- Both [KKB-MB] and [KBB-MB] are kept here. They are the same sheet at the same
URL and folding them touches citations in two other documents, so it is its own
change.
- [BITKOM2023] and [BITKOM2024] are the same Bitkom guide at the same URL under
two keys, cited both ways from Em002-1. Both kept, pending a ruling on which
year is right -- picking one would assert an edition nobody here can verify.
- Where the two documents use different headwords for one concept -- AV/AO,
Kollaboration/Collaboration, Subscription/Subskription and their Romance
equivalents -- both entries survive, one per tag. Unifying them is a translation
decision, not a formatting one.
- Six abbreviation entries are emitted twice in fr, it and rm: the two lists sort
them differently and no single order satisfies both. They collapse once the
source lists are alphabetised, which neither currently is.
Verified: 15 files, 5 languages, 3 lists, tag names parallel across all five with
no overlapping or unclosed region. 35 of 35 consumer/language selections render
under asciidoctor --safe -v --failure-level=INFO with no warning and reproduce
their document's list contiguously and in order; English matches the recorded
pre-change lists at 20/20/12/21/19/27/16 entries. 45 of 45 open-govpress render
--lang <xx> --strict checks pass, including that the two abbreviation selections
coincide in en and diverge in de, fr, it and rm. A deliberately wrong tag name is
caught: WARNING tag not found, exit 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
[OSI2019] in Em002-1 and [OSI] in Em002-3 were cited with no reference entry anywhere in the corpus, so both rendered as a bracketed label with nothing behind it. [KKB-MB] in Em002-3 named the same information sheet at the same URL as [KBB-MB], one letter apart -- KBB is Kompetenzzentrum Beschaffungswesen Bund, and the sibling keys are [KBB-KV] and [KBB-Perimap], so KKB was the typo. check-asciidoc.sh has carried OSI and OSI2019 in known_unresolved since it learned to check citations; that list is now empty. - [OSI2019] becomes [OSI2024], the Open Source Initiative's licence list by name, because the sentence is about "approximately 80 licences recognised by the OSI" and that entry is the list of them. Nothing dated 2019 exists. - [OSI] becomes [Pe1999], not [OSI2024]: the sentence introduces the five criteria of the Open Source Definition, and [OSI2024] is an alphabetical licence index, not a definition. Em002-1 already cites [Pe1999] for exactly this content. Perens wrote the Open Source Definition and the OSI adopted it, so the surrounding prose attributing it to the OSI stays correct, and reads correctly in all five languages -- the key is a trailing parenthetical in each. - Both Em002-1 citations take the italics the house style uses; _[Pe1999]_ sits in the same sentence and was already italic. Em002-3's were italic already. - The footnote:[https://opensource.org/] on the Em002-3 citation stays put. The prose still attributes the definition to the OSI, so the link is still right. - known_unresolved keeps its mechanism as an empty array rather than being deleted. The next citation an owner has yet to rule on needs the park and the stale-entry guard both, and the comment now carries a worked template instead of a TODO. - Em002-3's _[Pe1999]_footnote:[...] still prints its underscores literally -- constrained emphasis cannot close against a following word character. That is how _[OSI]_footnote: rendered too; left alone here so this stays a key substitution. Verified: tools/check-asciidoc.sh exits 0 over the whole tracked corpus in all five languages with known_unresolved empty; in a scratch copy it still exits 1 on a fabricated [Nope2026] and on a stale known_unresolved entry, so neither half of the mechanism was weakened. compare-census.sh reports every construct delta as zero in all five languages for both documents. The word census differs by exactly 20 lines, 4 per language: osi2019 -> osi2024, kkb -> kbb, osi 3 -> 2, pe1999 +1. shellcheck -x -o all and editorconfig-checker pass. Rendered en and fr: em002-1 21 and 21 pages, em002-3 30 and 31, all four unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Em002, Em002-6 and Em002-7 still carried the abbreviation, glossary and reference lists that 6dec878 extracted into docs/partials/, so the union was written down twice and none of the drift that commit reconciled had reached a single PDF. All 35 lists -- three documents, five languages -- are now one include of their own language's partial, selected by tag. Em002-7 also defined [KKB-MB] and cited [KBB-MB] six times without defining it: six bracketed labels pointing at nothing, per language, in every shipped PDF. KBB is Kompetenzzentrum Beschaffungswesen Bund and Annex A's own neighbours are [KBB-KV] and [KBB-Perimap], so [KKB-MB] was the deviant, and the two were one information sheet at one URL. Judgement calls: - [KBB-MB] survives the fold verbatim. [KKB-MB] had nothing it lacked -- same sheet, same URL, written as +url+, which renders as inert text -- so the merge is a deletion plus a tag, not a rewrite. - The surviving entry gains ref-em002-7 where it already stands rather than moving, because it sits between [BBL-CL] and [KBB-KV] in the file, which is exactly the slot [KKB-MB] held in Annex A. Annex A's order is unchanged. - [horizontal] stays in the consuming document, on the line above the include. The attribute carries across the include and across the leading // tag:: comment, and it is the document's layout choice, not the list's. - One tag per include, never two. tags=a,b is neither an error nor a warning and silently means tags=a, so the spelling that cannot be got wrong is the one used everywhere. - Em002's "Visual overview of OSS tools:", document map and "Figure 3:" caption stay in Em002. They sit between the two reference sections and belong to neither list. - Romansh Em002-7 grows by one page. Annex A gains two to four rendered lines in every language -- the [BBL-WL] and [BBL-CL] descriptions and [KBB-Perimap]'s URL that 6dec878 restored -- and only Romansh crosses a page boundary. - Em002-2 and Em002-3 cite the same sheet and are not touched here; those citations are fixed in their own changes. Verified: 35 of 35 lists replaced, 10 ifeval and 10 endif per document, +3/+2/+2 includes per document in all five languages, 5/5/0 [horizontal] kept, 0 comma tag separators. tools/check-asciidoc.sh exits 0 over the whole corpus in 5.5s and prek passes; in a scratch copy it exits 1 on the [KKB-MB] citation put back, and on one misspelled tag, which it catches twice -- WARNING tag not found and three Annex A citations losing their entries. compare-census.sh: 09_dlistterm -66/-67/-65/-65/ -65, -32/-32/-33/-33/-33 and -37 uniform, the two DIVERGES lines decomposing exactly as 19+27+abbr and abbr+12 over the abbreviation lists' own per-language lengths of 20/21/19/19/19 and 20/20/21/21/21; 18_link -28/-1/-1 uniform; 11_horizontal unchanged. The word census is negative in every language -- 1463/ 1419/1542/1498/1532, 598/551/681/640/654 and 1099/989/1217/1140/1204 words out -- and every word with a positive delta is include-directive syntax: all 25 added lines in Em002 and Em002-6 are includes, and Em002-7's five others differ from their predecessors by KKB->KBB alone. Extracted from the PDFs, all 35 rendered lists are identical before and after in all five languages, entry for entry, with the single intended difference [KKB-MB] -> [KBB-MB]; the English lists still match the recorded pre-change lists at 19/27/20 and 20/12 and 16/21 entries. Within Em002-7 alone, [KBB-MB] goes from cited 6 times and undefined to cited 7 times and defined, in all five languages. Page counts 17/20/27 en, 18/21/28 de, 17/21/29 fr, 17/21/27 it, 17/21/27->28 rm. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Em002-2 names the procurement information sheet three times per language as ++[++KKB-MB++]++ -- the brackets escaped, so it prints as the literal text [KKB-MB] rather than parsing as anything. That is why nothing caught it: the citation check in check-asciidoc.sh reads bracketed citations, and an escaped one is not a citation. With [KKB-MB] folded into [KBB-MB] everywhere else, these fifteen references would have pointed at a key that no longer exists in any reference list. - Only the key changes. The escaping stays, because these are prose mentions of a document alongside ++[++BBL-WL++]++, not citations meant to resolve, and unescaping them would be a different decision about how Em002-2 refers to the rest of the set. - Em002-2 defines no reference list of its own and cites no literature, so it gains nothing from the partials and is not wired to them. Verified: 15 occurrences to 0, and 15 new ++[++KBB-MB++]++ in their place; asciidoctor parses all five languages; compare-census.sh reports every construct delta as zero in all five; the word census differs by one token per language, kkb 3 -> kbb 3, and nothing else. After this, KKB-MB appears nowhere in docs/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every rendered document carried a byte-identical 19-attribute header, and seven of those lines never reached a reader. `:govpress-style: report` makes the tool apply two attribute files of its own before the document's header -- bundled in app.asar as styles/common.adoc and styles/report.adoc -- and between them they already set `govpress-front-block: report`, `title-page`, `pdf-theme: basic`, `title-logo: logo-ch-black.svg`, `toc`, `sectnums`, `toclevels: 3`, `sectnumlevels: 3`, `experimental` and more. They are applied softly, so a document's own header does outrank them; ours simply restated values already in force. The two files are byte-identical in open-govpress 0.0.12 and 0.0.13. Judgement calls: - `:toc:`, `:toclevels:`, `:sectnums:` and `:sectnumlevels:` stay, and gain a comment saying why they look redundant but are not. GitHub and GitLab render every .adoc in the repository with plain Asciidoctor, which applies none of open-govpress's style files: without these four the web view of the source loses its contents list and its section numbering. The rule applied throughout is drop what no renderer reads, not drop what the PDF does not need. - `:title-logo-base: black` goes because it restates the default -- with the attribute absent the converter's black-or-color normaliser returns black -- and `:title-logo-line3:` because it is empty and `:title-logo-line1:` already selects the explicit-lockup branch. - `:experimental:` goes even though the web view would read it: no document uses kbd:, btn: or menu:, so it is inert in both pipelines. - `:classification:` stays, empty, with a comment recording that this is deliberate. It renders exactly as omitting the line would; it is kept so a document answers the question rather than leaving it unasked. The cost, recorded because it is real: the documents now depend on the tool continuing to supply `title-logo` for this type. A future open-govpress that stopped would drop the coat of arms from the title page silently, rather than fail. Verified against the working tree before the change, at open-govpress 0.0.13: - Plain `asciidoctor --safe -a lang=...` output is byte-identical for all 15 documents in both en and de -- 30 of 30 renders -- so the GitHub and GitLab view does not move. - Rendering all 15 in en gives the same page count for each (211 pages) and every page pixel-identical at 60 dpi except page 1, which differs only in the `Version 2.0-<checksum>` string the tool derives from the source bytes and which therefore must change. Confirmed per document by diffing pdftotext output with that one token masked. - check-asciidoc.sh passes; de renders with the Kennzeichnung intact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`:url-repo:` was defined in all 15 documents and referenced by none, while the
repository URL was spelled out literally 70 times across them -- four different
forms of the same address, in five languages, with nothing holding them
together. The attribute is open-govpress's own documented idiom for exactly this
("Your own attributes are allowed and save repetition", showcase.adoc), so it was
already the right tool, just unwired.
All 55 body occurrences now derive from the attribute:
{url-repo} 5 Em002, the plain GitHub URL in prose
{url-repo}/releases 5 README
{url-repo}/tree/main 40 the README link every document carries
{url-repo}/tree/main/docs 5 Em002-5
Judgement calls:
- The value becomes the repository root rather than staying at /tree/main. The
root is what showcase.adoc uses for this attribute, and it is the only value
all four forms can be built from -- at /tree/main the five /releases links and
the five bare ones in Em002 would have had to stay literal. The repository
being a fork makes the owner segment the part most likely to move, which is
precisely the part now written once.
- The four checklists, index and README_template lose the attribute instead of
gaining a reference. None of them links to the repository at all, so defining
it there would recreate in six files the unused-attribute problem this commit
exists to remove. Two header shapes, each byte-identical within its group: nine
documents that link to the repository and six that do not.
- Substitution is safe in both macro forms present, `https://...[README]` and
`link:https://...[README]`, because attributes are substituted before macros
are parsed.
Verified against the tree before the change, at open-govpress 0.0.13: plain
asciidoctor output byte-identical for all 15 documents in en and de, so every
substituted link resolves to the same string it did before; all 211 rendered
pages pixel-identical except each title page's source checksum; check-asciidoc.sh
passes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Published as open-govpress-v0.0.14 on this fork, the same binary-host arrangement 0.0.7, 0.0.12 and 0.0.13 use: the electron-builder tar.gz is ~112MiB, over GitHub's 100MB object limit, and a fork may not upload new LFS objects. All three predecessors stay published -- commits and document tags still pin them, so deleting any of them would make a historical checkout unrenderable. Three things this release fixes for this repo, all of which were wrong in the published PDFs: - A wide table no longer runs off the paper. Em002-5's ten-column target-group matrix was clipped at the sheet edge in every language; German lost nine words, including X and (X) data cells, not merely a truncated heading. Cell content can break now, and a new build-time info diagnostic names any table that only fits because of it. - [.landscape] turns the page a single block sits on, without changing the document type. Applied to the matrix in its own commit. - Relative link: targets reach the PDF verbatim instead of serialising as app://govpress/em002-2.pdf, the renderer's private origin. That retires 285 dead annotations per language with no source change -- which is why the 1,095 .pdf targets here were deliberately never rewritten around the bug. The version, URL and sha256 are pinned in two places that have to agree. Only the workflow's URL needed editing; devenv.nix interpolates govpressVersion into both the tag and the filename. Verified: the published asset was downloaded from the pinned URL and hashes to the pinned sha256, so the workflow's `sha256sum -c` will pass. Rendering is unchanged in shape -- all 15 documents render in en with the same <basename>.pdf naming. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The matrix sets fourteen documents against nine target groups, and ten columns do not fit A4 portrait: at 453.5pt of measure a narrow column gets 37.8pt, and "Geschäftsleitung" renders at 81.1pt. Until 0.0.14 the surplus was simply painted past the sheet edge -- nine words in German, including X and (X) cells, so a reader of the published PDF could not tell those columns were there. 0.0.14 stops the clipping on its own by letting cell content break, and says so: "only fits because its words are broken. [.landscape] gives it a turned page." This takes the turned page, because a matrix whose headings are broken mid-syllable is legible only in the sense that the glyphs are on the sheet. - The attribute goes on the table, not the document. :govpress-style: base-form-landscape would turn all eight pages of a prose factsheet sideways and would also declare the factsheet a Basisformular, which it is not. - Landscape costs height: 700.2pt of measure instead of 453.5, but a 595pt sheet instead of 842. The matrix now runs across three turned pages, four in English, and the document grows from 8 pages to 10, 11 in English. That is the price of not breaking the headings. - Landscape alone would not have been enough. An 81.1pt word still exceeds the 46.3pt content box a 1/12 column gets even on a turned sheet -- the two fixes are complementary, and it is 0.0.14's word breaking that makes the column fit at all. Verified: renders clean in all five languages with no info or warning; portrait before, 842x595 for the matrix, portrait after; zero glyphs beyond the page width in any language, against nine in German before. Pages 8->11 en and 8->10 de, fr, it and rm. asciidoctor parses all five; construct census delta zero in every language and the word census unchanged, an attribute line carrying no prose. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Published as open-govpress-v0.0.15 on this fork, the same binary-host
arrangement 0.0.7 and 0.0.12 through 0.0.14 use: the electron-builder tar.gz is
~112MiB, over GitHub's 100MB object limit, and a fork may not upload new LFS
objects. All four predecessors stay published -- commits and document tags still
pin them, so deleting any of them would make a historical checkout unrenderable.
0.0.14 introduced [.landscape] with the geometry right and the paint wrong. The
app's backdrop is `:root > body { background: #d0d0d0 }`, scoped that way so it
cannot tint the phantom html/body clones Paged.js inserts into every page after
the first; the print block reset it as plain `body`, and @media adds no
specificity, so 0-0-1 lost to 0-0-2 and the reset never applied. It stayed
invisible for as long as every sheet was portrait, because .pagedjs_page paints
white over the whole of one -- a turned sheet is 297mm wide while that element is
still the document-global 210mm, so 87mm of backdrop reached the paper and
`printBackground: true` wrote it into the PDF.
In this repo that was a grey band down the right third of all four turned pages
of Em002-5, in every language, in the PDFs published from the last push.
No document changed. Em002-5's [.landscape] attributes were already correct, and
the exposure is exactly one release wide: before [.landscape] there was no sheet
wider than the page element.
Verified: the published asset was downloaded from the pinned URL and hashes to
the pinned sha256, so the workflow's `sha256sum -c` will pass. Across the whole
rendered corpus -- every landscape page of all 75 PDFs sampled -- the worst
#d0d0d0 coverage is 0.03%, which is anti-aliasing on borders, against 25% of the
upstream showcase's own turned page under 0.0.14. Zero clipped glyphs, zero
app:// URIs, 1082 pages before and after with no document changing size, the
form fields intact at 25 /Btn in Em002-7 and 87 /Btn plus 15 /Tx across the
checklists, and check-asciidoc.sh exits 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This turns the guidelines into a single localised AsciiDoc source tree that renders itself. Today the repository holds English-only Markdown and ODT files and publishes nothing; after this it holds 15 documents that each carry all five languages in one file (de, fr, it, rm, en), renders them to 75 PDFs through open-govpress — the Swiss federal AsciiDoc rendering tool — and publishes them to GitHub Pages with a language chooser, a per-language index, and an archive of every past build.
Live preview of the result, built from this branch by the same workflow being proposed here:
https://736-c41-2c1-e464fc974.github.io/opensource-guidelines/
The four main pieces:
1. Markdown/ODT → AsciiDoc
.mdand.odtdocument converted to.adocand the originals removed.docs/en/is flattened todocs/, since a per-language directory has nothing left to name once each file carries five languages.[%interactive]AsciiDoc checklists — 87 static☐glyphs turned into real fillable PDF AcroForm widgets, not just squares that look clickable.link:targets point at.pdf, because they are followed inside the rendered PDFs next to their siblings;README.adockeeps.adoctargets, being read on GitHub.2. All five languages, in one file per document
:lang:on line 1, a conditional doctitle, a shared attribute header (pared back in section 8 to only what a renderer actually reads), and five whole-bodyifeval::blocks in the orderen, de, fr, it, rm.open-govpress render --langpins{lang}as a locked attribute, which drives the guards.TRANSLATING.mdis the entry point for reviewers — it documents the file shape, the rules, the Kennzeichnung table and how to build.publiccode.ymlgainsrm-CH.3. Diagrams that are text
The 21 diagram PNGs behind 105
image::macros were language-blind — only their alt text was ever translated, so six of them shipped German text inside the English, French, Italian and Romansh PDFs. They are now:[mermaid]blocks with translated labels — they diff, spell-check and are reviewable in a pull request. Authored bare, so open-govpress supplies the project palette.tools/make-four-cs-svg.sh), so the labels are real text. The PNGs it replaces had no editable source in the repo at all.docs/partials/and is included where shown.docs/assets/drops from 1.45 MB of bitmaps to 233 kB.4. Build, CI and publishing
devenv.nix/.envrcfor local work:render-docs(all languages, orrender-docs de rm) produces byte-identical output to CI, including the index pages — an index reimplemented in the workflow is an index that stops matching what you see before you push..github/workflows/render-docs.yml: renders the five languages as a matrix, merges them into one site, uploads PDFs as an artifact on every push and PR, and deploys to Pages onmain.mainland undercommits/<sha>/(newest 10) and own the site root,v*release tags land permanently undertags/<tag>/. Backed by asite-historybranch, since a Pages deployment replaces the whole site and cannot read back what is published..github/workflows/lint.ymlruns 13 pre-commit hooks — whitespace/EOF/symlink/executable checks,editorconfig-checker,nixfmt,yamllint,ripsecrets,shellcheck -x -o all, and a repository-localcheck-asciidocthat renders every document once per language (a conditional left unterminated in the Romansh block is invisible while parsing as English) and checks every cited reference key against the entries actually emitted — see section 6.typosandvaleare deliberately excluded: they are English-language tools and four of five languages here are not English.tools/*.shrather than inside Nix strings, where shellcheck and editorconfig-checker cannot see it.5. Tables that were never tables
The document set drew 585 tables, and only 60 of them hold tabular data. The
rest are FAQ question-and-answer pairs, glossaries, reference lists, project
fact-sheets, single-cell callouts, empty write-in boxes, and tables whose only
job was to indent something. They are tables because the source came out of
Word, not because the content has columns.
That is not only untidy.
tr { break-inside: avoid }makes every table rowunbreakable, and open-govpress deliberately keeps it that way: a row that breaks
mid-cell is the one fragment shape its repair logic refuses to handle, and
Paged.js then abandons the rest of the document. So a page-tall FAQ answer was
an atomic block the layout could not place, and the remedy is the construct, not
a stylesheet.
Each family moved to the construct that already described it:
but a localised literal
Q/F/DandA/R— became[qanda.unnumbered].The convention was applied inconsistently, 190 of 245 questions bolded and 55
not; the construct supplies the emphasis, so that inconsistency went with it,
along with the legend table that documented the convention.
description lists. Em002-6's glossary was already one, so this finishes a
conversion someone had started.
sidebar, and a source attribution in Em002 became a
NOTE:, which alsodeleted five hand-maintained "Source note:" prefixes.
in Em002-2.3 — was empty in every single row.
The 75 write-in boxes were the worst of it. A
[cols="1"]table holding oneempty cell is an implicit header row, so asciidoctor emitted a
<thead>with asingle empty
<th>and no body, and the theme paintsthblack. Every box inall five languages printed as a solid black rectangle — three of them on page
2 of the English Em002-2.1. They are now
field::[], a real/Txwidget you cantype into and save. Together with the checkboxes, the checklists are fillable
rather than half-fillable.
The same implicit-header defect printed "Measure 1: Implement the Practical
Guidelines…" as a heading above Measures 3, 6 and 8 in Em002. Both are gone.
60 tables stay, and each earns it: Em002-5's ten-column target-group matrix,
Em002-4's morphological box, Em002-7's sovereignty perspectives, levels, grid,
use-case classification and WTO-threshold case distinction, Em002-3's two licence
comparisons, Em002-2's document and platform tables, and Em002-1's
constellation/impact pairs. Em002-3 and Em002-7 additionally cite "Table 1" by
number in running prose in all five languages, so converting those would have
broken the reference.
No prose was changed anywhere. Five of the eight commits leave the per-language
word census byte-identical; the other three differ only by markup that was
deliberately removed,
{empty}guards that render as nothing, or thefieldandrowstokens of the new macro.This needs open-govpress 0.0.12, released for it:
[qanda.unnumbered],page-break protection for terms, questions and bordered blocks, the
field::[]/Txwidget, and tables that now repeat their header and hold their columnwidths across a page break. One item was asked for and did not ship — the Romansh
admonition terminology in
attributes-rm.adocstill needs TERMDAT or a nativespeaker before a published document relies on it.
6. Lists kept in three places, and a gate that never checked
Three lists — abbreviations, glossary, references — were maintained by hand in
Em002, Em002-6 and Em002-7, and all three had drifted apart. They now live once, in
docs/partials/{abbreviations,glossary,references}.<lang>.adoc, and each documentincludes the subset it needs by tag.
The drift is the reason it was worth doing, and it was not cosmetic:
translation.
differed in title, in link form, or in both. Em002 writes
link:url[text];Annex A writes
+url+, which renders as inert text — and had lost[DigiV]'sURL entirely and the head noun from
[BBL-CL], which began mid-phrase, "forArt. 9 EMOTA Blanket Exception", in all five languages.
The parse gate had never checked cross-references — and not for the reason its
own comment gave.
tools/check-asciidoc.shpassed--out-file /dev/null, andasciidoctor special-cases that path and returns before conversion, so no
conversion-stage diagnostic could reach it at any
--failure-level. The commentblamed the log level, which is the kind of wrong that stops the next person
retrying. It now converts for real, and a companion check compares cited keys
against defined ones per document per language, flattening the corpus with
asciidoctor-reducerso it applies the same tag selection that could silently dropan entry.
That surfaced references that were broken in the published PDFs:
[KKB-MB]while citing[KBB-MB]six times without definingit — 30 bracketed labels pointing at nothing across the set. One information
sheet, one URL, one letter apart; KBB is Kompetenzzentrum Beschaffungswesen Bund
and the sibling keys are already
[KBB-KV]and[KBB-Perimap].BIT-KOM2023,BITCOM2024,PE1999,DigiO,OSI2019andOSIwere eachcited with no entry anywhere, in all five languages.
[OSI]deliberately became[Pe1999], not[OSI2024]: the sentence introduces the Open Source Definition'scriteria, and
[OSI2024]is an alphabetical licence index.Em002-7's self-assessment grid is now fillable. It asks the reader to place a
technology on a five-by-five grid of sovereignty perspectives against maturity
levels, and gave them twenty-five empty cells — nothing to tick on paper, nothing
to click on screen. Every cell is now an interactive checkbox, 25 real
/Btnwidgets per language, filled and read back through pdf.js. Its two reference tables
became description lists.
Every citation in the document set now resolves, and the gate proves it: exit 0
over the whole corpus with an empty allow-list. That has not been true before.
Needs open-govpress 0.0.15. 0.0.13 implements
include::[tags=…], which thepartials depend on — 0.0.12 accepted the attribute and silently ignored it, the
dangerous direction, since stock asciidoctor honours it and a gate would go green
while the PDF shipped every entry in the file. 0.0.14 adds the three fixes in
section 7, and 0.0.15 corrects a paint defect it introduced with them: on a turned
page the app's own viewport backdrop reached the paper, because the print
stylesheet's reset was a less specific selector than the rule it meant to override
and
@mediaadds no specificity. A quarter of every landscape page was grey. Bothdefects were specified from this repo, with measurements, and both were fixed
upstream rather than worked around here.
7. Links that went nowhere, and a table that ran off the paper
Two defects that were in every published PDF and that no check could see, both
fixed by the renderer rather than by editing the documents.
Every inter-document link was dead. A relative
link:em002-2.pdf[…]serialisedinto the PDF as
app://govpress/em002-2.pdf— the renderer's own private origin,a scheme no PDF reader can open. 285 annotations per language, 1,425 across the
set. The fix makes a relative target pass through verbatim, so the 1,095 targets
authored here were already correct input; they were deliberately never rewritten
into absolute URLs, which would have had to be undone. Not one source line
changed and all of them now resolve —
/URI (em002-1.pdf).Em002-5's target-group matrix ran off the sheet. Ten columns against a 453.5 pt
measure: a narrow column gets 37.8 pt and
Geschäftsleitungrenders at 81.1 pt, sothe surplus was simply painted past the paper edge — nine words in German,
including
Xand(X)data cells, not merely a clipped heading. A reader couldnot tell those columns were there.
The renderer now lets cell content break, and says when it had to. Em002-5 takes
the
[.landscape]role instead, which turns the page that one table sits onwithout changing the document type: a matrix whose headings are broken
mid-syllable is legible only in the sense that the glyphs are on the sheet. It
costs two to three pages — landscape buys width and spends height — and the
attribute sits on the table, not the document, because
:govpress-style: base-form-landscapewould turn all eight pages of a prosefactsheet sideways and declare it a Basisformular, which it is not.
Both fixes were specified from this repo, with measurements, in
ai-prompt-wide-tables.mdupstream.8. Headers that repeated themselves
Two smaller cleanups to the thing every document carries at the top.
Seven of the nineteen header attributes never reached a reader.
:govpress-style: reportmakes open-govpress apply two attribute files of itsown before the document's header —
styles/common.adocandstyles/report.adoc, bundled in the app — and between them they already setgovpress-front-block,title-page,pdf-theme,title-logo,toc,sectnums,toclevels,sectnumlevelsandexperimental. They are appliedsoftly, so a document's own header still outranks them; ours simply restated
values already in force, byte-identically, fifteen times over.
:toc:,:toclevels:,:sectnums:and:sectnumlevels:deliberately stay,with a comment saying why they look redundant and are not: GitHub and GitLab
render every
.adocin the repository with plain Asciidoctor, which applies noneof open-govpress's style files, so without them the web view of the source loses
its contents list and its section numbering. The rule applied was drop what no
renderer reads, not drop what the PDF does not need.
:classification:staystoo, empty and commented, so a document answers the question rather than leaving
it unasked.
The repository URL was written out 70 times.
:url-repo:was defined in all15 documents and referenced by none, while the address itself appeared literally
in four different forms, in five languages, with nothing holding them together.
All 55 body occurrences now derive from the attribute, and its value is the
repository root rather than
/tree/main— the only value all four forms can bebuilt from, and the form open-govpress's own showcase uses. The repository being
a fork makes the owner segment the part most likely to move, which is now written
once per document instead of seventy times across them.
The four checklists,
index.adocand the README template lose the attributerather than gaining a reference: none of them links to the repository at all, so
defining it there would have recreated in six files exactly the unused-attribute
problem the change exists to remove.
Pre-existing content bugs fixed along the way
None of these were introduced here; they were found by converting and then reading the text in five languages.
em002-1.mdhad a large duplicated content block — roughly the first third of the document repeated, in two different truncated forms. De-duplicated..odtchecklist links across several documents (typos, trailing spaces, a missing word in the filename).[N]leaking into the table of contents. Relocated to the first natural mention of the same term in the body.Fehler! Verweisquelle konnte nicht gefunden werden., an emptyin Section )cross-reference, "aApplication owner", "the roject is Free", "must be uthorised", "Outcomes are feedback continuously", "thus subject to the legal requirements of a?", "(at noted in Poledna/…", a doubled "in principle", the misspeltopdencode.de, aneoperation.chlink contradictingeoperations.chin the same document, a fedlex link whose display text pointed at/dewhile the target was/en, and a stray literal backslash before an en-dash.publiccode.yml's screenshot pointed at a deleted bitmap through a/blob/URL, which serves an HTML page rather than an image.Left for FCh to decide, as content rather than conversion: Em002-3 annex D.5 attributes the ASTRA.EMSG road-asset application to MeteoSwiss, though both its repository and its description point to the Federal Roads Office.
Verification
.adocparses at zero errors/warnings, in all five languages, on every commit (check-asciidoc).render-docsrenders 15 documents × 5 languages = 75 PDFs end to end via the realopen-govpressCLI — no diagram falling back to a source listing, no partial rendered as a document.git show, confirmed no content was lost — including theem002-1de-duplication./AcroForm/Widgetannotations with/TUtooltips matching each label; counts match the original glyph counts per file (25/14/9/39).v2.0release tag.colsattributes, headings per level, checkbox items, footnotes, links and list terms moves by the same delta in en/de/fr/it/rm for every commit. The only cross-language differences left in the corpus are the localised abbreviation lists, which already differed before.em002-2,em002-3,em002-4,em002-6andem002-7are byte-identical, and the only additions in the other two render as nothing./Txform fields were filled in and read back with pdf.js in en and de — values return unchanged, multiline set exactly on therows > 1boxes — and the 435/Btncheckboxes are unchanged at 25/14/9/39 per language.tools/check-asciidoc.shnow converts rather than stopping at parse, and exits 0 in all five languages with an emptyknown_unresolvedallow-list — a stale entry in that list is itself fatal, so it cannot outlive the problem it documents.[KKB-MB]→[KBB-MB]./Btnin Em002-7 and 87/Btn+ 15/Txacross the four checklists, per language.app://URIs remain, against 285 per language. The published 0.0.15 asset was downloaded from the pinned URL and hashes to the pinned sha256, so the workflow'ssha256sum -cwill pass. Every landscape page of all 75 PDFs was sampled for the backdrop defect: worst coverage 0.03%, which is anti-aliasing on borders, against 25% of a turned page before the fix.Repository settings this needs (one-time, by an admin)
Nothing here can be set from a pull request — these are repository settings, and the publish job stays red until they are in place. Merging without them is safe: the render job and the PDF artifact work regardless, only the Pages deploy fails.
1. Turn Pages on, sourced from Actions.
Settings → Pages → Build and deployment → Source: GitHub Actions (not "Deploy from a branch"). This is what creates the
github-pagesenvironment the deploy job targets.2. Let release tags deploy, not just
main.Settings → Environments →
github-pages→ Deployment branches and tags. GitHub creates this environment restricted to the default branch, so thev*tag runs are rejected with a protection-rule error whilemainsucceeds — the archive undertags/<tag>/reaches thesite-historybranch but never the site. Add a rule of type Tag matchingv*, so the list reads:mainv*3. Leave the default
GITHUB_TOKENpermissions alone — no change needed.Settings → Actions → General → Workflow permissions can stay on the restricted "Read repository contents and packages permissions" default. Both workflows declare
permissions: contents: readat the top and raise only what a specific job needs (contents: writeoncollectto push thesite-historybranch,pages: write+id-token: writeondeploy). An explicit block in the workflow overrides the repo default, so there is nothing to loosen globally.4. Check that no ruleset covers
site-history.The publish job force-pushes a single parentless commit to a
site-historybranch (created on first run — no need to make it by hand). An organisation ruleset matching**or requiring pull requests on all branches will block that push. The branch is pure derived output, re-renderable from source at any time, so exempting it costs nothing.Nothing needs to be done for pull requests: PR runs never deploy and never push — they only upload the
siteartifact.Notes for reviewers
open-govpressbinary reference will need repointing. open-govpress itself lives at https://gitlab.com/swiss-armed-forces/cyber-command/cea/open-govpress. Since this repository is on GitHub and needs the binary available to Actions, the devenv and CI builds currently fetch a 0.0.7 Linux tarball re-hosted as a GitHub Release on this fork (open-govpress-v0.0.7), verified against a pinned SHA-256 — committing it is not an option (~112 MiB, over GitHub's 100 MB limit) and Git LFS is blocked on forks by GitHub policy. On merge,govpressUrl/govpressSha256indevenv.nixandGOVPRESS_URL/GOVPRESS_SHA256in.github/workflows/render-docs.ymlshould point at whatever the canonical publication channel turns out to be — ideally a GitLab release artifact from the upstream project, so there is no re-hosted copy to keep in sync.v2.0.TRANSLATING.md, then one document (docs/em002-5.adocis the smallest and was the proof of the mechanism), thentools/.