check:describe-group renders from the real catalogue too, and runs in gates:pre

Catalogue arm by the UPG-100 owner: one row per GSEC level through describeGroup against the
shipping packet-strings.json, in en and ar, failing on any empty sentence. The existing arms test
the function on fixture strings; this one tests that the catalogue can feed it. It lands with the
ten keys, so it arrives green.
This commit is contained in:
Doxa Maps 2026-09-28 14:40:05 +00:00
parent 9fddcb9901
commit c99efff8a6
2 changed files with 21 additions and 2 deletions

View file

@ -13,6 +13,7 @@
*/
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { readFileSync } from 'node:fs'
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const { describeGroup } = await import(resolve(ROOT, 'library/packets/describeGroup.js'))
@ -109,6 +110,24 @@ if (cut.length > 40) fails.push(`maxChars 40 honoured: got ${cut.length}`)
if (!cut.endsWith('…')) fails.push(`truncation is marked: ${JSON.stringify(cut)}`)
if (/\s$/.test(cut.slice(0, -1))) fails.push(`truncation trims its tail: ${JSON.stringify(cut)}`)
// ── 7. THE CATALOGUE ARM ─────────────────────────────────────────────────────────────────────────
// ★ EVERYTHING ABOVE TESTS THE LOGIC AGAINST A FIXTURE, AND THAT IS THE RIGHT DESIGN — but it means
// this gate stayed GREEN while describeGroup returned "" for every group in the product, because the
// ten keys it needs (desc_where_both/_country/_language, desc_gsec_0..6) were not in the SHIPPING
// packet-strings.json. A green gate proved the function and said nothing about the catalogue. Wiring
// the profile table then would have blanked the wide description column on all 100 rows, with no
// error and no empty state — the same "returns nothing, looks like no data" shape as a fetch missing
// its selector field. So: run the REAL catalogue, and assert a non-empty sentence per GSEC level.
// en AND an RTL locale, because a per-key fallback can hide a missing block behind English.
const REAL = JSON.parse(readFileSync(resolve(ROOT, 'library/scripts/posters/packet-strings.json'), 'utf8'))
for (const locale of ['en', 'ar']) {
for (const gsec of [0, 1, 2, 3, 4, 5, 6]) {
const row = { ...G, imb_gsec: gsec }
const out = describeGroup(row, locale, { strings: REAL, countryName: CN })
if (!out) fails.push(`CATALOGUE: describeGroup renders nothing for GSEC ${gsec} in '${locale}' — the shipping packet-strings.json is missing its desc_* keys, so the packet's widest column would print empty`)
}
}
if (fails.length) {
console.error('DESCRIBE-GROUP: FAIL')
for (const f of fails) console.error(` ✗ ${f}`)

View file

@ -270,7 +270,7 @@
"fix:sri": "node internal/fix-sri.mjs",
"gallery": "node internal/generate-gallery.mjs",
"gates:post": "node internal/run-parallel.mjs --label=post -- check:capability-derived check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:zoning-declared check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:backlog-refs check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:regions-uses-library check:shadow-css-parses check:square-map-controls check:preset-config-merge check:frozen-labels",
"gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts",
"gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:describe-group",
"gen:iso3-to-alpha2": "node library/scripts/generate-iso3-to-alpha2.mjs",
"internet:adm1": "node plugin-designs/cities-internet/scripts/build-adm1-internet.mjs",
"library-index": "node internal/generate-library-index.mjs",
@ -346,7 +346,7 @@
"check:dashboard-filter-a11y": "the dashboard's cross-filter is operable and audible: every filter chip is a <button type=\"button\"> with an aria-label naming the ACTION (a filter that can be set by keyboard must be clearable by keyboard), the shell carries exactly ONE always-rendered polite role=\"status\" region so a brush that changes a number far from the control is announced once rather than six times or not at all, and no resize handle's accessible name is a raw English literal. AST over the parsed templates plus the built bundle — no browser (rega's alone).",
"check:dashboard-geocoder-provider": "the research dashboard leaf at ?provider=maplibre makes ZERO requests to api.mapbox.com, and its search bars still suggest (built tree; browser gate). It was kept out of gates:post while the generated leaf hard-linked the Mapbox engine; the leaf now loads only the engine it is asked for.",
"check:dep-names": "every declared dep is a REAL package name (a rename once rewrote @capacitor/* and broke bun install in a clone)",
"check:describe-group": "the prayer packet's generated profile sentence (library/packets/describeGroup.js): each GSEC level states its own fact, and an index-ordered language name (\"Arabic, Moroccan\") is un-inverted by library/utils/languageLabel.js without shredding a comma inside parentheses, the 13 names the old split printed reversed. Headless.",
"check:describe-group": "the prayer packet's generated profile sentence (library/packets/describeGroup.js): each GSEC level states its own fact, and an index-ordered language name (\"Arabic, Moroccan\") is un-inverted by library/utils/languageLabel.js without shredding a comma inside parentheses, the 13 names the old split printed reversed. A catalogue arm renders every GSEC level from the REAL packet-strings.json in en and ar and fails on a blank sentence. Headless; in gates:pre.",
"check:descriptions": "every shipped profile must say what it is (reads the BUILT manifest)",
"check:done-evidence": "a card may not reach status:done with NO bluf AND NO fields — the Doxa team inherits this board and an evidence-less 'done' cannot be reconciled against the code. 10 pre-existing are baselined by id; the list should only ever SHRINK.",
"check:dxlife": "⚠ NOT WAVE-SAFE, deliberately unwired: exit codes 0/1/2 are a DOCUMENTED contract (0 unchanged · 1 drift · 2 clone not found), and run-parallel grades any non-zero as FAILURE — so a checkout without the sibling /root-dx-life clone goes red on a precondition. Changing the contract could break another consumer; hand-run it, or give it a recognised SKIP first.",