From 166e4481e140dcb464511b330db28bd090f3ec07 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Thu, 30 Jul 2026 10:48:41 -0700 Subject: [PATCH] Let a concept record its aesthetic axis values Three of the six axes cannot be read from a world's prose, and widening their keywords manufactures signal rather than finding it. Depth's probe matched worlds that said "no cast shadow anywhere" and "without perspective or depth"; motion and colour strategy describe properties the system rules never state, so they place 28% and 7%. An optional axes object on the concept records the value instead. Absent means inferred from the rules as before, so nothing needs backfilling. Validated against the axes definition when the caller supplies it, because a typo would read as "unrecorded" and fall back to a probe already known not to work, which is the quietest way for this to fail. This is what makes an assigned wave measurable. If a wave draws "drenched" and "simulated physics" before designing anything, the world it produces has to carry those values or the assignment is lost the moment it lands, and occupancy goes back to guessing at prose. Co-Authored-By: Claude Opus 5 (1M context) --- skill/scripts/lib/concept-catalog.mjs | 29 ++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/skill/scripts/lib/concept-catalog.mjs b/skill/scripts/lib/concept-catalog.mjs index fc5b66a46..9c20711ef 100644 --- a/skill/scripts/lib/concept-catalog.mjs +++ b/skill/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)}`); }