|
| 1 | +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. |
| 2 | + |
| 3 | +import type { SemanticMigration } from '../../types.js'; |
| 4 | + |
| 5 | +// A shorthand that silently declared a value nobody wrote, refused instead. The |
| 6 | +// Field.select string shorthand used to delete every character outside a-z, 0-9 |
| 7 | +// and _, so 'no-overlay' was declared as nooverlay. It now keeps the string as |
| 8 | +// written beyond lower-casing and whitespace to _, and SelectOptionSchema.value |
| 9 | +// refuses what is no system identifier, as it always did for an explicit value. |
| 10 | +// A structured TODO, not a D2 conversion: the shorthand runs in the author's own |
| 11 | +// TypeScript, so no stored artifact carries it, and which value the author meant |
| 12 | +// to store (no_overlay, or the column is not a select at all) is intent no |
| 13 | +// artifact records. |
| 14 | +export const entry: SemanticMigration = { |
| 15 | + id: 'field-select-shorthand-keeps-written-value', |
| 16 | + // No backticks in `surface` — build-upgrade-guide renders it inside a code |
| 17 | + // span already, and a nested backtick would close it. |
| 18 | + surface: |
| 19 | + 'data.Field.select string shorthand (Field.select([...]) and Field.select({ options: [...] }) with ' |
| 20 | + + 'string options) — a string holding a character outside letters, digits, whitespace, _ and . ' |
| 21 | + + '(a hyphen, most often) is now refused at options.N.value instead of being declared with that ' |
| 22 | + + 'character deleted', |
| 23 | + replacement: |
| 24 | + 'write the option explicitly with the value you store: Field.select({ options: [{ label: ' |
| 25 | + + '\'no-overlay\', value: \'no_overlay\' }] }). Where rows already hold the hyphenated spelling, the ' |
| 26 | + + 'column is not a select: declare it as Field.text({ maxLength: 255 })', |
| 27 | + reason: |
| 28 | + 'The shorthand built each option as { label: s, value: s lower-cased, whitespace runs turned into _, ' |
| 29 | + + 'and every other character outside a-z, 0-9 and _ deleted }. The last step was undocumented and ' |
| 30 | + + 'silent: Field.select([\'no-overlay\']) declared nooverlay, and a record written with no-overlay, the ' |
| 31 | + + 'value as written, was refused with invalid_option on a writable field and held a value no option ' |
| 32 | + + 'declared on a read-only one. The ruling on this defect made the shorthand stop rewriting and join ' |
| 33 | + + 'the refusal an explicit { value: \'no-overlay\' } already met, rather than inventing a parallel one ' |
| 34 | + + '(ADR-0049: a declaration holds what its writer writes). SelectOptionSchema.value accepts and refuses ' |
| 35 | + + 'exactly what it did before; the shorthand only stops hiding a value from it. A dot is now kept ' |
| 36 | + + 'rather than deleted, so \'v1.2\' declares v1.2, a valid identifier, where it declared v12. An ' |
| 37 | + + 'object built by the old helper and stored carries explicit option values (nooverlay), which still ' |
| 38 | + + 'parse, so no stored row or definition needs rewriting. Measured on this tree at the change: the one ' |
| 39 | + + 'in-tree shorthand the strip altered was sys_metadata_audit.lock_state, now a text column; no ' |
| 40 | + + 'example, template, CLI scaffold or published skill writes such a string. ADR-0049 / ADR-0087.', |
| 41 | + acceptanceCriteria: |
| 42 | + 'Load every object module (os validate or os build): ObjectSchema.create and the object save door ' |
| 43 | + + 'report any shorthand option that is no system identifier at fields.FIELD.options.N.value with ' |
| 44 | + + 'invalid_format. Rewrite each as an explicit option whose value is the one your writers store, or ' |
| 45 | + + 'declare the column as text where stored rows hold the hyphenated spelling. Done when every object ' |
| 46 | + + 'loads and every select option value equals a value a writer stores.', |
| 47 | +}; |
0 commit comments