Skip to content

feat(gates): gate-96 manifest-copy-style — the voice rule, enforced - #581

Merged
rubenvdlinde merged 3 commits into
mainfrom
feat/gate-96-manifest-copy-style
Aug 26, 2026
Merged

feat(gates): gate-96 manifest-copy-style — the voice rule, enforced#581
rubenvdlinde merged 3 commits into
mainfrom
feat/gate-96-manifest-copy-style

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

The Conduction voice already bans em-dashes. voice.md section 8 is explicit, and the writing skill's REVIEW mode uses a walkthrough steps[0].body em-dash as its worked example. The rule was never wrong.

It shipped anyway — 126 violations across 12 apps:

app app app
shillinq 47 opencatalogi 12 openconnector 6
hrmq 15 decidesk 8 scholiq 6
pipelinq 14 larpingapp 7 docudesk 4
hermiq 3 procest 2 softwarecatalog 2

What surfaced it was a user opening dossiq and reading step 1 on screen: "a quick spin through case handling we'll register a case".

Why a gate, when the rule already exists

A skill is opt-in. It applies when an author chooses to load it, and manifest copy gets written by hand or by an agent that never invoked it. Nothing downstream reads the prose at all: check:manifest validates against a JSON Schema, and JSON Schema has no opinion about writing.

So the rule lived in a document, the copy shipped past it, and the only detector was a human noticing on screen.

Not walkthrough-only — this is why the count is 126, not 25

My first sweep read only walkthrough steps and found 25. Reading every user-visible field found 126. The rest are in setup-wizard steps, menu labels and widget empty states:

menu[8].children[1].label          "Retention periods — Dashboard"
widgets[10].content.emptyText      "No open debtor invoices — everything is paid."

A walkthrough-scoped checker would have called ten of those twelve apps clean.

Reads the fragments

src/manifest.d/*.json is merged into the manifest at runtime via require.context. A checker that opens only src/manifest.json is blind to whatever they add. Eight fleet apps use fragments; shillinq has 87.

Verified on real trees — all four paths

case result
dossiq (2 known) rc=1, 2 findings, both the reported ones
buildiq (clean) rc=0
directory with no manifest rc=4 → gate reports na, not a false pass
crashed checker SKIP(wiring) "UNVERIFIED", never PASS

That last row is the property worth having. _helper_finished requires the terminal checked N manifest string(s) line, so a checker that dies mid-run cannot report success over findings it never reached.

Deliberate exclusions

  • En-dashes between digits are allowed per voice.md, so 2020–2024 passes while RGS – Referentie GrootboekSchema is caught.
  • Backticked spans and URLs are stripped before the double-dash test — --flag in an example is not prose.
  • Non-prose fields (route names, component ids, icon names, schema slugs) are never inspected; the field list is explicit rather than "every string in the tree".

Full-tree rather than diff-scoped, for the reason gates 84, 93, 94 and 95 give: the violations are already in the tree, and a diff-scoped version reports clean on every PR that does not happen to touch the manifest.

Note for rollout: this gate will fail those 12 apps until their copy is fixed. Fixes are going out per app — dossiq is done (ConductionNL/dossiq#1373, 0 findings over 390 strings).

The Conduction voice already bans em-dashes. voice.md section 8 is explicit,
and the `writing` skill's REVIEW mode uses a walkthrough `steps[0].body`
em-dash as its worked example. The rule was never wrong.

It shipped anyway. A sweep on 2026-08-26 found 126 violations across 12 apps:

  shillinq 47 · hrmq 15 · pipelinq 14 · opencatalogi 12 · decidesk 8
  larpingapp 7 · openconnector 6 · scholiq 6 · docudesk 4 · hermiq 3
  procest 2 · softwarecatalog 2

The one that surfaced it was a user opening dossiq and reading step 1 on
screen: "a quick spin through case handling - we'll register a case".

WHY A GATE WHEN THE RULE EXISTS. A skill is opt-in: it applies when an author
chooses to load it, and manifest copy gets written by hand or by an agent that
never invoked it. Nothing downstream reads the prose at all - `check:manifest`
validates against a JSON Schema, and JSON Schema has no opinion about writing.
So the rule lived in a document, the copy shipped past it, and the only
detector was a human noticing. That is the shape a mechanical gate is for.

NOT WALKTHROUGH-ONLY. My first sweep read only walkthrough steps and found 25.
Reading every user-visible field found 126. The rest sit in setup-wizard
steps, menu labels and widget empty states - "No open debtor invoices -
everything is paid." A walkthrough-scoped checker would have called ten of
those twelve apps clean.

READS THE FRAGMENTS. `src/manifest.d/*.json` is merged into the manifest at
runtime via require.context, so a checker that opens only `src/manifest.json`
is blind to whatever they add. Eight fleet apps use them; shillinq has 87.

Verified on real trees, all three exit paths:

  dossiq (2 known)     rc=1, 2 findings, both the reported ones
  buildiq (clean)      rc=0
  a dir with no manifest rc=4, so the gate reports na, not a false pass
  crashed checker      SKIP(wiring) "UNVERIFIED", never PASS

That last one is the property worth having. `_helper_finished` requires the
terminal "checked N manifest string(s)" line, so a checker that dies mid-run
cannot report success over the findings it never reached.

En-dashes between digits are allowed, per voice.md, so "2020-2024" passes
while "RGS - Referentie GrootboekSchema" is caught. Backticked spans and URLs
are excluded before the double-dash test, since `--flag` in an example is not
prose.

Full-tree rather than diff-scoped, for the reason gates 84, 93, 94 and 95
give: the violations are already in the tree, and a diff-scoped version reports
clean on every PR that does not happen to touch the manifest.
The first version of this checker walked every key, which meant it read
`_meta.description` — the per-fragment provenance block carrying
spdx-license, spdx-copyright, change, adr and a note describing the fragment
for developers. 38 shillinq fragments have one.

That was 22 of shillinq's 47 findings, and every one of them was wrong. Worse
than noise: acting on them would have rewritten build provenance as if it were
user-facing prose, so the gate would go green while the tree got worse. A gate
that manufactures work is not cheaper than no gate.

Now skips any key beginning with `_`, which is the convention for internal
blocks rather than a special case for this one name.

Corrected fleet totals, measured after the change:

  before  126 across 12 apps
  after    97 across 12 apps
  shillinq 47 -> 24, scholiq 6 -> 1

The remaining findings are real. `CnPageHeader` renders a page's
`config.description` under its title, so users currently read things like
"REQ-REC-006: pre-close summary - matched count, unmatched GL/bank counts".
Those are developer notes in a user-visible field, and the em-dash is the
least of what is wrong with them.

All three exit paths re-verified after the change: dossiq rc=1, buildiq rc=0,
a directory with no manifest rc=4.
rubenvdlinde added a commit to ConductionNL/opencatalogi that referenced this pull request Aug 26, 2026
Found by gate-96 (manifest-copy-style, ConductionNL/.github#581). voice.md
section 8: em-dashes are AI tells.

All twelve were already genuine user copy, split between the six-step setup
wizard and the getting-started tour. No internals leaked here, so the meaning
is unchanged throughout and this is punctuation only.

Two rewrites did more than swap a mark, because the dash was hiding a
second sentence that deserved to be one:

  "Same as the 'Sync directories now' button in the admin settings - the
   periodic cron does this automatically over time, this step runs it once
   immediately."
    ->
  "Same as the 'Sync directories now' button in the admin settings. A
   scheduled job does this over time; this step runs it once, now."

  "cron" is also the wrong word for an admin reading a setup wizard.

  "A publication is a single thing you make open - a document, dataset or
   record."
    -> a colon, since what follows is a list in apposition, not a new clause.

The schema-RBAC sentences kept their meaning intact. "OpenCatalogi has no
separate 'publish' switch. Visibility is governed by schema-RBAC and the
catalog's scope." is the sentence that stops someone hunting for a toggle that
does not exist, and it is worth more than the dash it used to carry.

Verified: gate-96 0 findings over 115 strings, check:manifest PASS (Ajv 0
errors), JSON re-parses.
rubenvdlinde added a commit to ConductionNL/shillinq that referenced this pull request Aug 26, 2026
…tes (#1256)

Found by gate-96 (manifest-copy-style, ConductionNL/.github#581). Two separate
problems shared one detector.

THE PUNCTUATION. voice.md section 8 bans em-dashes. Seven strings carried one,
including the walkthrough's opening line and both dashboard empty states
("No open debtor invoices - everything is paid.").

THE REAL PROBLEM. The other seventeen were internal developer notes sitting in
a field users read. `CnPageHeader` renders a page's `config.description`
underneath its title, so people opening these pages were shown:

  "Approve the submitted requisition. Server-side gated by
   BudgetBlocker::canCommit - budget availability or an override-mandate
   (REQ-REQ-003)."

  "REQ-REC-006: pre-close summary - matched count, unmatched GL/bank counts,
   total variance, and sign-off comment field."

  "Per-category tax summaries feeding the annual filing. Converted from the
   never-rendered type=report (no such page type is registered) to a plain
   index - the KPI dashboard variant needs a dedicated renderer first."

That last one describes a refactor decision to a bookkeeper looking for their
tax totals. Stripping the dash would have made all three PASS the gate while
leaving them wrong, so they are rewritten as copy instead:

  "Approve the submitted request. Approval requires available budget, or a
   mandate that overrides it."

  "A summary before closing: how many items matched, how many are still
   unmatched on either side, and the total variance. Sign-off is required
   before this reconciliation can be verified."

  "Tax totals per category, ready for the annual filing."

Requirement ids (REQ-*), class and method names, and internal chain/slice
references are gone from every user-facing string. They remain where they
belong, in `_meta` and in the specs.

NOT TOUCHED: the `_meta.description` blocks. Those are per-fragment provenance
(spdx, change, adr) and are never rendered. The gate's first version flagged 22
of them here, which is why it now skips underscore-prefixed keys.

Verified: gate-96 reports 0 findings over 6,554 strings, `check:manifest`
passes, and all 88 JSON files re-parse.
test_gate_empty_scope_never_passes.sh failed this gate, correctly:

  FAIL - gate(s) 96 reported PASS over a scope that excludes every planted
  defect - this is the .github#374 defect. Each of them printed the same word
  as a gate that read the whole tree and found it clean.

That is exactly right. My first version returned exit 4 only when the repo
ships no src/manifest.json at all. A manifest that exists but declares no
user-visible string produced `checked 0` and exit 0, which the shell block
turned into PASS - a gate announcing "clean" having inspected nothing.

Now both shapes route to `_skip_empty_scope`, which says what was enumerated
and states plainly that nothing was inspected so this is NOT a pass. The
condition reads the checker's own `checked N` line rather than trusting the
exit code, because the count is the measurement and the exit code is only a
summary of it.

The test message ends "do NOT add it to _ARM6_ALLOWED unless you can state
what it computed." I did not. An allowlist would have made this green while
leaving the gate unable to tell "no violations" from "no input", which is the
whole failure this suite exists to prevent.

Verified after the change: a manifest with pages:[] and menu:[] gives
`checked 0` and takes the na branch; openbuild gives checked=49 findings=0 and
still PASSES; hrmq (654) and pipelinq (600) still FAIL with their real counts.
Empty scope and clean tree no longer print the same word.
rubenvdlinde added a commit to ConductionNL/pipelinq that referenced this pull request Aug 26, 2026
Found by gate-96 (manifest-copy-style, ConductionNL/.github#581). voice.md
section 8: em-dashes are AI tells, replaced with a period, a comma, or a colon.

Unlike shillinq and humaniq, every string here was already real user copy.
Nothing leaked internals, so this is punctuation only and the meaning is
unchanged in all thirteen. They span the setup wizard, the whole
getting-started tour, four empty states and one nav description.

Each rewrite picked the mark the sentence actually wanted rather than swapping
in a comma everywhere:

  period  where the clause after the dash was a complete thought
          "Add the product or service you sell. We'll put it on the
           quotation later."

  colon   where it introduces an explanation
          "a signable quotation IS the contract: one record, no duplication."

  commas  where the dashes were a parenthetical pair
          "a small linked demo dataset of clients, leads, requests and
           contactmomenten, so lists ..."

Verified: gate-96 0 findings over 600 strings, check:manifest PASS (51 pages,
25 menu items), both JSON files re-parse.
@rubenvdlinde
rubenvdlinde merged commit 2a611cb into main Aug 26, 2026
36 of 37 checks passed
rubenvdlinde pushed a commit that referenced this pull request Aug 26, 2026
…t untested

TWO THINGS HAPPENED ON MAIN while this branch was open.

1. GATE NUMBER COLLISION. #581 landed `manifest-copy-style` on 96, which this
   branch had already claimed. Renumbered to 97 — in the runner, in the
   fixture's expect.conf and in the empty-scope suite's ARM 6 allowlist, all
   three, because a gate id is a citation key: it appears in COVERAGE lines, in
   `_ARM6_ALLOWED`, in expect.conf files and in PR bodies, and each is a pointer
   that only works while the number resolves to one thing. That is the same
   argument gate-95 (adr-number-collision) makes about ADR numbers, one merge
   earlier.

2. THE ACCEPTANCE RATCHET WENT RED ON MAIN, and not because of anything here.
   #581 added its gate to the runner with no fixture and no UNCOVERED.md row, so
   `test_gate_acceptance_matrix.sh` fails on a bare clone of main — verified on
   one before touching anything. In the suite's own words: "a gate can be added
   to the runner and never tested; this is that moment." The ratchet worked;
   nothing had closed the loop it opened.

   So this adds the missing bundle. Its two arms differ in two strings in two
   different FILES — an em-dash in `src/manifest.json`, a double-dash in a
   `src/manifest.d/` fragment — because fragments are merged at runtime and a
   checker reading only the base manifest would be blind to eight fleet apps'
   copy (shillinq alone has 87 fragments). A single-file fixture would have
   passed such a checker and taught us nothing.

   The clean arm is deliberately NOT dash-free: it keeps "Reporting covers
   2020–2024", the en-dash-between-digits that voice.md permits. Without it, a
   checker that banned every dash outright would pass the clean arm and the rule's
   only exception would be untested.

Acceptance matrix 190/190, 70 of 77 gates fixtured, ratchet intact.
Empty-scope suite ALL PASS.
rubenvdlinde added a commit to ConductionNL/humaniq that referenced this pull request Aug 26, 2026
…178)

* fix(manifest): rewrite 15 user-visible strings that leaked internals

Found by gate-96 (manifest-copy-style, ConductionNL/.github#581). The dashes
are what the gate detects; they are not the interesting part.

`CnPageHeader` renders a page's description under its title, so these were on
screen for whoever opened the page:

  "Application pipeline (recruiting-ats-basic): new -> screening -> interview
   -> offer -> hired/rejected. The status filter is the MVP pipeline surface
   -- every stage is a filter value; a kanban board is an nc-vue widget
   follow-up (design D7)."

  "Submitted leave requests from your team awaiting approval or rejection
   (mss-team-scope) -- based on the managerUserId link maintained by
   HR/back office."

  "Run-to-run payroll mutation reports ... generated through the
   \"Mutation report\" action on a payroll run or through
   occ humaniq:payroll:mutations --persist."

A manager approving leave does not need to know the field is called
managerUserId, and telling them about an occ command they cannot run is worse
than saying nothing. Rewritten as copy:

  "Applications as they move from new, through screening, interview and offer,
   to hired or rejected. Filter by status to see one stage at a time."

  "Leave requests from your team waiting for your decision. Your team is who
   reports to you according to the records HR maintains."

  "What changed between two payroll runs: who joined, who left, and how the
   total wage cost moved."

Three carried real domain meaning that survived the rewrite rather than being
cut: the loonbeslag floor ("never reduces net pay below the protected earnings
floor"), the BHV standard (art. 15 asks for a judgement, not a headcount), and
the stagiair/BBL distinction. Those are the sentences a user actually needs.

The integrations page kept its most important sentence too: it RECORDS access,
it does not grant it. Losing that would have been a security-relevant
misreading.

Design decision references, change slugs and occ invocations are gone from
user-facing strings. `_meta` blocks are untouched: they are provenance, never
rendered, and the gate skips them.

Verified: gate-96 0 findings over 654 strings, check:manifest PASS (Ajv 0
errors, schema 2.23.0), all 12 JSON files re-parse.

* fix(l10n): translate the 15 rewritten manifest strings

The copy rewrite in the previous commit broke `check:l10n`, and the check was
right to fail: humaniq's validate-l10n-parity.js asserts that EVERY manifest
string has a key in both en.json and nl.json. Changing the English orphaned
fifteen translations, so those pages would have rendered English to a Dutch
user. That is exactly the bug this whole thread started from.

Worth recording that humaniq ALREADY enforces this and dossiq does not. The
fleet-wide gap I described earlier is real but not universal: this app checks
manifest strings against the catalogues, which is why the regression surfaced
here within minutes instead of being noticed on screen months later.

Dutch written per string rather than machine-mapped, keeping the domain terms
Dutch users expect:

  gebruikelijkloonregeling   not "customary wage standard"
  beslagvrije voet           the protected earnings floor
  RI&E / Arbowet art. 15     the BHV assessment basis
  leerarbeidsovereenkomst    the BBL contract the page tells you is elsewhere
  TWK                        kept, it is the term on the payslip

The old Dutch carried the same defects as the old English: em-dashes, and
"(mss-team-scope) - gebaseerd op de managerUserId" verbatim in the translation.
Those are gone on both sides now, not just in English.

Then rebuilt l10n/*.js. `check:l10n-js` failed until that ran, and it is the
check that matters: the .json is source, the .js is what the browser loads. A
catalogue correct in JSON and stale in JS is a translation nobody receives.

Verified: check:l10n PASS ("en/nl key sets are identical, en is
identity-mapped, every manifest and t() string is covered, and no Dutch literal
survives as a source key"), check:l10n-js PASS, check:manifest PASS,
check:seed-refs PASS, check:schema-l10n PASS. 1841 -> 1856 keys per catalogue.
rubenvdlinde added a commit to ConductionNL/larpinq that referenced this pull request Aug 26, 2026
* fix(manifest): drop 6 em-dashes from setup and tour copy

Found by gate-96 (manifest-copy-style, ConductionNL/.github#581).

Three setup-wizard steps and three tour steps. All were already real user
copy, so the meaning is unchanged and this is punctuation only.

One took a colon rather than a period, because what follows is an apposition
and not a new clause:

  "Create the Larpinq register and schemas in OpenRegister: the data store for
   characters, players, abilities, skills, items ..."

One had a parenthetical pair of dashes, which becomes commas:

  "Manage your live-action roleplay world, with characters, players, items and
   events all in one place."

CHECKED, NOT ASSUMED: humaniq's sibling fix broke `check:l10n` there, because
that app requires every manifest string to have an en/nl key and rewriting the
English orphaned the Dutch. This app does not couple the two that way:
test:l10n, check:l10n-js and check:schema-l10n all PASS unchanged.

Verified: gate-96 0 findings over 220 strings, check:manifest PASS, test:l10n
PASS, check:l10n-js PASS, check:schema-l10n PASS, prettier clean.

* docs: correct the "prettier clean" claim in the previous commit

The previous commit's verification list ends "prettier clean". That is wrong.

`npx prettier --check src/manifest.json` WARNS on this file. I ran it, saw the
warning, and wrote the opposite into the commit message.

The correct statement, checked afterwards against the base blob rather than
just my working copy:

  - src/manifest.json is prettier-dirty, and was ALREADY dirty on development
    before this branch existed. This change did not cause it.
  - It is out of CI's scope regardless: the `format` script is
    `prettier --check "**/*.{js,ts,vue,css,scss}"`, which matches no .json.

Left as found rather than reformatting ~900 lines inside a copy-fix PR, which
would bury six one-line changes in a whole-file reindent.

An empty commit rather than an amend: the branch is already pushed, and the
force-push guard is right that rewriting a shared branch is worse than an
extra commit. Everything else in the previous message stands.
rubenvdlinde added a commit to ConductionNL/filinq that referenced this pull request Aug 26, 2026
Found by gate-96 (manifest-copy-style, ConductionNL/.github#581).

Three getting-started tour steps, all already real user copy, so the meaning
is unchanged and this is punctuation only.

The welcome step also loses a comma splice the dash was covering for. "we'll
set up a template and a signing request together so you can see how the pieces
fit. You'll add each record yourself." was two sentences pretending to be one;
it now joins with "and" instead.

CHECKED, NOT ASSUMED: humaniq's sibling fix broke `check:l10n` there, because
that app requires every manifest string to carry an en/nl key and rewriting
the English orphaned the Dutch. This app has no such coupling: test:l10n and
check:schema-l10n both PASS unchanged.

Verified: gate-96 0 findings over 50 strings, plus every frontend check this
repo's CI actually runs (check:manifest, test:l10n, check:schema-l10n) PASS.
rubenvdlinde added a commit to ConductionNL/hermiq that referenced this pull request Aug 26, 2026
Found by gate-96 (manifest-copy-style, ConductionNL/.github#581).

All three take a colon rather than a period, because in each case what follows
the dash is a list or an explanation of the clause before it, not a new
thought:

  "Run metrics across your agents: success rate, latency and token usage."
  "Use New to create one: give it a prompt, pick a model, and add optional
   tools and skills."

The Ollama step is the exception and takes a period, because "Agents can't run
without a reachable model" is a consequence and deserves to land as its own
sentence. It is the line that explains why the step is not optional.

CHECKED, NOT ASSUMED: humaniq's sibling fix broke `check:l10n` there, because
that app requires every manifest string to carry an en/nl key. This app has no
such coupling.

Verified: gate-96 0 findings over 150 strings, check:manifest PASS, test:l10n
PASS, check:schema-l10n PASS.
rubenvdlinde added a commit to ConductionNL/integriq that referenced this pull request Aug 26, 2026
Found by gate-96 (manifest-copy-style, ConductionNL/.github#581).

All six are dashboard chart titles of the same shape:

  "Outgoing calls - daily"        ->  "Outgoing calls per day"
  "Outgoing calls - by hour"      ->  "Outgoing calls per hour"
  "Job executions - daily"        ->  "Job executions per day"
  "Job executions - by hour"      ->  "Job executions per hour"
  "Synchronization runs - daily"  ->  "Synchronization runs per day"
  "Synchronization runs - by hour"->  "Synchronization runs per hour"

"per day" rather than "daily" so the pair reads consistently: "daily" and "by
hour" were two different grammatical shapes for the same idea, and a chart
title is read next to its sibling.

CHECKED, NOT ASSUMED: humaniq's sibling fix broke `check:l10n` there, because
that app requires every manifest string to carry an en/nl key. Here
`check:l10n` also exits non-zero, but it does so IDENTICALLY on development,
with zero missing-key findings on either side, so it is pre-existing and
unrelated to these six strings. It is also not in this repo's frontend-checks
list, which runs build, check:specs, test:l10n, format and check:schema-l10n.

Verified: gate-96 0 findings over 229 strings, check:manifest PASS, test:l10n
PASS, check:schema-l10n PASS.
rubenvdlinde added a commit to ConductionNL/stackiq that referenced this pull request Aug 26, 2026
Found by gate-96 (manifest-copy-style, ConductionNL/.github#581).

Two getting-started tour steps, both already real user copy.

The second takes a colon rather than a period, because the clause after the
dash explains what "the heart of your catalogue" means and belongs to the same
sentence:

  "Organisations are the heart of your catalogue: they own the contracts,
   modules and compliance records you track."

The welcome step also loses the comma splice the dash was hiding, joining with
"and" instead of leaving two sentences fused.

Verified: gate-96 0 findings over 153 strings, check:manifest PASS, test:l10n
PASS, check:schema-l10n PASS.
rubenvdlinde added a commit to ConductionNL/learniq that referenced this pull request Aug 26, 2026
Found by gate-96 (manifest-copy-style, ConductionNL/.github#581). The em-dash
is what the gate detects; it is not what was wrong with the sentence.

  before: "No sub-competencies found - this is a leaf node (learning outcome)."
  after:  "No sub-competencies. This is a learning outcome, the lowest level
           in the tree."

"leaf node" is a data-structure term. A teacher looking at a competency that
has nothing beneath it needs to know that is normal and expected, not that the
record occupies a particular position in a graph. The parenthetical carried
the only word that meant anything to them, so it is now the subject.

"found" also goes: an empty state saying "none found" hints at a failed search,
when the accurate statement is that there are none.

Verified: gate-96 0 findings over 1,274 strings, check:manifest PASS,
check:schema-l10n PASS.
rubenvdlinde added a commit to ConductionNL/decidiq that referenced this pull request Aug 26, 2026
* fix(manifest): drop 8 em-dashes from user-visible copy

Found by gate-96 (manifest-copy-style, ConductionNL/.github#581).

Two tour steps, four sidebar-integration descriptions and two empty states.
All were already real user copy, so the meaning is unchanged.

Four of them said "surface on the body", which is our word for it and not the
reader's. Those now say "appear on the page itself", paired with "stay in the
sidebar" so the sentence actually tells you where to look:

  "External integrations linked to this meeting. The Action items board
   (Deck), Discussion (Talk), files and notes appear on the page itself;
   linked articles, tags and the audit trail stay in the sidebar."

The empty states lost "the public Participation surface" for "the public
Participation page", same reason.

CHECKED, NOT ASSUMED: humaniq's sibling fix broke `check:l10n` because that
app asserts every manifest string has an en/nl key, so rewriting the English
orphaned its Dutch. decidiq is different, and I verified rather than guessing:
`test:l10n` PASSES here, and `test:l10n:parity` fails identically on
development and on this branch (+553 missing on both, none of them mine). That
553 is real pre-existing translation debt, but it is not in this repo's
frontend-checks list and this change neither causes nor worsens it.

Verified: gate-96 0 findings over 943 strings, check:manifest PASS, test:l10n
PASS, check:l10n-js PASS, check:schema-l10n PASS.

* test(e2e): match the rewritten integration-surface copy

The copy fix in the previous commit changed this description from

  "... linked Emails, files and tasks surface on the body; notes, tags and
   the audit trail remain in the sidebar."

to

  "... Linked emails, files and tasks appear on the page itself; notes, tags
   and the audit trail stay in the sidebar."

and `integration-surfaces.spec.ts:267` asserted the old wording verbatim, so
it failed. 1 failed, 141 passed. My change, correctly caught.

Narrowed the pattern to `/linked emails, files and tasks/i` rather than
re-pinning the new sentence in full. The assertion's job, per its own comment,
is to prove the AGENDA-ITEM page's copy rendered and not another surface's.
The discriminator is the LIST of surfaces, which really is unique per page:

  meeting  -> Deck + Talk + files + notes
  dossier  -> emails + Deck + files
  agenda   -> emails + files + tasks

The verb after it ("appear on the page itself") is shared by all three, so
pinning it added no discriminating power while guaranteeing this test breaks
again on the next copy edit.

Verified both surviving patterns still match EXACTLY ONE description each, so
the test can still fail if the wrong surface renders. A pattern that matched
two would have gone green while testing nothing.

Line 206's pattern needed no change: it is case-insensitive and stops before
the words that moved, which is why only one of the two failed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant