Found while implementing #13871 (the ghost-label half of the same three vocabularies). Filed unassigned for triage. No severity asserted.
The shape
packages/cli/src/commands/generate.ts carries three field-type vocabularies:
FIELD_TYPE_MAP — the TypeScript type os generate types emits, with || 'unknown' as the fallback.FIELD_TYPE_SQL_MAP — the SQL column type os generate migration --format sql emits, with || 'TEXT' as the fallback.- the
switch (fType) in generateMigrationTs — the knex builder os generate migration emits (typescript, the DEFAULT format), with default: table.text(...).
#13871 measured and removed the labels in those vocabularies that name types the platform does not have. This card is the other direction: real FieldType members that no vocabulary mentions, so they land on the fallback.
Measured against the 49-member FieldType on origin/main:
- Missing from both maps (21):
secret, toggle, radio, checkboxes, tree, user, avatar, video, audio, summary, composite, repeater, record, location, address, code, slider, signature, qrcode, progress, tags. - Missing from the migration switch (24): the 21 above minus
user (which has its own arm), plus image, file, autonumber, vector.
Unlike the ghost labels, this half is reachable through the supported door: these are valid FieldType members, so defineStack accepts them and they arrive at the generator. Concretely, today:
Why it is a decision, not a mechanical fix
Each missing member needs a real answer to "what column type, and what TypeScript type, does this deserve" — and some of those answers are cross-cutting (a POINT column is not portable to SQLite; the embedded-structured family may want one shared rule; secret stores an opaque ref whose TS type is a policy statement). #13871 deliberately left this alone rather than inventing answers next to a ghost removal, and its pin is written forward-only so it does not prejudge this card: it asserts every vocabulary key IS a FieldType member, never that every FieldType member has a key.
Notes for triage
Dedup
search_issues for this shape returned total_count: 0; a control query in the same session returned #13871 and #4659, so the empty result is a reading rather than a broken search.
Found while implementing #13871 (the ghost-label half of the same three vocabularies). Filed unassigned for triage. No severity asserted.
The shape
packages/cli/src/commands/generate.tscarries three field-type vocabularies:FIELD_TYPE_MAP— the TypeScript typeos generate typesemits, with|| 'unknown'as the fallback.FIELD_TYPE_SQL_MAP— the SQL column typeos generate migration --format sqlemits, with|| 'TEXT'as the fallback.switch (fType)ingenerateMigrationTs— the knex builderos generate migrationemits (typescript, the DEFAULT format), withdefault: table.text(...).#13871 measured and removed the labels in those vocabularies that name types the platform does not have. This card is the other direction: real
FieldTypemembers that no vocabulary mentions, so they land on the fallback.Measured against the 49-member
FieldTypeonorigin/main:secret,toggle,radio,checkboxes,tree,user,avatar,video,audio,summary,composite,repeater,record,location,address,code,slider,signature,qrcode,progress,tags.user(which has its own arm), plusimage,file,autonumber,vector.Unlike the ghost labels, this half is reachable through the supported door: these are valid
FieldTypemembers, sodefineStackaccepts them and they arrive at the generator. Concretely, today:type: 'secret'generates TSunknownand aTEXT/table.textcolumn.type: 'location'generates TSunknownandTEXT/table.text— while the ghostgeo_pointthat [finding] packages/cli generate.ts 的迁移 codegen switch 携带自己的幽灵字段类型词表——'slug'/'ip_address'/'encrypted'/'integer' 是 case 标签但均非 FieldType 成员(#13716 同类,隔一个包) #13871 removed used to generate{ lat: number; lng: number }andPOINT, which is the shape the real member arguably wants.type: 'address','composite','repeater','record'are embedded structured values stored as JSON on the parent row, and all four generate TSunknownand a scalarTEXTcolumn rather than thejsonb/ object shapejsonandmultiselectalready get.Why it is a decision, not a mechanical fix
Each missing member needs a real answer to "what column type, and what TypeScript type, does this deserve" — and some of those answers are cross-cutting (a
POINTcolumn is not portable to SQLite; the embedded-structured family may want one shared rule;secretstores an opaque ref whose TS type is a policy statement). #13871 deliberately left this alone rather than inventing answers next to a ghost removal, and its pin is written forward-only so it does not prejudge this card: it asserts every vocabulary key IS aFieldTypemember, never that everyFieldTypemember has a key.Notes for triage
packages/cli⇒ likelydomain:cli.generate-field-type-vocabulary.pin.test.ts(added by [finding] packages/cli generate.ts 的迁移 codegen switch 携带自己的幽灵字段类型词表——'slug'/'ip_address'/'encrypted'/'integer' 是 case 标签但均非 FieldType 成员(#13716 同类,隔一个包) #13871) is where a coverage assertion would go if this card decides total coverage is the rule; it already reads all three vocabularies out of the source.defaultarm exists on purpose) is part of the decision, so "add all 21" is not assumed to be the answer.Dedup
search_issuesfor this shape returnedtotal_count: 0; a control query in the same session returned #13871 and #4659, so the empty result is a reading rather than a broken search.