feat(legal): add the trader identification block to the legal pages #138

Sammanfogat
supernaut sammanfogade 1 incheckning från fix/legal-controller-identification in i main 2026-08-01 19:10:03 +00:00
Ägare

Closes #135 — but deliberately publishes nothing yet. This needs a decision from you before it can
ship.

Structure

One source of truth (src/lib/legal-entity.ts) rendered by one component
(src/components/legal-entity.astro) at the end of the Terms, the Privacy Policy and /contact, in
both languages. Labels are new legal.entity.* keys in both dictionaries. The country is an i18n
string rather than a data field, so it reads correctly per locale instead of showing one language's
spelling on the other's page. Invoicing will need the same values, which is the other reason they live
in one module.

Why nothing renders

The registered details are not recorded anywhere I can source them from — no organisationsnummer,
no VAT number and no postal address exist in the project's records.

So nothing was guessed. Each unknown field is null with a TODO(legal) marker, and the block is
withheld all-or-nothing — half an identification block is a worse answer to e-handelslagen § 8 and
GDPR Art. 13(1)(a) than the single line those pages carry today, and it reads as an oversight rather
than a decision. The gate is compiler-checked rather than asserted with a cast. Confirmed against a real
production build: /terms, /en/privacy and /contact render no block and no placeholder text. In
pnpm dev a dev-only notice names the missing fields so the gap is visible to whoever works on the
pages.

Why this stops here rather than shipping

Filling the four fields in is not purely mechanical. Publishing a trader's registration details has
consequences that have to be settled before they go on a page that cannot be un-published, and that
decision is pending. Until it lands the block stays withheld and the three pages render exactly as they
do today.

To finish

Fill in legalName, organisationNumber, vatNumber and address in src/lib/legal-entity.ts.
Nothing else needs touching — the block then appears on all six pages.

/trial-terms was left alone; the issue named the Terms, the Privacy Policy and /contact only.

Verified

pnpm check 0 errors · eslint, stylelint, pnpm format:check, pnpm mdlint all clean ·
pnpm test 177 passed (8 new for the publish gate) · pnpm build succeeds.

Closes #135 — **but deliberately publishes nothing yet. This needs a decision from you before it can ship.** ## Structure One source of truth (`src/lib/legal-entity.ts`) rendered by one component (`src/components/legal-entity.astro`) at the end of the Terms, the Privacy Policy and `/contact`, in both languages. Labels are new `legal.entity.*` keys in both dictionaries. The country is an i18n string rather than a data field, so it reads correctly per locale instead of showing one language's spelling on the other's page. Invoicing will need the same values, which is the other reason they live in one module. ## Why nothing renders **The registered details are not recorded anywhere I can source them from** — no organisationsnummer, no VAT number and no postal address exist in the project's records. So nothing was guessed. Each unknown field is `null` with a `TODO(legal)` marker, and the block is withheld **all-or-nothing** — half an identification block is a worse answer to e-handelslagen § 8 and GDPR Art. 13(1)(a) than the single line those pages carry today, and it reads as an oversight rather than a decision. The gate is compiler-checked rather than asserted with a cast. Confirmed against a real production build: `/terms`, `/en/privacy` and `/contact` render no block and no placeholder text. In `pnpm dev` a dev-only notice names the missing fields so the gap is visible to whoever works on the pages. ## Why this stops here rather than shipping Filling the four fields in is not purely mechanical. Publishing a trader's registration details has consequences that have to be settled before they go on a page that cannot be un-published, and that decision is pending. Until it lands the block stays withheld and the three pages render exactly as they do today. ## To finish Fill in `legalName`, `organisationNumber`, `vatNumber` and `address` in `src/lib/legal-entity.ts`. Nothing else needs touching — the block then appears on all six pages. `/trial-terms` was left alone; the issue named the Terms, the Privacy Policy and `/contact` only. ## Verified `pnpm check` 0 errors · eslint, stylelint, `pnpm format:check`, `pnpm mdlint` all clean · `pnpm test` 177 passed (8 new for the publish gate) · `pnpm build` succeeds.
supernaut lade till 2 incheckningar 2026-08-01 14:18:46 +00:00
The Terms and the Privacy Policy identified the controller only as "Gitborg,
Sverige" — no registered name, no organisationsnummer, no address. E-handelslagen
(SFS 2002:562) § 8 expects a trader's name, geographic address, email and
registration/VAT details; GDPR Art. 13(1)(a) expects the controller's identity.

The values now live in one module, src/lib/legal-entity.ts, rendered by
src/components/legal-entity.astro at the end of the Terms, the Privacy Policy and
the contact page in both languages. Invoicing needs the same set, which is the
other reason for a single source rather than six hand-written copies.

INCOMPLETE ON PURPOSE. The registered details are not recorded anywhere in this
repository, so every field that needs them is `null` and carries a TODO(legal)
marker: the registered name, the organisationsnummer, the momsregistreringsnummer
and the postal address. Guessing them would misidentify the trader on the two
pages a reader consults precisely to find out who they are dealing with, which is
worse than the gap. While anything is missing the block is withheld in full —
all-or-nothing, because half an identification block reads as an oversight — so
the pages render exactly as they did before and a dev-only notice names the
missing fields. Filling in that one module is the whole remaining change.

Refs #135
ci(content): run the content-style check automatically on copy changes
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m12s
7380180f8d
The content-style rules were enforced only by someone remembering to run them, and
the transactional email copy shipped three separate faults that a human caught
late. Equality or snapshot assertions on those strings would have caught none of
them — they mostly assert a string equals itself, and they turn every intentional
wording change into a test update. So the rules that can be expressed as a pattern
now run on their own instead.

`pnpm lang-check` (scripts/check-content-style.mjs) checks src/content/** and
src/i18n/*.ts. It runs from the lefthook pre-commit path on staged files, next to
the existing prettier/eslint/markdownlint jobs, and over every surface in CI so a
rule added later also reaches copy that already exists. Each finding prints the
rule id, the hint, the docs/content-style.md section behind it and the offending
line, so the output says which rule was broken rather than only what matched.

Two changes to the ported rules, both to keep the gate honest rather than loud:
"arkiv" is exempt on a line about backups, where it is the correct Swedish word for
an archive rather than a stand-in for kodförråd, and whole-line comments in the
TypeScript dictionaries are not checked, because failing a commit over the wording
of a code comment would only teach people to write awkward comments. A legitimate
line is suppressed with a `lang-check-ignore-next-line <rule-id>` directive instead
of weakening a rule for everyone.

The ceiling is documented in the guide and in the script: it is a regular-expression
heuristic. It stops the known faults recurring silently and it cannot see that a
call to action contradicts its link, that an instruction excludes half its audience,
or that the tone is wrong. Email and legal copy still have to be read in both
languages. Tests cover the gate's own logic, not the copy.

Version bumped to 1.4.0 for the branch.

Refs #128
supernaut tvångsskickade fix/legal-controller-identification från 7380180f8d
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m12s
till c2ed304857
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m6s
2026-08-01 18:51:22 +00:00
Jämför
supernaut sammanfogade incheckning d5dfa1513e till main 2026-08-01 19:10:03 +00:00
supernaut tog bort grenen fix/legal-controller-identification 2026-08-01 19:10:03 +00:00
Logga in för att delta i denna konversation.
Inga granskare
Ingen milstolpe
Inget projekt
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Förfallodatumet är ogiltigt eller utanför gränserna. Använd formatet "åååå-mm-dd".

Inget förfallodatum satt.

Beroenden

Inga beroenden satta

Referens
bitborg/bitborg-web!138
Ingen beskrivning angiven.