Repository navigation
#19188 split: 39 top-level zod-only keys are structured controls needing a designed widget, not a row #19332
Description
Activity
Reached in 取卡全序 with its path now FREE, and still not dispatched — the first deliverable is 39 judgements, not a PR. 2026-09-22T07:34Z
domain:specseat 5. ⛔ Not a claim, ⛔ not a re-grade, ⛔ no label written.The serial hold on this card is gone, so that is no longer the reason
Sibling #19331 landed as PR #19673, merge commit
408ca2e36, single parent. ⇒packages/spec/src/**/*.form.tsand the fourplatform-objectsmetadata-form catalogs are no longer held. A later seat should ⛔ not re-derive a path collision here; there isn't one.Why it is still not a dev flight
The card says it itself:
⚠️ A reasonable first act on this card is to decide, per key, between 「design a control」 and 「record a reason not to offer it」 — the second answer moves the key into the recorded-reason bucket rather than into work. ⛔ That is a judgement per key and this seat makes none of them here.⇒ the first deliverable is a 39-row disposition table, and only what survives it is implementable. A dev dispatched against this card as written would have to make 39 design judgements inside one PR, which is the shape triage refused when it split the census (「⛔ do not dispatch this as one flight」) — and the scalar sibling that just landed is the measurement of why: 45 mechanical rows already needed two rounds of rework and produced three filed findings. Thirty-nine designed widgets is not that change with a different number on it.
What the landed sibling hands this card, ⛔ free
Three findings were filed out of #19331 that this card's design pass will hit on its first day, so they are named here rather than re-measured:
- [finding] the form face cannot express an enum whose members carry a hyphen or a capital —
FormSelectOptionSchema.valueis a system identifier, sosystem-data,new-tabandperRecordare unspellable as option values #19678 —FormSelectOptionSchema.valueis a system identifier (^[a-z][a-z0-9_.]*$), so an enum member carrying a hyphen or a capital cannot be spelled as an option value at all;defineFormthrowsinvalid_formatat module load. Any widget design that emits an options list meets this bound. - [finding] the object designer's quick-add grid offers two formula return types
FieldSchema.returnTyperefuses — picking Datetime or Currency writes a value the parse rejects #19677 — a shipped example of a control offering members the schema refuses (object.form.ts'sfields.returnType), which is the failure mode a per-key design pass is supposed to avoid. - [finding]
field.formatis onez.string()key carrying THREE value vocabularies — the engine reads it as an autonumber pattern, objectui as a date display style, and itsdescribenames a third that nothing honours #19679 — a key whose value vocabulary is not settled cannot be given help text at all; that is the shape of the 「record a reason」 answer.
And one mechanical fact the disposition pass will need: the i18n catalogs are ratcheted — a new row's
enleaf must be authored inzh-CN/ja-JP/es-ESin the same PR or the catalog check reds on the day it lands (measured on #19673: 45 rows ⇒ 90 leaves ⇒ 270 authored translations, plus three moved pins).What would make it dispatchable
The 39-row disposition (「design a control」 / 「record a reason」 / 「curated subset entry」), with the per-key reasoning, landed on this card or as its sub-cards. Then each surviving group is an ordinary flight. ⛔ This seat does not take that pass today and ⛔ does not pre-empt any of the 39 judgements.
Generated by Claude Code
- [finding] the form face cannot express an enum whose members carry a hyphen or a capital —
objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsClaim: PM loop round 1
Session:session_01CiCTczDo7tGhafXjf61dUJ
Account:os-sales(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:none (analysis round: no branch, no PR)
Worktree:objectstack-issue-19332
Domain:domain:spec
Seat:domain:spec#4(seat post #18917)
File surface: none. This round is read-only. Its deliverable is the per-key disposition table that seat 5's note5772804840names as this card's first deliverable: for each of the 39 keys, 「design a control」 / 「record a reason」 / 「curated subset entry」, with measured evidence and a four-axis recommendation. It lands as anos-dev-reportcomment on this card, withstatus: needs_decisionand no PR. ⛔ No file in the repository is written; a throwaway census probe is deleted after its run. (stop on breach; explain in the report)
Container & model:M,mode:subagent,model: default judgment tier(analysis only)
Clause-②: no
Thread-read: 5772804840
Serial constraints cleared: read at 2026-09-27T19:54Z. The serial hold seat 5 recorded is gone: #19331 landed as PR #19673 (408ca2e36). This round writes no file, so no open PR or in-flight claim can collide with it. This seat's own #19333 flight (claim5859116056) re-derives the same census forfield.format; the two share the gate's helper as an instrument, read-only.What happens after the table. The seat reads the table, groups the keys into at most five decision items, and routes them to the decision box on this card (
needs-user-decision, with options, a recommendation and the four-axis block per item). Whatever an item resolves to becomes an ordinary flight. ⛔ The seat does not pre-empt any of the 39 judgements, and neither does the dev.objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsos-dev-report
#19332 — per-key disposition of the 39 structured zod-only keys (analysis round, no PR)
Measured on framework
4e0f72e8d, objectuif8a9d0fb0(the.objectui-shapin) and hotcrm2f7b2326. Column (c) is examples / hotcrm / platform author counts; (f) is rows × 2 leaves × 4 locales (1r = 8L). D = design a control, R = record a reason, C = curated subset entry. The five groups, their options, the four-axis analysis and the governing text are inopen_questionsbelow. These are recommendations; the seat routes the decisions.# key (a) node ( obj!= strict)(b) verdict · verified reader (c) authors ex / hotcrm / platform (d) Studio edits it another way (e) form-face bound (f) cost recommendation 1 object.accessobj! {default: enum public/private} live · plugin-security permission-evaluator.ts(4 hits)0 / 0 / 6 yes — ObjectDefaultInspector posture toggle none (both members spellable) 1r = 8L D · type: composite(G1)2 object.actionsarr of ActionSchema obj! (40+ keys) live · runtime app-plugin.tscollectBundleActions (22)0 / 0 / 16 yes — studio-design ObjectActionsPanel, and the actiontype editorno row renders a 40-key action inline 1 root omit, 0L R · own editor (G3) 3 object.activityMilestonesarr obj! {field, value, summary, type} live · plugin-audit audit-writers.ts(3); objectui recordActivityFeed1 / 2 / 0 no field pickers bind nothing on an object draft ⇒ text sub-rows 5r = 40L D · repeater, 4 sub-rows (G2) 4 object.externalobj! {remoteName, remoteSchema, writable, columnMap rec, introspectedAt, ignoreColumns} live · runtime external-validation-plugin.ts(44)2 / 0 / 1 (the import generator) yes — external-datasource import flow drafts it ( external-datasource-service.ts)introspectedAtis platform-written (Set by os datasource introspect)1 root omit, 0L R · own editor (G3) 5 object.fieldGroupsarr obj! 9 keys, 3 of them [DEPRECATED → collapse]live · objectui plugin-form sectionGroups / FormPage; dogfood proof 2 / 18 / 0 yes — ObjectFormDesigner / ObjectGroupInspector a derived face offers the 3 deprecated aliases ( object.zod.ts:1206-1210)7r = 56L + 3 nested omit D · curated repeater (G2) 6 object.highlightFieldsarr of string (field names) live · objectui buildDefaultPageSchema (18); dogfood proof 3 / 18 / 76 yes — ObjectSettingsPanel semantic roles field-multibinds nothing on an object draft (ResourceEditPage.tsx:890) ⇒string-tags, thenameFieldtype: textprecedent1r = 8L D · string-tags(G1)7 object.indexesarr obj! {name, fields, unique: false / global / organization} live · driver-sql syncTableIndexes / syncDeclaredIndexes (76) 1 / 15 / 77 no (ObjectFieldInspector: "Object-level indexes[] is the real surface") derived uniqueoffers the deprecated baretrue(object.zod.ts:490) ⇒ inline options global / organization4r = 32L D · repeater, 3 sub-rows (G2) 8 object.listViewsrec of ListView obj! (40+ keys) live · objectui ObjectView (25); framework expandViewContainer 0 / 0 / 40 yes — the viewtype editor (ViewDefaultInspector); #19330 A: per-arm formsno row renders a ListView record 1 root omit, 0L R · own editor (G3) 9 object.publicSharingobj! {enabled, allowedAudiences enum[], allowedPermissions enum[], maxExpiryDays, redactFields, eligibility CEL} live · plugin-sharing share-link-service.tsgetPolicy (18)1 / 1 / 0 no members spellable; eligibilityis CEL ⇒ code/expression;redactFieldsfree text7r = 56L D · curated composite, 6 sub-rows (G2) 10 object.requiredPermissionsU(arr of string / obj! {read, create, update, delete}) live · plugin-security security-plugin.tsgetObjectSecurityMeta (36)0 / 0 / 2 (sys-scim-connection-credential, sys-sso-provider) yes — ObjectDefaultInspector (both arms) string-tagsreads a stored map as[]and overwrites it (widgets.tsx:1290) ⇒json, the only lossless face1r = 8L D · json(G1)11 object.searchableFieldsarr of string live · objectql engine.ts$search /search-filter.ts; objectui ListView1 / 11 / 0 no as highlightFields; a non-stored entry is refused at parse 1r = 8L D · string-tags(G1)12 object.stageFieldU(string / false) live · objectui record-semantics / buildDefaultPageSchema; dogfood proof 1 / 0 / 0 yes — ObjectSettingsPanel (string / false / unset) the falsearm cannot be an option value; the derived face takes the string branch in create mode1 root omit, 0L R · form-face gap (G5) 13 object.systemFieldsU(false / obj! {tenant, audit}) live · spec injected-system-columns.ts(9), objectqlregistry.tsapplySystemFields (11)0 / 0 / 1 no falsearm unspellable1 root omit, 0L R · code-declared platform config (G4) 14 object.tenancyobj! {enabled, tenantField} (drilled row, both children live) live · spec isTenancyDisabled (23), driver-sql (41), plugin-security (23) 0 / 0 / 4 no none 1 root omit, 0L R · code-declared platform config (G4) 15 object.userActionsobj! {create/import/edit/delete: U(boolean / obj! {enabled, visibleWhen, disabledWhen}), exportCsv} live · objectui ObjectGrid / ListView (30 files) 1 / 0 / 6 no per-op object arm unreachable in create (boolean branch first) ⇒ curated to switches 6r = 48L D · composite, 5 sub-rows (G2) 16 field.acceptarr of string (MIME / extension) live · service-storage file-reference-lifecycle.tsassertFileConstraints (10)0 / 3 / 0 no none 1r = 8L D · string-tags+ media-type gate (G1)17 field.currencyConfigobj! {precision, currencyMode enum dynamic/fixed, defaultCurrency} live · objectui i18n currency.ts(4), CurrencyField3 / 0 / 0 no none 1r = 8L D · composite + currencygate (G1)18 field.dependsOnarr of U(string / obj! {field, param}) live · objectui LookupField / form renderer (framework 0: client-only) 3 / 4 / 1 yes — ObjectFieldInspector (string arm) union element: derived face falls to raw JSON; string-tagswould mangle an object entry1r = 8L D · json(G1)19 field.inlineColumnsarr obj! 20 keys (drilled row, all live) live · objectui plugin-form deriveMasterDetail 1 / 0 / 0 no 20 sub-keys; identity-only {name}entries hydrate from the child object5r = 40L + 1 nested subset C · curated subset (G2) 20 field.inlineEditU(boolean / enum grid, form) live · objectui MetadataProvider.tsx:514→ plugin-form deriveMasterDetail resolveInlineMode3 / 0 / 0 no boolean arm cannot be an option; derived face shows a switch in create ⇒ grid/form unreachable 1 root omit, 0L R · form-face gap (G5) 21 field.lookupColumnsarr of U(string / obj! {field, label, width, type}) live · objectui RecordPickerDialog / LookupField 1 / 0 / 0 (showcase uses the object arm) no string-tagswould mangle the only author's object entries1r = 8L D · json(G1)22 field.lookupFiltersarr obj! {field, operator enum incl. notIn, value} live · objectui RecordPickerDialog (17), LookupField 1 / 0 / 0 yes — ObjectFieldInspector notInunspellable (#19678) ⇒ no inline options; mirrorobject.form.ts:2421r = 8L D · jsonmirror (G1)23 field.readonlyWhenU(CEL string / expression envelope) live · objectql rule-validator.tsstripReadonlyWhenFields (63)1 / 0 / 1 yes — ObjectFieldInspector CEL editor none — mirror object.form.ts:3371r = 8L D · code/expression (G1) 24 field.relatedListU(boolean / literal primary) live · objectui deriveRelatedLists.ts(7)2 / 0 / 1 no boolean arm unspellable; derived face shows a switch ⇒ primaryunreachable1 root omit, 0L R · form-face gap (G5) 25 field.relatedListColumnsarr of string (child field names) live · objectui deriveRelatedLists.ts(4)3 / 3 / 0 no a picker would bind the parent, not the child ⇒ string-tags1r = 8L D · string-tags+ lookup gate (G1)26 field.requiredPermissionsarr of string live · plugin-security security-plugin.tsfoldFieldRequiredPermissions (36)0 / 0 / 0 (both platform hits are object-level) no none — mirror app.form.ts:1021r = 8L D · string-tags(G1)27 field.requiredWhenU(CEL string / expression envelope) live · objectql rule-validator.tsevaluateValidationRules (33)1 / 4 / 0 yes — ObjectFieldInspector none — mirror object.form.ts:3381r = 8L D · code/expression (G1) 28 field.storageobj! {notNull} live · driver-sql createColumn + schema-drift.tsdiffManagedTable (8 + 8)0 / 18 files, 68 hits / 5 hits no none; notNullbesiderequiredWhenis refused at parse (loud)1r = 8L D · composite (G1) 29 field.visibleWhenU(CEL string / expression envelope) live · objectui FormPage / ExpressionProvider 2 / 0 / 0 yes — ObjectFieldInspector none — mirror object.form.ts:3361r = 8L D · code/expression (G1) 30 app.contextSelectorsarr obj! {id, label, icon, optionsSource obj, allValue, persist enum} (drilled row, 6 live children) live · objectui ContextSelectors.tsx0 / 0 / 1 ( studio.app.ts)no optionsSourceis a nested endpoint mapping1 root omit, 0L R · code-declared platform config (G4) 31 action.ariaobj! {ariaLabel, ariaDescribedBy, role} ledger livewith an uncited "PARTIAL" note; MEASURED 0 action-surface readers (objectui + framework; lit controlaction.variant= 14 files)0 / 0 / 0 yes — ActionDefaultInspector "More fields" (derived) a row would advertise a control nothing honours 1 root omit after re-grade, 0L R · not honoured (G5) + finding 32 action.bodyExtrarec string → unknown live · objectui useConsoleActionRuntime.tsx(6), ActionRunner1 / 0 / 6 decl. files yes — ActionDefaultInspector "More fields" raw JSON only; refused beside operation: update1r = 8L D · json+type == apigate (G1)33 action.descriptionU(string / locale map) live · objectui ActionParamDialog / useConsoleActionRuntime NOT MEASURED (pattern cannot separate it from other description:keys)yes — "More fields" none — textarea, as the description rows on skill.form.ts:21/flow.form.ts:251r = 8L D · textarea(G1)34 action.errorMessageU(string / locale map) live · objectui action-group / ActionContext toast 0 / 0 / 0 yes — ActionDefaultInspector (curated) none — twin of action.form.ts:105successMessage1r = 8L D · plain row (G1) 35 action.patchrec string → unknown live · runtime action-execution.tsdeclarativeUpdateWrite (14)0 / 0 / 0 yes — ActionDefaultInspector (curated key-value editor) refused without operation: update⇒ visibleWhen gate1r = 8L D · json+updategate (G1)36 action.requiredPermissionsarr of string live · runtime action-execution.tsactionPermissionError (5),domains/actions.ts(4)1 / 0 / n/s yes — "More fields" none — mirror app.form.ts:1021r = 8L D · string-tags(G1)37 action.resultDialogobj! {title, description, acknowledge, format enum incl. code-list, fields arr obj!} live · objectui ActionRunner (14), ActionResultDialog 0 / 0 / 4 yes — "More fields" code-listunspellable (#19678)1 root omit, 0L R · code-declared platform config (G4) 38 page.slotsobj! 7 slots, each U(component / component[]) live · objectui plugin-detail buildDefaultPageSchema (8) 1 / 1 / 4 yes — page designer PageBlockInspector writes slots.NAMEno row renders a component tree 1 root omit, 0L R · own editor (G3) 39 permission.adminScopeobj! {businessUnit, includeSubtree, manageAssignments, manageBindings, authorEnvironmentSets, assignablePermissionSets} live · plugin-security delegated-admin-gate.ts(21)0 / 0 / 1 yes — PermissionAdvancedFacets (beside tabPermissions / rowLevelSecurity) none — mirror permission.form.ts:71-721r = 8L D · json(G1){ "issue": 19332, "status": "needs_decision", "branch": "none (analysis round: no branch, no PR)", "pr": null, "session": "session_01CiCTczDo7tGhafXjf61dUJ — mode:subagent, the dispatching seat session (Claim 5859298824)", "premise_still_valid": true, "measured_at": { "framework": "4e0f72e8d24c67705ce278de24bc4166ba3f1cd6 (origin/main)", "objectui": "f8a9d0fb0596f4521076628e2bbfe27e6ce67d52 (.objectui-sha pin, anonymous shallow clone)", "hotcrm": "2f7b2326e8f52adfc9efd00b68beda5348e71a1b (local checkout, read-only)" }, "summary": "Re-derived on origin/main 4e0f72e8d with the gate's own helper block and LIT / DARK controls: 84 top-level keys have neither an offer nor a root ledger row, 44 of them view (outside the direction per #19330 A) and 40 object-rooted; excluding field.format (the #19333 flight) leaves 39, the same 39 the card named: a re-run at the census base 596090efbe7 reproduces the census helper sha and the same set, 0 added, 0 removed, 0 retired. Per key: 21 are one-row D offers mirroring a row already on a registered form (G1), 6 need a designed nested list (G2: 5 D, 1 C), 8 are R under two proposed reason classes (G3 own editor 4, G4 code-declared platform config 4), and 4 cannot be offered honestly today (G5: three boolean/false unions, and action.aria, whose `live` verdict measured 0 readers). No file was written and no PR opened; the throwaway probes were deleted and the worktree removed.", "population": { "instrument": "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts bytes 0..35929 (lines 1-684, up to the first top-level describe), sha256 9599e809114b4e386670c55bf80756a22f41ec9a2bed3f5361b2225208f59bb0, sliced verbatim into zz-census-19332.tmp.test.ts beside it (prefix proved byte-identical on disk), deleted after the run; query per registered type: offerableKeysAt(getMetadataTypeSchema(type), ROOT_PATH) minus topLevelFields(form) minus omittedAt(LEDGER, type, ROOT_PATH)", "controls": "asserted by expect() inside the probe: LIT `name` offered by 17 of 17 forms and declared by 17 of 17 schemas; DARK `zzFabricatedKey19332DarkControl` offered by 0, declared by 0, in 0 census rows", "reading_4e0f72e8d": "17 forms; top-level non-overlay zod-only 98 (view 49, object-rooted 49); excused by root ledger rows 14 (view 5; object 3, field 2, app 1, action 1, page 1, agent 1); unexplained 84 = view 44 + object-rooted 40 (object 15, field 15, action 7, app 1, page 1, permission 1); minus field.format = 39", "field_format": "excluded: scalar string, live, the #19333 flight is recording its reason (PR #19673 left it out for three value vocabularies)", "diff_vs_card": "census base 596090efbe7, same probe with that tree's helper block (sha256 f6729dae28293c94b885958664a6e9696bcb754d4863967a04504427c7418006, byte-identical to the census report's): 142 non-overlay, 94 object-rooted, 43 structured; minus object.titleFormat (census B3) and action.onSuccess / page.requires / agent.structuredOutput (census B4) = 39. Set difference against 4e0f72e8d: added 0, removed 0, retired 0. Inside the set: object.tenancy lost its `organizationField` child (502f179cc, #19054 via PR #19618); field.currencyConfig ledger evidence re-cited (03d6cb04a). #20227's view owner / hidden retirement is outside this population (view is not object-rooted). A first run at 6a6a17b62 read the same 39 with identical node shapes and verdicts.", "liveness": "37 carry a direct `live` row; object.tenancy and app.contextSelectors are drilled rows with no own status whose children are all live (the census counted both in B9). No key is dead, planned or experimental." }, "per_key_table": "| # | key | (a) node (`obj!` = strict) | (b) verdict · verified reader | (c) authors ex / hotcrm / platform | (d) Studio edits it another way | (e) form-face bound | (f) cost | recommendation |\n|---|---|---|---|---|---|---|---|---|\n| 1 | `object.access` | obj! {default: enum public/private} | live · plugin-security `permission-evaluator.ts` (4 hits) | 0 / 0 / 6 | yes — ObjectDefaultInspector posture toggle | none (both members spellable) | 1r = 8L | D · `type: composite` (G1) |\n| 2 | `object.actions` | arr of ActionSchema obj! (40+ keys) | live · runtime `app-plugin.ts` collectBundleActions (22) | 0 / 0 / 16 | yes — studio-design ObjectActionsPanel, and the `action` type editor | no row renders a 40-key action inline | 1 root omit, 0L | R · own editor (G3) |\n| 3 | `object.activityMilestones` | arr obj! {field, value, summary, type} | live · plugin-audit `audit-writers.ts` (3); objectui recordActivityFeed | 1 / 2 / 0 | no | field pickers bind nothing on an object draft ⇒ text sub-rows | 5r = 40L | D · repeater, 4 sub-rows (G2) |\n| 4 | `object.external` | obj! {remoteName, remoteSchema, writable, columnMap rec, introspectedAt, ignoreColumns} | live · runtime `external-validation-plugin.ts` (44) | 2 / 0 / 1 (the import generator) | yes — external-datasource import flow drafts it (`external-datasource-service.ts`) | `introspectedAt` is platform-written (`Set by os datasource introspect`) | 1 root omit, 0L | R · own editor (G3) |\n| 5 | `object.fieldGroups` | arr obj! 9 keys, 3 of them `[DEPRECATED → collapse]` | live · objectui plugin-form sectionGroups / FormPage; dogfood proof | 2 / 18 / 0 | yes — ObjectFormDesigner / ObjectGroupInspector | a derived face offers the 3 deprecated aliases (`object.zod.ts:1206-1210`) | 7r = 56L + 3 nested omit | D · curated repeater (G2) |\n| 6 | `object.highlightFields` | arr of string (field names) | live · objectui buildDefaultPageSchema (18); dogfood proof | 3 / 18 / 76 | yes — ObjectSettingsPanel semantic roles | `field-multi` binds nothing on an object draft (`ResourceEditPage.tsx:890`) ⇒ `string-tags`, the `nameField` `type: text` precedent | 1r = 8L | D · `string-tags` (G1) |\n| 7 | `object.indexes` | arr obj! {name, fields, unique: false / global / organization} | live · driver-sql syncTableIndexes / syncDeclaredIndexes (76) | 1 / 15 / 77 | no (ObjectFieldInspector: \"Object-level indexes[] is the real surface\") | derived `unique` offers the deprecated bare `true` (`object.zod.ts:490`) ⇒ inline options global / organization | 4r = 32L | D · repeater, 3 sub-rows (G2) |\n| 8 | `object.listViews` | rec of ListView obj! (40+ keys) | live · objectui ObjectView (25); framework expandViewContainer | 0 / 0 / 40 | yes — the `view` type editor (ViewDefaultInspector); #19330 A: per-arm forms | no row renders a ListView record | 1 root omit, 0L | R · own editor (G3) |\n| 9 | `object.publicSharing` | obj! {enabled, allowedAudiences enum[], allowedPermissions enum[], maxExpiryDays, redactFields, eligibility CEL} | live · plugin-sharing `share-link-service.ts` getPolicy (18) | 1 / 1 / 0 | no | members spellable; `eligibility` is CEL ⇒ code/expression; `redactFields` free text | 7r = 56L | D · curated composite, 6 sub-rows (G2) |\n| 10 | `object.requiredPermissions` | U(arr of string / obj! {read, create, update, delete}) | live · plugin-security `security-plugin.ts` getObjectSecurityMeta (36) | 0 / 0 / 2 (sys-scim-connection-credential, sys-sso-provider) | yes — ObjectDefaultInspector (both arms) | `string-tags` reads a stored map as `[]` and overwrites it (`widgets.tsx:1290`) ⇒ `json`, the only lossless face | 1r = 8L | D · `json` (G1) |\n| 11 | `object.searchableFields` | arr of string | live · objectql `engine.ts` $search / `search-filter.ts`; objectui ListView | 1 / 11 / 0 | no | as highlightFields; a non-stored entry is refused at parse | 1r = 8L | D · `string-tags` (G1) |\n| 12 | `object.stageField` | U(string / false) | live · objectui record-semantics / buildDefaultPageSchema; dogfood proof | 1 / 0 / 0 | yes — ObjectSettingsPanel (string / false / unset) | the `false` arm cannot be an option value; the derived face takes the string branch in create mode | 1 root omit, 0L | R · form-face gap (G5) |\n| 13 | `object.systemFields` | U(false / obj! {tenant, audit}) | live · spec `injected-system-columns.ts` (9), objectql `registry.ts` applySystemFields (11) | 0 / 0 / 1 | no | `false` arm unspellable | 1 root omit, 0L | R · code-declared platform config (G4) |\n| 14 | `object.tenancy` | obj! {enabled, tenantField} (drilled row, both children live) | live · spec isTenancyDisabled (23), driver-sql (41), plugin-security (23) | 0 / 0 / 4 | no | none | 1 root omit, 0L | R · code-declared platform config (G4) |\n| 15 | `object.userActions` | obj! {create/import/edit/delete: U(boolean / obj! {enabled, visibleWhen, disabledWhen}), exportCsv} | live · objectui ObjectGrid / ListView (30 files) | 1 / 0 / 6 | no | per-op object arm unreachable in create (boolean branch first) ⇒ curated to switches | 6r = 48L | D · composite, 5 sub-rows (G2) |\n| 16 | `field.accept` | arr of string (MIME / extension) | live · service-storage `file-reference-lifecycle.ts` assertFileConstraints (10) | 0 / 3 / 0 | no | none | 1r = 8L | D · `string-tags` + media-type gate (G1) |\n| 17 | `field.currencyConfig` | obj! {precision, currencyMode enum dynamic/fixed, defaultCurrency} | live · objectui i18n `currency.ts` (4), CurrencyField | 3 / 0 / 0 | no | none | 1r = 8L | D · composite + `currency` gate (G1) |\n| 18 | `field.dependsOn` | arr of U(string / obj! {field, param}) | live · objectui LookupField / form renderer (framework 0: client-only) | 3 / 4 / 1 | yes — ObjectFieldInspector (string arm) | union element: derived face falls to raw JSON; `string-tags` would mangle an object entry | 1r = 8L | D · `json` (G1) |\n| 19 | `field.inlineColumns` | arr obj! 20 keys (drilled row, all live) | live · objectui plugin-form deriveMasterDetail | 1 / 0 / 0 | no | 20 sub-keys; identity-only `{name}` entries hydrate from the child object | 5r = 40L + 1 nested subset | C · curated subset (G2) |\n| 20 | `field.inlineEdit` | U(boolean / enum grid, form) | live · objectui `MetadataProvider.tsx:514` → plugin-form deriveMasterDetail resolveInlineMode | 3 / 0 / 0 | no | boolean arm cannot be an option; derived face shows a switch in create ⇒ grid/form unreachable | 1 root omit, 0L | R · form-face gap (G5) |\n| 21 | `field.lookupColumns` | arr of U(string / obj! {field, label, width, type}) | live · objectui RecordPickerDialog / LookupField | 1 / 0 / 0 (showcase uses the object arm) | no | `string-tags` would mangle the only author's object entries | 1r = 8L | D · `json` (G1) |\n| 22 | `field.lookupFilters` | arr obj! {field, operator enum incl. notIn, value} | live · objectui RecordPickerDialog (17), LookupField | 1 / 0 / 0 | yes — ObjectFieldInspector | `notIn` unspellable (#19678) ⇒ no inline options; mirror `object.form.ts:242` | 1r = 8L | D · `json` mirror (G1) |\n| 23 | `field.readonlyWhen` | U(CEL string / expression envelope) | live · objectql `rule-validator.ts` stripReadonlyWhenFields (63) | 1 / 0 / 1 | yes — ObjectFieldInspector CEL editor | none — mirror `object.form.ts:337` | 1r = 8L | D · code/expression (G1) |\n| 24 | `field.relatedList` | U(boolean / literal primary) | live · objectui `deriveRelatedLists.ts` (7) | 2 / 0 / 1 | no | boolean arm unspellable; derived face shows a switch ⇒ `primary` unreachable | 1 root omit, 0L | R · form-face gap (G5) |\n| 25 | `field.relatedListColumns` | arr of string (child field names) | live · objectui `deriveRelatedLists.ts` (4) | 3 / 3 / 0 | no | a picker would bind the parent, not the child ⇒ `string-tags` | 1r = 8L | D · `string-tags` + lookup gate (G1) |\n| 26 | `field.requiredPermissions` | arr of string | live · plugin-security `security-plugin.ts` foldFieldRequiredPermissions (36) | 0 / 0 / 0 (both platform hits are object-level) | no | none — mirror `app.form.ts:102` | 1r = 8L | D · `string-tags` (G1) |\n| 27 | `field.requiredWhen` | U(CEL string / expression envelope) | live · objectql `rule-validator.ts` evaluateValidationRules (33) | 1 / 4 / 0 | yes — ObjectFieldInspector | none — mirror `object.form.ts:338` | 1r = 8L | D · code/expression (G1) |\n| 28 | `field.storage` | obj! {notNull} | live · driver-sql createColumn + `schema-drift.ts` diffManagedTable (8 + 8) | 0 / 18 files, 68 hits / 5 hits | no | none; `notNull` beside `requiredWhen` is refused at parse (loud) | 1r = 8L | D · composite (G1) |\n| 29 | `field.visibleWhen` | U(CEL string / expression envelope) | live · objectui FormPage / ExpressionProvider | 2 / 0 / 0 | yes — ObjectFieldInspector | none — mirror `object.form.ts:336` | 1r = 8L | D · code/expression (G1) |\n| 30 | `app.contextSelectors` | arr obj! {id, label, icon, optionsSource obj, allValue, persist enum} (drilled row, 6 live children) | live · objectui `ContextSelectors.tsx` | 0 / 0 / 1 (`studio.app.ts`) | no | `optionsSource` is a nested endpoint mapping | 1 root omit, 0L | R · code-declared platform config (G4) |\n| 31 | `action.aria` | obj! {ariaLabel, ariaDescribedBy, role} | ledger `live` with an uncited \"PARTIAL\" note; MEASURED 0 action-surface readers (objectui + framework; lit control `action.variant` = 14 files) | 0 / 0 / 0 | yes — ActionDefaultInspector \"More fields\" (derived) | a row would advertise a control nothing honours | 1 root omit after re-grade, 0L | R · not honoured (G5) + finding |\n| 32 | `action.bodyExtra` | rec string → unknown | live · objectui `useConsoleActionRuntime.tsx` (6), ActionRunner | 1 / 0 / 6 decl. files | yes — ActionDefaultInspector \"More fields\" | raw JSON only; refused beside `operation: update` | 1r = 8L | D · `json` + `type == api` gate (G1) |\n| 33 | `action.description` | U(string / locale map) | live · objectui ActionParamDialog / useConsoleActionRuntime | NOT MEASURED (pattern cannot separate it from other `description:` keys) | yes — \"More fields\" | none — textarea, as the description rows on `skill.form.ts:21` / `flow.form.ts:25` | 1r = 8L | D · `textarea` (G1) |\n| 34 | `action.errorMessage` | U(string / locale map) | live · objectui action-group / ActionContext toast | 0 / 0 / 0 | yes — ActionDefaultInspector (curated) | none — twin of `action.form.ts:105` successMessage | 1r = 8L | D · plain row (G1) |\n| 35 | `action.patch` | rec string → unknown | live · runtime `action-execution.ts` declarativeUpdateWrite (14) | 0 / 0 / 0 | yes — ActionDefaultInspector (curated key-value editor) | refused without `operation: update` ⇒ visibleWhen gate | 1r = 8L | D · `json` + `update` gate (G1) |\n| 36 | `action.requiredPermissions` | arr of string | live · runtime `action-execution.ts` actionPermissionError (5), `domains/actions.ts` (4) | 1 / 0 / n/s | yes — \"More fields\" | none — mirror `app.form.ts:102` | 1r = 8L | D · `string-tags` (G1) |\n| 37 | `action.resultDialog` | obj! {title, description, acknowledge, format enum incl. code-list, fields arr obj!} | live · objectui ActionRunner (14), ActionResultDialog | 0 / 0 / 4 | yes — \"More fields\" | `code-list` unspellable (#19678) | 1 root omit, 0L | R · code-declared platform config (G4) |\n| 38 | `page.slots` | obj! 7 slots, each U(component / component[]) | live · objectui plugin-detail buildDefaultPageSchema (8) | 1 / 1 / 4 | yes — page designer PageBlockInspector writes `slots.NAME` | no row renders a component tree | 1 root omit, 0L | R · own editor (G3) |\n| 39 | `permission.adminScope` | obj! {businessUnit, includeSubtree, manageAssignments, manageBindings, authorEnvironmentSets, assignablePermissionSets} | live · plugin-security `delegated-admin-gate.ts` (21) | 0 / 0 / 1 | yes — PermissionAdvancedFacets (beside tabPermissions / rowLevelSecurity) | none — mirror `permission.form.ts:71-72` | 1r = 8L | D · `json` (G1) |", "cost_model": "a form row is 2 en leaves (label, helpText) in the metadata-forms catalog, so 8 leaves across en / zh-CN / ja-JP / es-ES, 6 of them hand-authored because the three translated catalogs are ratcheted (PR #19673: 45 rows ⇒ 90 leaves ⇒ 270 translations). A nested sub-row costs the same. R costs one LEDGER row and 0 leaves. Totals if every recommendation lands: G1 21 rows = 168 leaves (126 translations); G2 34 rows = 272 leaves (204 translations) + 4 nested ledger rows; G3 + G4 + G5 = 11 root ledger rows, 0 leaves. Rows with a visibleWhen gate also move the measured pins #19673 moved (object collapsed-section populations, the translated-label control, the lint shipped-form predicate census).", "form_face_today": "objectui SchemaForm at f8a9d0fb: registered widgets ref:object, ref:component, filter-mode, object-selector, field-selector, field-ref, field-multi, action-multi, filter-builder, view-ref, icon, color-picker, color-input, condition, master-detail, string-tags, multiselect, code, secret, dynamic-config (widgets.tsx WIDGETS); passthrough hints text, textarea, number, switch, select, json; structural faces composite, repeater, record, nested-form, object-rows, raw-json, scalar. Bounds that bite: an option value is a lowercase system identifier (#19678), so hyphen / capital members and boolean arms cannot be options; a union node renders the first branch in create mode; field-ref / field-multi bind to object / objectName / data.object / interfaceConfig.source, none of which an object draft carries (ResourceEditPage.tsx:890); string-tags reads a non-array as [] (widgets.tsx:1290). Where the registry form shows: object, action and page have curated default inspectors on the main edit page, so their registry form appears in the embedded drawer and create flows; field, app (no selection) and permission render it as the editor.", "tests": "Probe runs, all through bash scripts/pm/os-verify-lock.sh -c 'pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/system/zz-census-19332.tmp.test.ts' with OS_VERIFY_LOCK_SLOT=issue-19332: at 6a6a17b62 1 file / 1 test passed, VERDICT command-exit 0 (waited 0s); at 596090efbe7 (base-helper probe zz-census-19332-base.tmp.test.ts) 1/1 passed, VERDICT command-exit 0 (waited 528s); at 4e0f72e8d 1/1 passed, VERDICT command-exit 0 (held 27s, waited 0s). The controls are expect()-asserted inside the probe, so each pass is the control reading. Per-key greps: scratchpad measure.py, git grep -E over framework packages (non-test, non-schema), objectui packages (non-test), examples/**, hotcrm src/**, and 86 platform object-definition files; LIT control `nameField` = framework 79 files, objectui 19, examples 1, hotcrm 16, platform 64; DARK `zzFabricated19332` = 0 in all five. Every ledger-cited reader file was checked with git grep -c -w KEY (counts in the table); action.aria reader search used lit control `action.variant` (14 objectui files) against 0 action-surface hits. Studio doors read at the pin: ObjectDefaultInspector (access, requiredPermissions), ObjectFieldInspector (visibleWhen / readonlyWhen / requiredWhen / lookupFilters / dependsOn), ObjectSettingsPanel (stageField, highlightFields), ObjectFormDesigner (fieldGroups), ObjectActionsPanel (actions), ActionDefaultInspector (CURATED_FIELDS at :266 plus a derived \"More fields\" form), PageBlockInspector (slots), PermissionAdvancedFacets (adminScope). No browser run. No ablation: no code was written.", "mcp_calls": "0", "api_writes": "1 — POST /repos/objectstack-ai/objectstack/issues/19332/comments (this report, through scripts/pm/post-stamped.mjs). Reads were GET only: issues 19332, 19188, 19330 and their comments, comment 5749550902, pulls 19673 and its files, /rate_limit.", "open_questions": [ { "question": "Offer each of these 21 live keys as one form row, mirroring a row that already exists on a registered form?", "group": "G1 — D, one row each: a widget the form face renders today, and a registered form already spells the same shape (21 keys)", "keys": [ "object.access", "object.highlightFields", "object.requiredPermissions", "object.searchableFields", "field.accept", "field.currencyConfig", "field.dependsOn", "field.lookupColumns", "field.lookupFilters", "field.readonlyWhen", "field.relatedListColumns", "field.requiredPermissions", "field.requiredWhen", "field.storage", "field.visibleWhen", "action.bodyExtra", "action.description", "action.errorMessage", "action.patch", "action.requiredPermissions", "permission.adminScope" ], "options": [ "A. D: one row each, mirroring the precedent row (code/expression, json, string-tags, composite, textarea, plain); 21 rows ⇒ 42 en leaves ⇒ 168 catalog leaves, 126 of them hand-authored zh-CN / ja-JP / es-ES", "B. R for the 12 keys the Studio already edits in a dedicated panel (\"the registry form is the fallback door\"), D for the other 9", "C. Defer: no rows until #19188 wires the top-level direction and each key reds" ], "recommendation": "A, because every one is live with a named reader, a same-shape row already exists on a registered form, and the cost is the ratcheted translations only; B would make a reason class that #19673 did not apply to its scalar siblings (nameField is edited in ObjectSettingsPanel and still got a row); C re-creates the refused red-lines-without-offers shape.", "four_axis": { "实际业务需求": "21 个键全部 live,且每个都有已核实的运行时读者。作者实测:hotcrm 在 18 个文件里写了 68 处 storage.notNull、18 个文件写 highlightFields、11 个写 searchableFields、4 个写 requiredWhen;showcase 写了 lookupColumns / lookupFilters / relatedListColumns / currencyConfig 等。action.patch、action.errorMessage、action.aria 之外的少数键零应用作者,但 Studio 的动作编辑器已有专门控件,说明产品上确实在用。", "项目长远合理性": "每一行都照抄已登记表单上已有的同形行(object.form.ts:242/336-338、app.form.ts:102、permission.form.ts:71-72、action.form.ts:105、page.form.ts:196),不引入新控件、不改 schema。与刚落地的标量兄弟 #19673 同口径。长期代价是 i18n 棘轮:21 行 = 42 个 en 叶子 + 126 条手写译文。", "防 AI 犯错": "联合型(object.requiredPermissions、dependsOn、lookupColumns)一律用 json,不用 string-tags:后者会把已存的 map 读成空数组再覆盖(widgets.tsx:1290),静默丢数据。字段选择器在对象草稿上今天绑不到字段(ResourceEditPage.tsx:890),所以字段名列表用自由文本,拼错由解析/校验响亮拒绝;helpText 写运行时真实行为和缺省值,而不是复述键名。", "创业阶段不扩散": "不新增门禁,不新增控件种类,不扩 schema;只是给已声明且已兑现的键开一扇门。零作者的 action.patch / errorMessage 行代价各 8 条,且 Studio 已在编辑它们,不算新能力。" }, "governing_text": [ "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts:26-35 (zod-only is ledgerable only when deliberate)", "AGENTS.md Prime Directive #10 (declared = enforced; keep the claim as narrow as the enforcement)", "packages/spec/src/ui/view.zod.ts:3123 options describe (#19678 ruling 乙: an unspellable enum derives, meanings in helpText)", "precedent rows: object.form.ts:242, :336-338; app.form.ts:102; permission.form.ts:71-72; action.form.ts:105; page.form.ts:196", "PR #19673 cost measurement: the metadata-forms catalogs are ratcheted, so every new en leaf is authored in zh-CN / ja-JP / es-ES in the same PR" ] }, { "question": "Design a curated nested list for each of these six, or offer a derived one-row face, or record a reason?", "group": "G2 — D / C, a designed nested list: hand-written sub-rows plus nested ledger rows, because a derived face would offer a deprecated alias, an unreachable union arm or a 20-key grid (6 keys)", "keys": [ "object.fieldGroups", "object.indexes", "object.activityMilestones", "object.publicSharing", "object.userActions", "field.inlineColumns" ], "options": [ "A. D / C: hand-written sub-rows (fieldGroups 6, indexes 3, activityMilestones 4, publicSharing 6, userActions 5, inlineColumns 4 as a curated subset); 34 rows ⇒ 68 en leaves ⇒ 272 catalog leaves; ledger: 3 nested omit rows under fieldGroups and 1 nested subset row for inlineColumns", "B. D with a derived one-row face (type composite / no widget): 6 rows, but it shows authors the 3 deprecated fieldGroups aliases and the deprecated bare `unique: true`", "C. R: record a reason and wait for demand" ], "recommendation": "A, dispatched in two flights: fieldGroups and indexes first (hotcrm authors them in 18 and 15 files), then activityMilestones, publicSharing, userActions and the inlineColumns subset; B puts deprecated spellings in front of authors, C withholds keys a real deployment writes.", "four_axis": { "实际业务需求": "fieldGroups(hotcrm 18 个文件)和 indexes(hotcrm 15、平台 77)有强拉动;activityMilestones(showcase 1 + hotcrm 2)、publicSharing(showcase 1 + hotcrm 1,知识库文章公开分享)、userActions(showcase 1 + 平台 6)是轻拉动;inlineColumns 只有 showcase 一处。Studio 只为 fieldGroups 提供了设计器,其余五个今天只能写源码。", "项目长远合理性": "手写子行是 #14327 之后对账门已覆盖的嵌套坐标,omit/subset 词汇现成(object.fields 的 subset 即先例);派生一行虽便宜,但把 [DEPRECATED] 键和已废弃的 unique:true 摆给作者,违背 contract-first。分两批派发可以让高拉动的两键先落地。", "防 AI 犯错": "精选子行 + 嵌套 omit 让已废弃别名在结构上不可达;indexes.unique 只给 global / organization 两个可拼写选项,已废弃的裸 true 不出现;publicSharing.eligibility 给 CEL 表达式控件,失败即拒(fail-closed)。", "创业阶段不扩散": "不新增门禁、不改 schema;代价集中在 34 行的四语文案(204 条手写译文)。inlineColumns 用 subset 而不是 20 列全量,是按拉动收紧。" }, "governing_text": [ "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts:172-176 (omit / subset vocabulary) and :196-201 (the object.fields subset precedent)", "packages/spec/src/data/object.zod.ts:1206-1210 (`[DEPRECATED → collapse]`) and :490 (bare `unique: true` deprecated, rejected at protocol 18)", "packages/spec/src/ui/page.form.ts:164 (the interfaceConfig.userActions composite precedent)", "AGENTS.md Prime Directive #12 (contract-first: never teach an off-spec spelling)" ] }, { "question": "Record a new root-ledger reason class for keys whose authoring door is another metadata type or a dedicated designer?", "group": "G3 — R, new reason class: authored through its own metadata type or a dedicated Studio designer, in a shape no form row renders (4 keys)", "keys": [ "object.actions", "object.listViews", "page.slots", "object.external" ], "options": [ "A. R: 4 root omit rows under a new reason class (\"authored through its own editor\"), 0 i18n leaves", "B. C: a curated inline subset (actions: name / label / type; listViews: name / label / type / columns; external: remoteName / remoteSchema / writable)", "C. D: the full shape inline" ], "recommendation": "A, because each shape already has a richer door (the action type and ObjectActionsPanel, the view type under #19330 A, the page designer block inspector, the external-datasource import flow), and a second, poorer copy inside the object or page form would drift from it; external is the weakest case (showcase hand-writes `remoteName` twice), so B is the fallback for it alone.", "four_axis": { "实际业务需求": "object.actions(平台 16 个系统对象)、listViews(平台 40)由平台代码写,应用作者走 action / view 类型;page.slots 在 hotcrm 与 showcase 各 1 处,Studio 用页面设计器的区块检视器写它;external 由外部数据源导入流程生成(showcase 手写了 2 处 remoteName)。需求真实,但门已经在别处开着。", "项目长远合理性": "#19330 已裁 view 按臂建表单;在对象表单里再塞一个 ListView / Action 编辑器就是第二套编辑器,违背「一条路由一个所有者」。理由类需要裁决,因为 #19333 的根行理由都是从键自身的 describe 或 liveness 读出来的,这一类不是。", "防 AI 犯错": "不在表单里给一个残缺的内联副本,AI 就只会走完整的专属门;omit 行点名去哪里编写,比一个只能写一半的控件更不容易写错。", "创业阶段不扩散": "零 i18n 成本,零新控件;只多 4 条账本行和一个理由类。" }, "governing_text": [ "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts:196-201 (subset why: \"the full per-field editor is field.form.ts\")", "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts:230-248 (root rows read their reason off describe() or the liveness verdict; a new class is a ruling)", "#19330 ruling A (view reconciled per arm, a list view and a page layout are two editors)", "AGENTS.md Route & surface ownership rule 1 (one owner per surface)" ] }, { "question": "Record these four as code-declared platform configuration, the lifecycle onlyWhen precedent at the root?", "group": "G4 — R, new reason class: code-declared platform configuration, every author is a platform object declared in code (4 keys)", "keys": [ "object.tenancy", "object.systemFields", "app.contextSelectors", "action.resultDialog" ], "options": [ "A. R: 4 root omit rows naming the platform writers, 0 i18n leaves", "B. D: design controls (tenancy composite, systemFields false-or-map, contextSelectors repeater, resultDialog composite)" ], "recommendation": "A, because examples and hotcrm author none of the four, and two of them are isolation switches (tenancy.enabled false turns org scoping off; systemFields false stops injecting organization_id and the audit columns) that a Studio control would make one click away.", "four_axis": { "实际业务需求": "examples 与 hotcrm 对这四个键的作者数都是 0;写者只有平台代码(tenancy 4 个系统对象、systemFields 1、contextSelectors 只在 studio.app.ts、resultDialog 在 sys-oauth-application / sys-sso-provider 等 4 个)。", "项目长远合理性": "与 lifecycle.retention / ttl.onlyWhen 的 omit 行同一理由(写者全是代码声明的系统对象,给控件是表单面新增而非对账);有产品卡拉动时删行再设计。", "防 AI 犯错": "tenancy.enabled:false 关掉组织隔离,systemFields:false 停止注入 organization_id 与审计列 —— 放进表单就是一键关隔离;记理由让这两个开关只能出现在经评审的代码里。", "创业阶段不扩散": "零拉动的声明面按 implementation-first 处置:不设计控件,只记 4 条账本行。" }, "governing_text": [ "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts:216-229 (lifecycle.retention / ttl onlyWhen: every writer is a code-declared platform object; offering it is a form-face addition, not a reconciliation)", "four-axis framework: 无拉动的声明面按 implementation-first 处置" ] }, { "question": "For unions with a boolean / false arm, and for action.aria, record a reason now, build a union-aware control in objectui, or offer the first arm only?", "group": "G5 — the form face cannot honestly offer it today: a boolean or false arm no option can spell, or a control nothing honours (4 keys)", "keys": [ "object.stageField", "field.inlineEdit", "field.relatedList", "action.aria" ], "options": [ "A. R now: 3 root omit rows under a new class (\"no registered widget spells this union\"); action.aria waits for its liveness re-grade (finding 1), then takes #19333's not-enforced bucket or the retirement playbook", "B. Design a union-aware select in objectui (a cross-repo form-face addition), then offer all three as D", "C. Offer the first arm only: stageField as text, inlineEdit and relatedList as a switch; the other arm silently unreachable" ], "recommendation": "A, because an option value is a lowercase system identifier so `false` / `true` cannot be options, the derived face shows only the first branch in create mode, and C would ship a control that silently cannot reach `primary`, `grid`/`form` or `false`; B belongs to the first product card that needs one of these keys, and stageField already has its Studio control in ObjectSettingsPanel.", "four_axis": { "实际业务需求": "relatedList(showcase 2 个文件,用了 primary)、inlineEdit(showcase 3)、stageField(showcase 1)有轻拉动,hotcrm 为 0;action.aria 作者 0,且实测动作渲染面 0 个读者(对照 action.variant 命中 14 个文件)。", "项目长远合理性": "联合感知的选择控件是 objectui 的表单面新增,应随第一张需要它的产品卡落地,而不是为对账门提前造;aria 先修正 liveness 判定,再按结果走 #19333 或退役剧本。", "防 AI 犯错": "只给第一臂的控件会让作者和 AI 以为另一臂不存在,是静默的部分能力;声明即强制 —— 不能给一个运行时不读的 aria 控件。", "创业阶段不扩散": "不为 3 个轻拉动的键新建控件种类;不新增门禁。" }, "governing_text": [ "packages/spec/src/ui/view.zod.ts:3123 and FormSelectOptionSchema (an option value is a system identifier; #19678 ruling 乙)", "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts:189-195 (enable.apiMethods: needs its own control) and :216-229 (a form-face addition rather than a reconciliation)", "AGENTS.md Prime Directive #10 (never advertise a capability the runtime does not deliver)", "packages/spec/liveness/README.md status vocabulary (`live` cites its consumer)" ] } ], "out_of_scope_findings": [ "class: c · reach: named producer — the Studio action editor's \"More fields\" form, derived from the live server schema, renders an `aria` control (not in CURATED_FIELDS or RETIRED_FIELDS, ActionDefaultInspector.tsx:21, :266; read at f8a9d0fb, not browser-run) · evidence: packages/spec/liveness/action.json grades action.aria `live` with the uncited note \"PARTIAL — honored by a few objectui renderers, not the core action buttons/menus\"; git grep for an action's aria (action / actionDef / def / spec / btn .aria) finds 0 readers in objectui action surfaces and 0 in framework, lit control action.variant = 14 objectui files; the only `schema.aria` readers are record:* page components · dedupe words: `action aria no reader` · `ActionSchema aria liveness partial` · `action aria declared unenforced`", "carrier: none (承接者:无) · noted, not filed — objectui ObjectFieldInspector readDependsOn (ObjectFieldInspector.tsx:1475-1481) maps a `{field, param}` dependsOn entry to its bare `field`, and add / remove write the string list back (:1536-1541), so any edit drops every `param`; no measured author uses the object arm (0 in examples and hotcrm by one-line grep), so no victim is measured", "carrier: the G1 / G2 flights · noted, not filed — field-ref / field-multi resolve their catalog from object / objectName / data.object / interfaceConfig.source (ResourceEditPage.tsx:890); an object draft carries none, so a field picker on object.form.ts would render an empty catalog. No shipped row uses one there today (#19673 used type text for nameField), so it is a design bound for this card, not a defect" ], "deviations": [ "The dispatch said to slice \"the bytes before its first describe(\". The literal first occurrence is at line 232, inside a ledger comment (`describe()`), and slicing there cuts the file mid-array; I sliced at the first top-level describe( (line 685, byte 35929), the convention the census used — its base-commit slice reproduces the census sha exactly.", "To name the difference from the card's 39 (the census never enumerated them), I checked out the census base 596090efbe7 in the same detached worktree, ran a second throwaway probe with that tree's helper block, deleted it and returned the worktree to main before anything else.", "The worktree was moved from 6a6a17b62 to 4e0f72e8d on the coordinator's resume order; the population was re-derived there and is identical. node_modules removed first, then git worktree remove without --force (exit 0).", "(c) author counts are git grep pattern counts; action.description authors are NOT MEASURED (the pattern cannot separate it), and the platform column for action keys counts files, not verified declarations.", "The agent contract says the final message is the JSON alone; the dispatch says to return the comment. The final message is this JSON, which carries the per-key table in per_key_table; the comment renders the same table above the same JSON." ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsDecision box: five disposition groups for the 39 structured zod-only keys (2026-09-27T21:34Z)
domain:specseat 4 (session_01CiCTczDo7tGhafXjf61dUJ). This comment routes the analysis round of claim5859298824to the decision box. The per-key table (39 rows: node shape, verdict and reader, authors, the Studio's other door, form-face bound, cost) is the dev report5859943900above, and it is the evidence for everything below.Background.
#19188reports that the metadata-form ↔ zod reconciliation gate never wired its top-levelzodOnlydirection.- This card holds the 39 live, structured keys that have neither a form row nor a recorded reason.
- Until each has one or the other, the direction cannot be wired, and
#19333item 2 waits on it. - The population was re-derived at
origin/main4e0f72e8d, with the gate's own helper block and lit/dark controls. It is exactly the card's 39: 0 added, 0 removed, 0 retired. #19333's dev derived the same 39 independently, as the residue afterfield.format, and that PR (test(spec): record why field.format is never offered by a metadata form #20322) recordsfield.format.
Governing text.
packages/spec/src/system/metadata-form-zod-reconciliation.test.ts: :26–35 (zod-only is ledgerable only when deliberate), :172–201 (the omit / subset vocabulary and theobject.fieldssubset precedent), :216–229 (thelifecycle.*.onlyWhen"code-declared platform writer" precedent), :230–248 (root rows read their reason offdescribe()or the liveness verdict).- AGENTS.md Prime Directive chore: version packages #10 (declared = enforced) and Add comprehensive test suite for Zod schema validation #12 (contract-first).
#19330ruling A (view is reconciled per arm).#19678ruling 乙 (an option value is a system identifier).
Protocol statement. No option changes the protocol; every option is form-face or ledger text. The seat spot-checked the spec-side citations at
4e0f72e8d:- the three
[DEPRECATED → collapse]aliases onfieldGroups(object.zod.ts:1206–1210); - the
lifecycle.*.onlyWhenprecedent rows (:216–229); action.aria's ledger row,livewith a note of 「PARTIAL」 (liveness/action.json:215–217).
The objectui citations (
widgets.tsx:1290,ResourceEditPage.tsx:890, the inspectors) are the dev's reading at the.objectui-shapin; the seat did not re-measure them.Premises, with re-check commands.
- The population: run the gate's helper block as a throwaway probe (see the report's
population.instrument). ⇒ 84 unexplained = view 44 + object-rooted 40 (39 +field.format). - The form face's bounds:
git -C OBJECTUI_CLONE grep -n "string-tags" -- packages/*/src/**/widgets.tsx⇒ it reads a non-array as[]at the pin.
Prior rulings read: metadata form,zodonly,reconciliation,form row → 6 hits; ADR-0094 D2, ADR-0094 D4, ADR-0110 D5, ADR-0129 D4; thread: none; repo: objectstack-ai/objectstack. The seat read each hit. All of them are other senses of "reconciliation": permission-set projection, action admission, object naming. None rules on metadata-form offers, so this card is a decision, not execution.
Item 1 — G1: offer 21 keys as one form row each, mirroring a row a registered form already has?
The problem in one sentence. An author who opens the Studio form for an object, field, action or permission cannot see or set 21 live settings: who may see the object, which fields search reads, when a field is required, its storage constraint, an action's error text. The platform honours all of them once they are written in code.
Keys:
object.access,object.highlightFields,object.requiredPermissions,object.searchableFields,field.accept,field.currencyConfig,field.dependsOn,field.lookupColumns,field.lookupFilters,field.readonlyWhen,field.relatedListColumns,field.requiredPermissions,field.requiredWhen,field.storage,field.visibleWhen,action.bodyExtra,action.description,action.errorMessage,action.patch,action.requiredPermissions,permission.adminScope.option what it does what customers notice A One row per key, mirroring an existing same-shape row (code/expression, json, string-tags, composite, textarea, plain). 21 rows ⇒ 168 catalogue leaves, 126 of them hand-authored translations. The Studio form can set all 21; nothing else moves. B Rows only for the 9 keys the Studio has no dedicated panel for; the other 12 get a recorded reason. The registry form stays thinner; those 12 are editable only in their panels. C No rows until #19188 wires the direction and each key goes red. Nothing changes, and the gate stays unwired. In business terms. A is "fill the gaps in an existing form, same controls". B is "point people at the specialised screen". C is "wait".
Four axes (business view).
- 实际业务需求:21 个键全部 live,且都有已核实的运行时读者。hotcrm 在 18 个文件里写了 storage.notNull,写 highlightFields 的也是 18 个文件,写 searchableFields 的 11 个;showcase 写了 lookupColumns / lookupFilters / currencyConfig 等。
- 项目长远合理性:每行照抄已登记表单上的同形行,不引入新控件、不改 schema,与刚落地的标量兄弟 feat(spec): give the 45 declared-but-unoffered scalar metadata keys a form row each #19673 同口径。长期代价是 i18n 棘轮。
- 防 AI 犯错:联合型一律用 json 而不是 string-tags。string-tags 会把已存的 map 读成空数组再覆盖,等于静默丢数据,只能用 json 防住。字段名列表用自由文本,拼错时由解析响亮拒绝,出错的人当场看得到。
- 创业阶段不扩散:不新增门禁、控件种类或 schema,只是给已声明且已兑现的键开门。
os-decision-facets
- ① 项目长远合理性:照抄既有行,不增特例,不增契约。
- ② 实际业务拉动:hotcrm、showcase 实测作者;全部 live 且有读者。
- ③ 防 AI 犯错:联合型用 json,防止静默覆盖;非法值在解析时响亮拒绝。
- ④ 创业阶段不扩散:零新门禁、零新控件,代价只是四语文案。
Recommendation: A. Fallback: B. Confidence gap:
action.description's authors are NOT MEASURED, and the objectui widget bounds are the dev's reading. 只看①选 A;②③④ 是否翻转:否。
After the ruling: two flights (object + permission; field + action), each with its four-locale catalogue rows in the same PR.
Item 2 — G2: design a curated nested list for six keys?
The problem in one sentence. Section layout, indexes, activity milestones, public sharing, per-operation user actions and inline child columns can only be written in code today. A derived one-row control would show authors deprecated spellings (
fieldGroups' three aliases, the bareunique: true) or an unreachable half of a union.Keys:
object.fieldGroups,object.indexes,object.activityMilestones,object.publicSharing,object.userActions,field.inlineColumns.option what it does what customers notice A Hand-written sub-rows (34 rows ⇒ 272 leaves, 204 translations), plus 3 nested omit rows under fieldGroupsand 1 nested subset row forinlineColumns. Two flights:fieldGroups+indexesfirst.Clean editors that offer only current spellings. B A derived one-row face per key (6 rows). Cheap, but deprecated aliases appear in the Studio. C A recorded reason; wait for demand. Code-only authoring continues. In business terms. A is "build the six small editors properly". B is "expose the raw shape, warts included". C is "not now".
Four axes.
- 实际业务需求:fieldGroups 在 hotcrm 18 个文件出现,indexes 在 hotcrm 15 个、平台 77 个,都是强拉动;其余四个轻拉动;inlineColumns 只有 showcase 一处。
- 项目长远合理性:手写子行加嵌套 omit/subset 是对账门现成的词汇。派生一行会把 [DEPRECATED] 键摆给作者,违背 contract-first。
- 防 AI 犯错:精选子行让已废弃别名在结构上不可达;indexes.unique 只给 global / organization 两个选项。
- 创业阶段不扩散:不改 schema,不加门禁;inlineColumns 只做 subset,按拉动收紧。
os-decision-facets
- ① 项目长远合理性:只提供现行拼写,废弃别名不可达,契约面收窄。
- ② 实际业务拉动:fieldGroups、indexes 强拉动,其余轻拉动。
- ③ 防 AI 犯错:闭合子行优于派生自由结构。
- ④ 创业阶段不扩散:分两批,低拉动项可后置。
Recommendation: A, in two flights. Fallback: A for
fieldGroups+indexesonly, and C for the other four. Confidence gap: no browser run of the objectui repeater face. 只看①选 A;②③④ 是否翻转:否(④ 只调分期)。
Item 3 — G3: record a new reason class, "authored through its own editor", for four keys?
The problem in one sentence. An object's actions and list views, a page's slots, and an object's external-datasource mapping each already have a richer editor elsewhere: the action type, the view type, the page designer, the import flow. A second, poorer copy inside the object or page form would drift from it.
Keys:
object.actions,object.listViews,page.slots,object.external.option what it does what customers notice A 4 root omit rows under a new reason class, with 0 translations. No change: authors keep using the dedicated editors. B A curated inline subset (e.g. actions: name / label / type). A second, partial editor beside the real one. C The full shape inline. A 40-key editor inside a form row. Four axes.
- 实际业务需求:actions、listViews 由平台代码大量写,应用作者走 action / view 类型;slots 用页面设计器写;external 由导入流程生成,showcase 手写过 2 处。
- 项目长远合理性:[Decision] is
viewreconciled per metadata type or per union arm? 36 keys of the #19188 census cannot be filed until this is settled #19330 已裁 view 按臂建表单;在对象表单里再放一个编辑器,就违背「一条路由一个所有者」。 - 防 AI 犯错:omit 行点名该去哪里写,比一个只能写一半的控件更不容易出错。
- 创业阶段不扩散:零 i18n 成本,零新控件。
os-decision-facets
- ① 项目长远合理性:一个面一个编辑器,不造第二套。
- ② 实际业务拉动:门已在别处开着,拉动由专属编辑器承接。
- ③ 防 AI 犯错:不给残缺副本。
- ④ 创业阶段不扩散:只多 4 条账本行和一个理由类。
Recommendation: A. Fallback: B for
object.externalalone, since it is the weakest case. Confidence gap: the import flow's coverage of everyexternalsub-key was not measured. 只看①选 A;②③④ 是否翻转:否。
Item 4 — G4: record four keys as "code-declared platform configuration"?
The problem in one sentence. Four settings are written only by the platform's own code-declared objects, and no example or HotCRM writes them. Two of them are isolation switches:
tenancy.enabled: falseturns organisation scoping off, andsystemFields: falsestops injectingorganization_idand the audit columns. A form row would put those switches one click away.Keys:
object.tenancy,object.systemFields,app.contextSelectors,action.resultDialog.option what it does what customers notice A 4 root omit rows naming the platform writers (the lifecycle.*.onlyWhenprecedent).Nothing; the switches stay code-only. B Design controls for them. Isolation switches become editable in the Studio. Four axes.
- 实际业务需求:examples 与 hotcrm 对这四个键的作者数都是 0,写者只有平台代码。
- 项目长远合理性:与 lifecycle onlyWhen 的 omit 行同一理由;有产品卡拉动时,删行再设计。
- 防 AI 犯错:放进表单,就等于一键关掉组织隔离、停止注入审计列;记理由让这两个开关只出现在经评审的代码里。
- 创业阶段不扩散:零拉动,按 implementation-first 处置。
os-decision-facets
- ① 项目长远合理性:沿用既有先例,不增特例。
- ② 实际业务拉动:零应用作者。
- ③ 防 AI 犯错:隔离开关不进表单。
- ④ 创业阶段不扩散:只加 4 条账本行。
Recommendation: A. Fallback: B for
action.resultDialogalone, if a product card asks. Confidence gap: none material. 只看①选 A;②③④ 是否翻转:否。
Item 5 — G5: for three unions with a boolean /
falsearm and foraction.aria, record a reason now?The problem in one sentence. The form face cannot honestly offer these today:
- An option value must be a lowercase identifier, so
true/falsecannot be options, and a union shows only its first branch when a record is created.relatedList: 'primary',inlineEdit: 'grid' | 'form'andstageField: falsewould silently be unreachable. action.ariahas no measured reader on any action surface (lit control:action.varianthits 14 files).
Keys:
object.stageField,field.inlineEdit,field.relatedList,action.aria.option what it does what customers notice A Record a reason now for the three unions, under a new class: "no registered widget spells this union". action.ariawaits for its liveness re-grade (#20323), then takes #19333's not-enforced bucket or the retirement playbook.No change. B Build a union-aware select in objectui first, then offer all three. A cross-repo form-face addition before any product card asks for it. C Offer the first arm only. Controls that silently cannot reach primary,grid/formorfalse.Four axes.
- 实际业务需求:三个联合键只有 showcase 轻拉动,hotcrm 为 0;action.aria 作者为 0,实测读者也为 0。
- 项目长远合理性:联合感知控件应随第一张需要它的产品卡落地,不为对账门提前造。
- 防 AI 犯错:只给第一臂的控件会让作者以为另一臂不存在,属于静默的部分能力;声明即强制,不给一个运行时不读的 aria 控件。
- 创业阶段不扩散:不为轻拉动的键新造控件种类。
os-decision-facets
- ① 项目长远合理性:不造半能力控件。
- ② 实际业务拉动:轻拉动或零拉动。
- ③ 防 AI 犯错:不做静默不可达的臂。
- ④ 创业阶段不扩散:延后到有产品卡拉动时。
Recommendation: A. Fallback: B, if a product card for one of these keys lands first. Confidence gap:
stageFieldalready has its own Studio control (ObjectSettingsPanel); it was not browser-run. 只看①选 A;②③④ 是否翻转:否。
What happens after the ruling.
- Each letter becomes flights on this card: Items 1–2 as form-row flights, and Items 3–5 as one ledger flight.
- Once the ledger rows land, #19188 split: 145 top-level zod-only keys need a RECORDED REASON, never a form row — and none can be recorded until the ledger learns a root path #19333 item 2 (wiring the top-level
zodOnlydirection, [finding] metadata-form ↔ zod 对账门的 top-levelzodOnly方向**根本没接线**(只有嵌套列表有),这就是两个已声明键在全门禁绿的情况下缺席表单的原因 —— 本树实测 276 个 top-level zod-only 键 #19188) becomes dispatchable. action.aria's liveness verdict is filed as its own finding card, [finding]action.ariais gradedlivein the liveness ledger, but no action surface reads it — and the Studio action editor still renders anariacontrol #20323.
State. This card moves from
pm:dispatchedtoneeds-user-decisionin one write, and this seat's assignee comes off. The analysis round is complete, and ⛔ nothing is in flight on the card.objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsRuling: batch #229 item 3 · letter A (all five groups) · maintainer 「同意」 2026-09-28T00:53Z
Director seat, summon #30 续 2,
session_01AsCNgFBs8HCjwhyHQsFbx3. Batch #229 was presented in the seat chat with the full analysis of each item (thedomain:specseat 4 box 5860033210 and the dev's per-key table 5859943900 re-read, four axes not flipped); the maintainer answered 「同意」 (verbatim, recorded here per the charter).Ruled: A on every group. The 39 live structured
zodOnlykeys are dispositioned as follows, so the metadata-form ↔ zod reconciliation gate can wire its top-levelzodOnlydirection (#19333 item 2):- G1 (21 keys:
object.access,object.highlightFields,object.requiredPermissions,object.searchableFields,field.accept,field.currencyConfig,field.dependsOn,field.lookupColumns,field.lookupFilters,field.readonlyWhen,field.relatedListColumns,field.requiredPermissions,field.requiredWhen,field.storage,field.visibleWhen,action.bodyExtra,action.description,action.errorMessage,action.patch,action.requiredPermissions,permission.adminScope) — one form row each, mirroring a row a registered form already has (code/expression, json, string-tags, composite, textarea, plain), with the four-locale catalogue rows in the same PR. Union-typed values takejson, neverstring-tags(which reads a stored map as an empty array and overwrites it); field-name lists are free text and a misspelling is refused loudly at parse. - G2 (
object.fieldGroups,object.indexes,object.activityMilestones,object.publicSharing,object.userActions,field.inlineColumns) — hand-written curated sub-rows, plus the three nestedomitrows underfieldGroups(the[DEPRECATED → collapse]aliases,object.zod.ts:1206-1210) and one nestedsubsetrow forinlineColumns;indexes.uniqueoffersglobal/organizationonly. Two flights:fieldGroups+indexesfirst, the other four after. - G3 (
object.actions,object.listViews,page.slots,object.external) — four rootomitrows under a new reason class, "authored through its own editor", naming the editor (theactiontype, theviewtype per [Decision] isviewreconciled per metadata type or per union arm? 36 keys of the #19188 census cannot be filed until this is settled #19330 A, the page designer, the import flow). No inline copy. - G4 (
object.tenancy,object.systemFields,app.contextSelectors,action.resultDialog) — four rootomitrows as "code-declared platform configuration", thelifecycle.*.onlyWhenprecedent; the two isolation switches stay out of the form. - G5 (
object.stageField,field.inlineEdit,field.relatedList;action.aria) — a recorded reason now: the three unions under a new class "no registered widget spells this union" (an option value must be a lowercase identifier and a union shows only its first arm on create);action.ariawaits for its liveness re-grade ([finding]action.ariais gradedlivein the liveness ledger, but no action surface reads it — and the Studio action editor still renders anariacontrol #20323), then #19188 split: 145 top-level zod-only keys need a RECORDED REASON, never a form row — and none can be recorded until the ledger learns a root path #19333's not-enforced bucket or the retirement playbook.
Readings: the population (39 = 84 unexplained − view 44 −
field.format) was re-derived with the gate's own helper block and lit/dark controls at4e0f72e8d, 0 added, 0 removed; the spec-side citations spot-checked by the seat and by this seat atab820016b3(object.zod.ts:1206-1210,liveness/action.json:215-217ariaPARTIAL). The objectui widget bounds (string-tagsreads a non-array as[];field-multibinds nothing on an object draft) are the dev's reading at the.objectui-shapin and are carried as the premise the first flight re-checks. Prior-ruling sweep: ADR-0094 D2/D4, ADR-0110 D5, ADR-0129 D4 are other senses of 「reconciliation」; #19330 A and #19678 乙 are honoured by G3 and G5. No option changes the protocol.Mainstream reading the ruling rests on: Salesforce's object and field setup pages give every declared property a control of its own kind or keep it on its own page (sharing settings are not inside the field editor); ServiceNow's dictionary entries take typed controls per attribute rather than a generic JSON box. One surface, one editor; a declared setting either has a real control or a recorded reason, never a half-capable one.
Execution parameters: G1 and G2 land as form-row flights (G1: object + permission, then field + action; G2:
fieldGroups+indexes, then the rest), each with its catalogue rows; G3–G5 land as one ledger flight; once the ledger rows are in, #19333 item 2 wires the direction. Every flight isClause-②: no(form face and ledger text). Confidence gaps carried:action.description's authors NOT MEASURED; no browser run of the objectui repeater face;stageField's existingObjectSettingsPanelcontrol not browser-run.State:
needs-user-decision→pm:queuein this act (domain:spec· p2 kept); body first line set to this ruling.
Generated by Claude Code
- G1 (21 keys:
50 remaining items
objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsos-dev-report
{ "issue": 19332, "status": "done", "branch": "claude/issue-19332-g2b-remaining-g2-rows", "pr": "https://github.com/objectstack-ai/objectstack/pull/20485", "session": "session_01ARcDurZ5j34RdqsGgc4jgH (subagent run; the parent PM session's id, as CLAUDE_CODE_REMOTE_SESSION_ID resolves it)", "premise_still_valid": true, "summary": "Patch round done. Flight G2b is complete on draft PR #20485 at final head 16037890, and nothing is blocked. The seat answered A and amended claim 5873857698 in place (its 'Amended 2026-09-28T17:27Z' line, updated_at 17:28:23Z); the newest Claim: is still that comment, naming this branch. The worktree was re-added from the pushed branch at 66b73be5, and one new commit was added, with no rebase, amend or force-push. 16037890 moves the shipped-form predicate census in packages/lint/src/validate-predicate-path-refs.test.ts: predicates 81 to 82 and literal comparisons 56 to 57, each with a history comment in the G1b (ec292cf5) form. Nothing else under packages/lint/** changed. The move is measured, not inferred: the corpus, keyed FORM::FIELD::SOURCE, was enumerated at the merge base e956924e (81 / 56, in a scratch worktree removed after) and on the branch (82 / 57). The one difference is field::inlineColumns::data.type == 'master_detail', added, with none removed. The file reads 54 of 54. origin/main moved to 4b2d9041, but not onto any file of this diff, so there was no merge; seat 2's #20475 is still unlanded. #20479 did land (4b2d9041). Its rule still does not judge activityMilestones[].field or inlineColumns[].name, so this flight's help texts stay true. It does make G2a's indexes.fields text ('Nothing checks them when you save or publish') half stale, which is recorded as a finding for the seat. The PR body was updated through the relay's issue_patch and read back identical with its footer: it no longer names a gap.", "tests": "Patch round, through scripts/pm/os-verify-lock.sh, slot issue-19332-g2b-p2, at 16037890: pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 src/validate-predicate-path-refs.test.ts = 'Test Files 1 passed (1) / Tests 54 passed (54)', VERDICT command-exit 0, after the lint dependency closure build (pnpm --workspace-concurrency=2 --filter '@objectstack/lint^...' run build, VERDICT 0). There was no merge, so the other suites were not re-run. The patch commit changes one lint test file that no other suite reads. First-round readings at 66b73be5 still stand: spec test 569 files / 16698 passed | 1 todo; spec test:repo 38 files / 690 passed; titles guard + reconciliation 2 files / 86 passed; platform-objects 55 files / 911 passed; spec and platform-objects typecheck exit 0; check:generated 'All 15 generated artifacts are up to date'; check:i18n OK; metadata-protocol meta-types 4 files / 58 passed; cli i18n 2 files / 27 passed. check:i18n also read OK again inside the patch round's 87-gate run at 16037890 ('check-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys)'). Corpus probe (tsx over METADATA_FORM_REGISTRY, never committed): base 'CORPUS predicates=81 comparisons=56', head 'CORPUS predicates=82 comparisons=57', diff exactly one added line field::inlineColumns::data.type == 'master_detail'. No ablation this round: the change moves two pin numbers to a measured corpus, and the first round's red run of this file (2 failed, expected 82 to be 81 and 57 to be 56) followed by this green run is its before/after.", "mcp_calls": "0 — no MCP GitHub tool was called", "api_writes": "2 this round — both through the fleet-write relay as objectstack-fleet[bot]: (1) issue_patch = PATCH /repos/objectstack-ai/objectstack/issues/20485 (the PR body), relay run 36460809006, success; read back identical (21344 bytes), footer intact, draft still true, assignee os-warren untouched. (2) this os-dev-report comment = POST /repos/objectstack-ai/objectstack/issues/19332/comments, through scripts/pm/post-stamped.mjs. No label or assignee write. Git push this round, not REST: 16037890. Reads were REST GETs: claim comment 5873857698, the card's comments, pull 20485 (twice), and the check-runs of 16037890.", "gates": { "derived_by": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at the final head 16037890 (change set vs merge base e956924e, 14 files, 0 untracked); the first round's 86 plus check:docs-transcript-drift, which the lint test file brings in", "derived": 87, "ran": 87, "exit_nonzero_final": 0, "not_measured": 0, "unrun": 0, "ran_verdict": "✓ dispatch-gates --ran: 87 derived famil(ies) accounted for — 87 run, 0 NOT-MEASURED (a DERIVED zero — all 87 recorded an exit code and none of them is 3).", "first_run_nonzero": [ "pnpm check:dual-build-cjs-loads :: exit 3 (PREREQUISITE NOT MET: nine packages had no dist/ in the re-added worktree). Later gates in the same run built them; the rerun exited 0 with '104 published require entry point(s) across 66 package(s) load'. ran.list records the rerun's code." ], "ci_at_report": "head 16037890, read 2026-09-28T17:49Z: 40 check runs — 27 success, 5 skipped, 8 in_progress, 0 failure" }, "line_budget": { "changed_lines": 646, "additions": 615, "deletions": 31, "files": 14, "human_merge_threshold": 5000, "verdict": "under", "skills_budget": "n/a — no skills/** path" }, "files_changed": [ ".changeset/19332-g2b-remaining-g2-form-rows.md", "packages/lint/src/validate-predicate-path-refs.test.ts", "packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/field-panel-echo-decisions.test.ts", "packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/object-collapsed-sections-echo-decisions.test.ts", "packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts", "packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts", "packages/spec/src/data/field.form.ts", "packages/spec/src/data/field.zod.ts", "packages/spec/src/data/object.form.ts", "packages/spec/src/data/object.zod.ts", "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts" ], "deviations": [ "packages/lint/src/validate-predicate-path-refs.test.ts is in the diff under the claim's in-place amendment (5873857698, 'Amended 2026-09-28T17:27Z'): only the two census numbers and their history comments, +14/−2 in that file.", "The base-corpus reading ran in a scratch worktree detached at e956924e (/home/user/objectstack-issue-19332-base). The probe file was copied in, run with tsx and deleted, and the worktree was removed with rm -rf node_modules then git worktree remove, unforced. The issue worktree carried the head reading the same way, and git status was clean afterwards.", "check:dual-build-cjs-loads is recorded in ran.list with its rerun code (0); the first run exited 3 on a prerequisite (gates.first_run_nonzero).", "Suites were not re-run beyond the lint file: there was no merge, and the only new commit changes one lint test file no other suite reads. The instruction made the other suites conditional on a merge.", "Shared checkout /home/user/objectstack: only git fetch and git worktree add / remove ran there. No edit was made to it.", "Cleanup: the issue worktree /home/user/objectstack-issue-19332 is removed right after this comment posts, with rm -rf node_modules and then git worktree remove, unforced. Everything is pushed and the tree is clean." ], "open_questions": [], "out_of_scope_findings": [ "class: c · folded into #20432 by the seat, as the patch message says — activityMilestones[].field and inlineColumns[].name are judged by no authoring door. Measured in the first round, and re-read on origin/main 4b2d9041 after #20479 landed: its list positions still do not include them. dedupe words: activityMilestones field unknown · inlineColumns name unknown field · milestone never fires misspelt field · inline grid column reference integrity", "carrier: #20432 (the seat) · noted, not filed — G2a's indexes.fields help text on object.form.ts ('Nothing checks them when you save or publish: a name that is not a stored column makes the SQL driver skip the whole index…') went half stale when #20479 landed as 4b2d9041. object-field-ref-unknown now refuses an index column that names no field, at error, at the publish door and in os validate; the Studio save door (ObjectSchema.safeParse) still admits one. The row is G2a's, outside this flight's rows, and a text change there moves four catalogue leaves, so it is not edited here. #20479's own body said no form text changes.", "carrier: 承接者:无 · noted, not filed — carried unchanged from the first round's Acceptance notes: the activityMilestones.type describe default ('completed' vs the runtime's updated), the public share-link audience TSDoc, maxExpiryDays not forcing an expiry, the field-ref name convention on lifecycle.ttl.field (code reading, not browser-run), and the userActions switch reading off when unset." ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsos-dev-report
{ "issue": 19332, "status": "done", "branch": "claude/issue-19332-g2b-remaining-g2-rows", "pr": "https://github.com/objectstack-ai/objectstack/pull/20485", "session": "session_01ARcDurZ5j34RdqsGgc4jgH (subagent run; the parent PM session's id, as CLAUDE_CODE_REMOTE_SESSION_ID resolves it)", "premise_still_valid": true, "summary": "Second patch round done. Flight G2b is complete on draft PR #20485 at final head 2bcad436, and nothing is blocked. The seat amended claim 5873857698 in place a second time ('Amended 2026-09-28T17:51Z', updated_at 17:52:18Z): G2a's indexes.fields help text and its four catalogue leaves joined the surface for one correction. The newest Claim: is still that comment, naming this branch. The worktree was re-added from the pushed branch at 16037890, and only new commits were added, with no rebase, amend or force-push. origin/main (9801da12) had moved onto one file of this diff: #20456's e967cbd2 edited three view why texts in the reconciliation ledger. So it was merged with scripts/pm/os-regen-merge.sh as e809f0bd, without conflict; step 2 took main's side of the generated artifacts it moved, and check:generated then read all 15 up to date. Measured on that merged tree, which includes #20479 (4b2d9041): ObjectSchema.safeParse (the draft-save door) accepts a misspelt index column. os validate's rules (runAuthoringRules('validate')) and the runtime publish gate (runRuntimeAuthoringRules, type object) both return object-field-ref-unknown at error on it; a lit control on highlightFields also fires. An in-memory SQLite SqlDriver sync skips an index on a formula field, and one on a misspelt name, each with '[sql-driver] skipping declared index … column(s) not materialized' at warn, and creates the index on a stored column. 2bcad436 rewrites only that sub-row's helpText to 'Column names of this object, in key order (e.g. status, owner). Saving does not check them; publishing and os validate refuse a name that is not a field of this object. A field that is not a stored column (a formula, say) makes the SQL driver skip the whole index, with a warning in the server log.' It also rewrites the code comment directly above the row, which stated the old claim (see deviations). The en leaf was regenerated by the tool and the zh-CN / ja-JP / es-ES leaves authored. The text change moves no pin. The PR body gained the sentence naming the correction under the amendment, plus updated Concurrency, Gates and verification lines; it was read back identical with its footer.", "tests": "Third round, through scripts/pm/os-verify-lock.sh, slot issue-19332-g2b-p3, at 2bcad436 unless noted. pnpm --filter @objectstack/spec test = 'Test Files 572 passed (572) / Tests 16791 passed | 1 todo (16792)', VERDICT 0 (the reconciliation, title and form tests included). pnpm --filter @objectstack/platform-objects test = 'Test Files 55 passed (55) / Tests 911 passed (911)', VERDICT 0, and no pin moved. lint src/validate-predicate-path-refs.test.ts + src/validate-object-field-refs.test.ts = 2 files, 114 passed, VERDICT 0. pnpm check:i18n = 'check-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys)', after the closure build ('Tasks: 59 successful, 59 total') and two --write passes; the second kept the three authored translations and left no source-hash row. pnpm --filter @objectstack/spec check:generated = 'All 15 generated artifacts are up to date', VERDICT 0, on the merged tree with the edited form (the build closure built spec from it). spec test:repo was not re-run: the instruction named spec test, and the merge's spec changes are covered by it and by CI. Probes, never committed: (a) packages/lint untracked .mjs over built spec and lint: 'save door ObjectSchema.safeParse success: true'; 'os validate rule findings on indexes: object-field-ref-unknown / error / objects[0].indexes[0].fields[0]'; 'publish gate errors: object-field-ref-unknown / error / objects.probe_task.indexes[0].fields[0]', advisories none; the formula column produced no finding; lit highlightFields control 1 finding. (b) driver-sql untracked vitest file, after building the driver's dependency closure: 'columns [id, created_at, updated_at, status, amount]', 'indexes [idx_ok, sqlite_autoindex_probe_task_1]', and logs 'warn: [sql-driver] skipping declared index on \"probe_task\" — column(s) not materialized: amount_fx' and '… : statsu'. Both probe files were deleted and git status read clean.", "mcp_calls": "0 — no MCP GitHub tool was called", "api_writes": "2 this round — both through the fleet-write relay as objectstack-fleet[bot]: (1) issue_patch = PATCH /repos/objectstack-ai/objectstack/issues/20485 (the PR body), relay run 36465418341, success; read back identical (22689 bytes), footer intact, draft still true, assignee os-warren untouched. (2) this os-dev-report comment = POST /repos/objectstack-ai/objectstack/issues/19332/comments, through scripts/pm/post-stamped.mjs. No label or assignee write. Git pushes this round, not REST: e809f0bd (the merge) and 2bcad436, in one push. Reads were REST GETs: claim comment 5873857698, the card's comments, pull 20485 (twice), and the check-runs of 2bcad436.", "gates": { "derived_by": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at the final head 2bcad436 (change set vs merge base 9801da12, 14 files, 0 untracked); the same 87 families as the first patch round", "derived": 87, "ran": 87, "exit_nonzero_final": 0, "not_measured": 0, "unrun": 0, "ran_verdict": "✓ dispatch-gates --ran: 87 derived famil(ies) accounted for — 87 run, 0 NOT-MEASURED (a DERIVED zero — all 87 recorded an exit code and none of them is 3).", "first_run_nonzero": [ "pnpm check:dual-build-cjs-loads :: exit 3 (PREREQUISITE NOT MET: eight packages listed without dist/ in the re-added worktree). Later gates in the same run built them; the rerun exited 0 with '104 published require entry point(s) across 66 package(s) load'. ran.list records the rerun's code." ], "ci_at_report": "head 2bcad436, read 2026-09-28T18:29Z: 40 check runs — 27 success, 5 skipped, 8 in_progress, 0 failure" }, "line_budget": { "changed_lines": 670, "additions": 628, "deletions": 42, "files": 14, "human_merge_threshold": 5000, "verdict": "under", "skills_budget": "n/a — no skills/** path" }, "files_changed": [ ".changeset/19332-g2b-remaining-g2-form-rows.md", "packages/lint/src/validate-predicate-path-refs.test.ts", "packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/field-panel-echo-decisions.test.ts", "packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/object-collapsed-sections-echo-decisions.test.ts", "packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts", "packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts", "packages/spec/src/data/field.form.ts", "packages/spec/src/data/field.zod.ts", "packages/spec/src/data/object.form.ts", "packages/spec/src/data/object.zod.ts", "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts" ], "deviations": [ "packages/spec/src/data/object.form.ts: besides the indexes.fields helText the amendment admits, the six-line code comment directly above the indexes row was rewritten (8 lines). It stated the old claim ('No authoring door judges its names: not the schema parse, not the publish door, not os validate … so the help text claims that and no refusal'), and it would have contradicted the corrected text five lines below it. No other row, sub-row or comment of G2a changed. If the seat reads the amendment as the helText alone, the comment hunk reverts cleanly on its own, but the file would then carry a false comment.", "origin/main was merged once, with scripts/pm/os-regen-merge.sh, as e809f0bd (9801da12), because e967cbd2 had edited the reconciliation ledger in this diff. The merge had no conflict and there was no generated-hunk hand resolution. The deferral was discharged by 2bcad436 after check:generated read clean.", "Probes ran as untracked files inside the issue worktree (packages/lint and packages/drivers/driver-sql/src), deleted right after; git status read clean. Building the driver-sql dependency closure (pnpm --workspace-concurrency=2 --filter '@objectstack/driver-sql^...' run build, VERDICT 0) was for the probe only.", "check:dual-build-cjs-loads is recorded in ran.list with its rerun code (0); the first run exited 3 on a prerequisite (gates.first_run_nonzero).", "The PR body update carries more than the one sentence the instruction named: the Concurrency, Gates and verification lines and the stale-text acceptance note were also updated, because each would otherwise have been false about the merge, the new head or the correction.", "Shared checkout /home/user/objectstack: only git fetch and git worktree add / remove ran there. No edit was made to it.", "Cleanup: the issue worktree /home/user/objectstack-issue-19332 is removed right after this comment posts, with rm -rf node_modules and then git worktree remove, unforced. Everything is pushed and the tree is clean." ], "open_questions": [], "out_of_scope_findings": [ "class: c · folded into #20432 by the seat — activityMilestones[].field and inlineColumns[].name are judged by no authoring door. Re-read on origin/main after #20479: still not among its list positions. dedupe words: activityMilestones field unknown · inlineColumns name unknown field · milestone never fires misspelt field · inline grid column reference integrity", "carrier: 承接者:无 · noted, not filed — an index column that names a real field which is not a stored column (a formula) passes object-field-ref-unknown and is still skipped whole at sync with a warn, as #20479's own acceptance notes record ('Materialization stays the driver's question'). The corrected help text states it. The sync-half durability question (a skipped unique index logged at warn) is #20432's step 2, per that PR.", "carrier: 承接者:无 · noted, not filed — carried unchanged from the first round's Acceptance notes: the activityMilestones.type describe default ('completed' vs the runtime's updated), the public share-link audience TSDoc, maxExpiryDays not forcing an expiry, the field-ref name convention on lifecycle.ttl.field (code reading, not browser-run), and the userActions switch reading off when unset." ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsReview: ACCEPT · PR #20485 (flight G2b) at head
2bcad436f3efb86e4390a565efd72e814cd942e9· 2026-09-28T18:53Zdomain:specseat 4 (session_01ARcDurZ5j34RdqsGgc4jgH), reviewer of record under claim5873857698, as amended at 17:27Z and 17:51Z. This seat took the readings below on GitHub or on the tree.check reading Shape Draft, base main. Body line 1Part of #19332, line 2Flight G2b of ruling 5861442317., andClause-②: nostands alone. No closing keyword: the seat closes the card after this lands.Scope 14 files, +628/−42. What is in the diff: the activityMilestonesrepeater (4 sub-rows), thepublicSharingcomposite (6), theuserActionscomposite (5,jsonon the four union members per the ruling), thefield.inlineColumnsrepeater (4, gated like itsmaster_detailsiblings), the one nestedsubsetrow forinlineColumnsin the reconciliation ledger, 24.meta({ title })calls (byte-proved to be the only change toobject.zod.ts/field.zod.ts), the four catalogues (46enleaves + 138 translated), the moved pins, and the changeset.Amendments 17:27Z: validate-predicate-path-refs.test.tsmoves only its census pin (81 → 82, 56 → 57, one new==predicate), and nothing else underpackages/lint/**changes. 17:51Z: G2a'sindexes.fieldshelp text and its four leaves now state whatmaindoes after #20479: save does not check; publish andos validaterefuse an unknown name; the SQL driver skips a non-stored-column index with a warn. The 8-line code comment above theindexesrow was rewritten too. The claim did not name it, but the record judges it a necessary consequence of the admitted correction, not a breach, because the old comment was false onmain. This seat accepts that reading.Contract review At-tier record PASS 5876386886, same head. It checks every sub-row's face against its schema node and mirror row at the objectui pindd3f7e1b, including whywidget: 'text'escapes thefield-refconvention and whymultiselect/string-tags/jsonfit. It checks thesubsetrow's sixteen withheld keys againsthydrateColumns, and every help-text claim against its reader (audit-writers.ts,share-link-service.ts,system-write-guard.ts,deriveMasterDetail.ts). The accept set does not move.Clause-②: noholds.Premises The residue drops 4 → 0. No help text claims a refusal it does not have: redactFieldsalone claims the publish refusal that exists. The mergee809f0bdcarried nothing ofe967cbd2's ledger edit and resolved nothing by hand (the record reproduces its tree frommerge-tree).Pins Mechanical and complete: collapsed / advanced 69 → 105 / 60 → 96 (+36 = 18 rows × 2); lifecycle 634 → 657 (+23 labels); field-panel ROW_PROPERTIES6 → 10 withinlineColumnswalked; the lint census +1 / +1.CI at this head 37 success, 5 skipped, all on the roster ( check-expected-skips.mjs --pr 20485, exit 0).mergeable_state:clean.git merge-treeagainstorigin/mainfc0db22bis clean; the five commitsmaingained touch no file of this diff and no catalogue source.Governed / size Not governed ( check-governed-merges.mjs --pr 20485: 0 of 14 paths), 670 changed lines.Out-of-scope findings:
activityMilestones[].fieldandinlineColumns[].nameare judged by no authoring door (class c) → folded into [finding] a misspelt field name in a field'srelatedListColumns,lookupColumns,lookupFilters[].fieldordependsOnpasses every authoring door, and fails only at view or picker time #20432 (5875171759).- The
activityMilestones[].typedescribe says the default iscompleted, but a milestone withouttypewritesupdated(the record's ③ escalation;object.zod.ts:2123, published in the reference page) → filed [finding]activityMilestones[].typeis documented as defaulting to "completed", but a milestone withouttypewritesupdated#20494 (class b, release-fixed wrong text). - A formula column in an index passes
object-field-ref-unknownand is skipped whole at sync → already carried by [finding] a misspelt field name in a field'srelatedListColumns,lookupColumns,lookupFilters[].fieldordependsOnpasses every authoring door, and fails only at view or picker time #20432 step 2 (5875310879, re-routed todomain:engineby5875602547). lifecycle.ttl.field(G1a'stextrow, nowidget) may meet the samefield-refconvention this flight escaped, and render a None-only picker on an object draft. That is a code reading at the pin by the record, not measured in a browser → Acceptance notes, ⛔ not filed (noreach:). A seat that browser-runs the lifecycle panel measures it.- Carrier-none notes from the dev reports (
maxExpiryDaysnot forcing an expiry, the switch reading off when unset, thepublicaudience TSDoc) → Acceptance notes. ThepublicSharingsystem-context mint bypass is not an author-facing door; noted by the record, not a defect.
Landing next: ready + auto-merge through the relay. At MERGED, G2's two flights are done, and the seat records the card's close-out against ruling
5861442317.domain:specseat 4 · #18917 ·session_01ARcDurZ5j34RdqsGgc4jgHobjectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsLanded: PR #20485 (flight G2b) →
main19e58e2dbf129aa6c91992906e534970f8127c2f· 2026-09-28T19:21Zdomain:specseat 4 (session_01ARcDurZ5j34RdqsGgc4jgH), landing record for claim5873857698(as amended at 17:27Z and 17:51Z). ACCEPT5876446270on the at-tier PASS5876386886. Queue path: ready at 18:55Z, into the merge queue at 18:57Z, merged at 19:19Z.Verified on
origin/main:19e58e2dhas one parent,45f428d8, and is an ancestor oforigin/main.- Its diff against that parent is 14 files, +628/−42, the same as the PR.
- Content controls, parent → merge: the
field: 'activityMilestones','publicSharing'and'userActions'rows inobject.form.ts, and'inlineColumns'infield.form.ts, each 0 → 1. Thesubsetrowpath: 'inlineColumns'in the reconciliation ledger is 0 → 1. G2a's correctedindexes.fieldssentence ("publishing and os validate refuse a name") is 0 → 1. - No queue branch for feat(spec): curated activityMilestones, publicSharing, userActions and inlineColumns form rows (#19332, flight G2b) #20485 remains.
G2's two flights are done. The card's close-out record follows.
domain:specseat 4 · #18917 ·session_01ARcDurZ5j34RdqsGgc4jgHobjectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsClose-out: ruling
5861442317is fully landed · 2026-09-28T19:22Zdomain:specseat 4 (session_01ARcDurZ5j34RdqsGgc4jgH). Ruling carried: batch #229 item 3 · letter A on all five groups · maintainer 「同意」. This card asked for a disposition of the 39 live structuredzodOnlykeys, so that the reconciliation gate can wire its top-levelzodOnlydirection. Every group has landed through the merge queue:group landed as G3 · G4 · G5 reasons (the root omitrows)ledger flight, PR #20362 → b1cbd927G5 action.ariadisposition RETIRED 5865896639, after #20323 (dcd3bcea)G1, object + permission G1a, PR #20405 → 7db1332f(#20349)G1, field + action G1b, PR #20428 → ec292cf5G2 fieldGroups+indexesG2a, PR #20449 → dc0ab6a2G2 activityMilestones,publicSharing,userActions,inlineColumnsG2b, PR #20485 → 19e58e2d- Residue: 4 → 0 at G2b, by the dev's run of the reconciliation test's helper block with lit and dark controls (report
5875500507). ⛔ This seat did not re-run it. The at-tier record5876386886found the diff consistent with it. - Unlock: #19188 split: 145 top-level zod-only keys need a RECORDED REASON, never a form row — and none can be recorded until the ledger learns a root path #19333 is
pm:blockedwithBlocked-by: #19332, for item 2 (wire the direction). This close is its unlock trigger. The unlock scan re-derives whether anything else now blocks it. - Filed or folded from the flights: objectui#10963 (the
CodeWidgetenvelope bound), [finding] a misspelt field name in a field'srelatedListColumns,lookupColumns,lookupFilters[].fieldordependsOnpasses every authoring door, and fails only at view or picker time #20432 (field-name integrity:indexes[].fields,activityMilestones[].field,inlineColumns[].name), [finding]activityMilestones[].typeis documented as defaulting to "completed", but a milestone withouttypewritesupdated#20494 (theactivityMilestones[].typedescribe). - Confidence gaps the ruling carried, still open:
action.description's authors are NOT MEASURED.- The objectui repeater faces were read at the pin, not browser-run.
stageField's existingObjectSettingsPanelcontrol is not browser-run.lifecycle.ttl.field's face is a code reading only (G2b ACCEPT5876446270).
pm:dispatchedcomes off and the assignee is released in this act. Thedomain:specandpriority:p2labels stay.domain:specseat 4 · #18917 ·session_01ARcDurZ5j34RdqsGgc4jgH- Residue: 4 → 0 at G2b, by the dev's run of the reconciliation test's helper block with lit and dark controls (report
- added 5 commits that reference this issue
on Sep 29, 2026
Ruled: 5861442317 · letter A · 2026-09-28T00:57Z
Path: P1 | 那条路第 1 步「写元数据」 | 39 个活键需要设计控件而非表单行
The bucket
39 keys of #19188's 274, bucket B9: object-rooted metadata type, liveness verdict
live, and the schema node is object / array / record / union. ⇒ each needs a designed control, ⛔ not a one-line row.Examples named by the census:
object.access·object.external·object.fieldGroups·field.summaryOperations·action.params.Why it is separate from the scalar bucket
The scalar bucket (47 keys, filed alongside) is one row per key with no design decision. This one is a widget design per key, or a curated subset entry where the full shape is not worth authoring. Merging them would hide 39 design decisions inside a 86-row mechanical change — which is the shape triage refused when it said 「⛔ do not dispatch this as one flight」.
Dedup words
structured control metadata form·object array record union offer·zodOnly bucket structured·fieldGroups summaryOperations params control·curated subset form entryOrigin: the #19188 census round, report comment 5749550902 (2026-09-20T11:37Z), base
596090efbe7. Its numbers were re-derived by the dev with the reconciliation gate's OWN helper block sliced verbatim (sha256f6729dae2829…), ⛔ not by grepping source, with a lit control (name, offered by 17 of 17 forms) and a dark control (a fabricated key, 0) asserted inside the probe.Filed-by:
session_01LvwGppdonww4zGLWZo5rho(domain:specexecution seat 1), as the split triage asked for at 5747751499 — 「the claiming seat's first deliverable is the split, not the fix」. ⛔ Not graded and ⛔ not routed by this seat.Generated by Claude Code