Observation from the #139 work (PR #182). Not a live defect — the two copies agree today. Filing it because the drift has never been drilled and the reason it existed is now gone.
What
scripts/content-lint.mjs validates post frontmatter against two literal sets:
const VALID_TOPIC = new Set([
'ai-agents', 'app-building', 'integration-data', 'automation',
'modernization', 'governance', 'customer-stories',
]);
const VALID_AUDIENCE = new Set(['business', 'it', 'developer', 'general']);
Both restate what src/lib/terms.ts already declares — slugsByGroup('topic') and slugsByGroup('role') — which is also what src/content.config.ts builds its z.enum from. Two producers of one fact.
The drift is one-directional and reaches an author, not a reviewer: add a topic to the taxonomy, use it in a post, and pnpm content:lint rejects the post with Invalid topic: <slug> while astro check and astro build are perfectly happy, because the Zod enum derives from the taxonomy and the lint set does not. The message points at the post, which is correct; the file to edit is the script.
Why now
The list was hard-coded because the script is plain Node and could not import src/lib/terms.ts (it value-imports extensionless ./i18n and ./zhconvert, which only Vite resolves). PR #182 removes that constraint: the taxonomy data now lives in src/lib/term-data.ts, a module with no imports, and content-lint.mjs already imports it to resolve topic hub links. Deriving both sets from RAW_TERMS is a few lines in a file the script already loads.
Notes for whoever takes it
Observation from the #139 work (PR #182). Not a live defect — the two copies agree today. Filing it because the drift has never been drilled and the reason it existed is now gone.
What
scripts/content-lint.mjsvalidates post frontmatter against two literal sets:Both restate what
src/lib/terms.tsalready declares —slugsByGroup('topic')andslugsByGroup('role')— which is also whatsrc/content.config.tsbuilds itsz.enumfrom. Two producers of one fact.The drift is one-directional and reaches an author, not a reviewer: add a topic to the taxonomy, use it in a post, and
pnpm content:lintrejects the post withInvalid topic: <slug>whileastro checkandastro buildare perfectly happy, because the Zod enum derives from the taxonomy and the lint set does not. The message points at the post, which is correct; the file to edit is the script.Why now
The list was hard-coded because the script is plain Node and could not import
src/lib/terms.ts(it value-imports extensionless./i18nand./zhconvert, which only Vite resolves). PR #182 removes that constraint: the taxonomy data now lives insrc/lib/term-data.ts, a module with no imports, andcontent-lint.mjsalready imports it to resolve topic hub links. Deriving both sets fromRAW_TERMSis a few lines in a file the script already loads.Notes for whoever takes it
src/lib/term-data.tsmust exist).solutionsandindustriesare not checked by the script at all today; the same derivation would cover them, but that is an added check, not a de-duplication, so it deserves its own decision rather than a silent ride-along.scripts/content-lint.mjsis also wanted by MDX frontmatter is now split and parsed in two scripts with no shared helper #162; whoever schedules these should serialise them.