From 24a014ddcff485ea32b95b8e5357bcc5f174942a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2026 17:49:14 +0000 Subject: [PATCH] Sync generated provider output --- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- .../scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++- 15 files changed, 420 insertions(+), 15 deletions(-) diff --git a/.agents/skills/impeccable/scripts/lib/concept-catalog.mjs b/.agents/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.agents/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.agents/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/.claude/skills/impeccable/scripts/lib/concept-catalog.mjs b/.claude/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.claude/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.claude/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/.cursor/skills/impeccable/scripts/lib/concept-catalog.mjs b/.cursor/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.cursor/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.cursor/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/.gemini/skills/impeccable/scripts/lib/concept-catalog.mjs b/.gemini/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.gemini/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.gemini/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/.github/skills/impeccable/scripts/lib/concept-catalog.mjs b/.github/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.github/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.github/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/.grok/skills/impeccable/scripts/lib/concept-catalog.mjs b/.grok/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.grok/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.grok/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/.kiro/skills/impeccable/scripts/lib/concept-catalog.mjs b/.kiro/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.kiro/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.kiro/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/.opencode/skills/impeccable/scripts/lib/concept-catalog.mjs b/.opencode/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.opencode/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.opencode/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/.pi/skills/impeccable/scripts/lib/concept-catalog.mjs b/.pi/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.pi/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.pi/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/.qoder/skills/impeccable/scripts/lib/concept-catalog.mjs b/.qoder/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.qoder/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.qoder/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/.rovodev/skills/impeccable/scripts/lib/concept-catalog.mjs b/.rovodev/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.rovodev/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.rovodev/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/.trae-cn/skills/impeccable/scripts/lib/concept-catalog.mjs b/.trae-cn/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.trae-cn/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.trae-cn/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/.trae/skills/impeccable/scripts/lib/concept-catalog.mjs b/.trae/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.trae/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.trae/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/.vibe/skills/impeccable/scripts/lib/concept-catalog.mjs b/.vibe/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/.vibe/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/.vibe/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); } diff --git a/plugin/skills/impeccable/scripts/lib/concept-catalog.mjs b/plugin/skills/impeccable/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/plugin/skills/impeccable/scripts/lib/concept-catalog.mjs +++ b/plugin/skills/impeccable/scripts/lib/concept-catalog.mjs @@ -50,9 +50,36 @@ export function normalizeConceptForm(value) { .trim(); } -export function validateConceptEntry(concept, { existingForms = new Map() } = {}) { +export function validateConceptEntry(concept, { existingForms = new Map(), axes = null } = {}) { const errors = []; const id = concept?.id || '(unknown)'; + + // Recorded aesthetic axis values. Optional, and absent means the value is + // inferred from the system rules instead. Some axes cannot be inferred at all: + // depth's keyword probe matched worlds that said "no cast shadow anywhere", + // and motion and colour strategy describe properties the rules never state, so + // a wave that assigns those has to record them or the assignment is lost. + // Validated against the axes definition when the caller supplies it, because a + // typo would read as "unrecorded" and silently fall back to a probe that is + // known not to work. + if (concept?.axes !== undefined && concept.axes !== null) { + if (typeof concept.axes !== 'object' || Array.isArray(concept.axes)) { + errors.push(`concept ${id} axes must be an object of axis id to value id`); + } else if (axes) { + const byId = new Map((axes.axes || []).map(axis => [axis.id, axis])); + for (const [axisId, valueId] of Object.entries(concept.axes)) { + const axis = byId.get(axisId); + if (!axis) { + errors.push(`concept ${id} names unknown axis "${axisId}"`); + } else if (!(axis.values || []).some(value => value.id === valueId)) { + errors.push( + `concept ${id} axis "${axisId}" has unknown value "${valueId}" ` + + `(expected one of ${(axis.values || []).map(v => v.id).join(', ')})` + ); + } + } + } + } if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(concept?.id || '')) { errors.push(`invalid concept id: ${String(concept?.id)}`); }