check:studio-rail — scope the label rule by PROFILE stage, not bundle stage
regions-plugin challenged the rule and was right. A BUNDLE reads `stage: production` when ANY ONE of its profiles is production, so scoping the loop by b.stage drags every experimental sibling in with it. Measured: ALL SEVEN violations it reported were false. cities-internet is a production bundle whose connectivity-list and data-tables are EXPERIMENTAL; gospel-resources likewise for charts / data-tables / resources-list; and my-upg-100-list's own poster-wall and upg-100-list are experimental too -- only upg-100-mfe is production, and it has carried a label all along. With p.stage the gate reports 0. THE SAME TRAP A THIRD TIME. I moved off stageOf (bundle-keyed) after catching it once, and moved to b.stage -- the same mistake wearing a different field name. The profile's own stage is the only one that answers "is this row production". THE SELF-TEST HAD A HOLE THAT MADE THE BUG INVISIBLE: every fixture bundle had exactly one profile, so bundle-stage and profile-stage always agreed and scoping by the wrong one could not be detected. A fixture where the two CANNOT disagree cannot test which one you read. Adds a mixed bundle whose production profile must FAIL and whose experimental profile must PASS; 6/6. The two meta files in be398ce8f stay. They were a fix for a non-problem -- both profiles are experimental -- but a named row is better than a humanized id, and they cost nothing.
This commit is contained in:
parent
114df6f6c4
commit
1223fa7b45
1 changed files with 25 additions and 5 deletions
|
|
@ -25,8 +25,17 @@ import { TREE_NAME } from './_tree-name.mjs'
|
|||
function undeclaredLabels(manifest) {
|
||||
const out = []
|
||||
for (const b of manifest.bundles || []) {
|
||||
if (b.stage !== 'production') continue
|
||||
for (const p of b.profiles || []) {
|
||||
// ⚠ PROFILE STAGE, NOT BUNDLE STAGE — the same trap, a third time. A BUNDLE reads
|
||||
// `stage: production` when ANY ONE of its profiles is production, so scoping the loop by
|
||||
// b.stage drags every experimental sibling in with it. Measured: cities-internet is a
|
||||
// production bundle whose connectivity-list and data-tables are EXPERIMENTAL, and
|
||||
// gospel-resources likewise for charts / data-tables / resources-list. That was 5 false
|
||||
// violations out of 7 (regions-plugin caught it, 2026-09-28).
|
||||
// I had already moved off stageOf (bundle-keyed) once; moving to b.stage was the SAME
|
||||
// mistake wearing a different field name. The profile's own stage is the only one that
|
||||
// answers "is this row production".
|
||||
if (p.stage !== 'production') continue
|
||||
const declared = p.label
|
||||
if (declared === null || declared === undefined || !String(declared).trim()) {
|
||||
out.push(`${b.bundle}/${p.profile} is PRODUCTION but declares no "label" in its <profile>.meta.json — the rail falls back to a humanized id, which names nothing`)
|
||||
|
|
@ -42,11 +51,20 @@ function undeclaredLabels(manifest) {
|
|||
// fails everything is as useless as one that fails nothing.
|
||||
if (process.argv.includes('--self-test')) {
|
||||
const fixture = { bundles: [
|
||||
{ bundle: 'planted-prod', stage: 'production', profiles: [{ profile: 'nameless', label: null }] },
|
||||
{ bundle: 'planted-exp', stage: 'experimental', profiles: [{ profile: 'nameless', label: null }] },
|
||||
{ bundle: 'planted-ok', stage: 'production', profiles: [{ profile: 'named', label: 'A Real Name' }] },
|
||||
{ bundle: 'planted-prod', stage: 'production', profiles: [{ profile: 'nameless', stage: 'production', label: null }] },
|
||||
{ bundle: 'planted-exp', stage: 'experimental', profiles: [{ profile: 'nameless', stage: 'experimental', label: null }] },
|
||||
{ bundle: 'planted-ok', stage: 'production', profiles: [{ profile: 'named', stage: 'production', label: 'A Real Name' }] },
|
||||
// An EMPTY STRING is nameless too — `label: ""` would satisfy a null check and render blank.
|
||||
{ bundle: 'planted-blank', stage: 'production', profiles: [{ profile: 'blank', label: ' ' }] },
|
||||
{ bundle: 'planted-blank', stage: 'production', profiles: [{ profile: 'blank', stage: 'production', label: ' ' }] },
|
||||
// ★ THE CASE THE FIRST SELF-TEST MISSED, and the bug it would have caught. A BUNDLE reads
|
||||
// production when ANY profile is, so a mixed bundle must flag ONLY its production profile.
|
||||
// The earlier fixture gave every bundle one profile, so bundle-stage and profile-stage agreed
|
||||
// and scoping by the wrong one was invisible. A fixture where the two CANNOT disagree cannot
|
||||
// test which one you read.
|
||||
{ bundle: 'planted-mixed', stage: 'production', profiles: [
|
||||
{ profile: 'prod-nameless', stage: 'production', label: null },
|
||||
{ profile: 'exp-nameless', stage: 'experimental', label: null },
|
||||
] },
|
||||
] }
|
||||
const hits = undeclaredLabels(fixture)
|
||||
const cases = [
|
||||
|
|
@ -54,6 +72,8 @@ if (process.argv.includes('--self-test')) {
|
|||
['experimental + no label PASSES', !hits.some((h) => h.startsWith('planted-exp/'))],
|
||||
['production + a real label PASSES', !hits.some((h) => h.startsWith('planted-ok/'))],
|
||||
['production + whitespace-only FAILS', hits.some((h) => h.startsWith('planted-blank/'))],
|
||||
['mixed bundle: its PRODUCTION profile FAILS', hits.some((h) => h.startsWith('planted-mixed/prod-nameless'))],
|
||||
['mixed bundle: its EXPERIMENTAL profile PASSES', !hits.some((h) => h.startsWith('planted-mixed/exp-nameless'))],
|
||||
]
|
||||
let ok = 0
|
||||
for (const [name, pass] of cases) { console.log(` ${pass ? '✓' : '✗'} ${name}`); if (pass) ok++ }
|
||||
|
|
|
|||
Loading…
Reference in a new issue