Repository navigation
Studio field designer is hand-coded, not metadata-driven — bring design-time authoring into the protocol (fieldForm / metadata-form-registry) #3257
Description
Activity
- added a commit that references this issue
on Jul 19, 2026 分诊(spec 车道欠账代扫,维护者 2026-08-05 指令):挂
needs-user-decision。这是一张 ADR 级方向卡:Studio 设计期表单是否按棘轮收敛到fieldForm/metadata-form-registry协议路径(三步:设计期 widget 可声明化 → Studio 消费注册表 →fieldFormenforce-or-remove)——多周程序,需要 appetite 与排序,且横跨 framework+objectui。正文的「immediate small step」(fieldForm的summaryOperations补 composite 子字段声明)立单于 07-19、状态未核,appetite 确认后由 spec 车道先核该步是否已被后续工作覆盖再拆单。不构成认领。会话:session_01N3uGFF8teXbpgtbEJ1aYXu
Generated by Claude Code
处置(2026-08-06):改标 pm:on-hold。
理由:协议链已自发长活——
METADATA_FORM_REGISTRY已是/metaform payload 单一来源(protocol.ts:94),objectui 抽屉编辑器已消费下发的entry.form(EmbeddedItemEditor.tsx:62、editAs:'field'),原单「enforce-or-remove fieldForm」诉求已被现实解决;「immediate small step」也已随 PR #3258 落地。剩余的全量 program(L,多周跨两仓)当前无迫使性痛点。重启条件:(a) 下一次新增可作字段能力再度迫使 objectui 手写编辑器块(#1868 型触发),或 (b) #2657 审计刷新产出后,与其 Part B 准入窄问题合并为一次 Studio appetite 拍板。届时若批,建议先做 B 切片(简单字段类型走 fieldForm 渲染,复杂块保留手写,M),不上来就全量棘轮。
经办:PM 会话
session_01GcjbQLUQKysMU9uXB34iyv;维护者 2026-08-06 审阅决策简报后授权按建议执行(否决窗口:可评论/重开推翻)。
Generated by Claude Code
【决策箱注记】维护者 2026-08-06 批复全舰队决策箱评估报告(批复「同意」),本单处置:
方向卡已过维护者视野:棘轮三步收敛路线(设计期 widget 声明化 → Studio 消费注册表 → fieldForm enforce-or-remove)成立,但胃口未定——横跨 framework+objectui 的多周程序,继续挂箱待排期,不启动。
可先行项:immediate small step(fieldForm 的 summaryOperations 补 composite 子字段)由 spec 座位核实是否已被覆盖后自行拆单,不等本单、不塞本单。
本单保留
needs-user-decision(待胃口/排期)。评估会话:
session_01N3uGFF8teXbpgtbEJ1aYXu
Generated by Claude Code
Label repair:
pm:queue→needs-user-decision(spec seat, sessionsession_01E5tUwGM3LQoqErTfkvRW7W).Found while selecting the next dispatch batch: the card was carrying
pm:queue— advertising itself as dispatchable — while its own record ends at "held pending appetite". The maintainer's 2026-08-06 approval of the decision-box review (批复「同意」) closes with 本单保留needs-user-decision(待胃口/排期): the ratchet route is accepted in principle, the appetite for a multi-week programme spanning framework + objectui is not granted. Nothing here re-decides that; only the label is brought back into agreement with the thread.Two things carried forward so the next reader does not have to reconstruct them:
- The card's own "immediate small step" (
fieldForm'ssummaryOperationsgaining composite sub-field declarations) is explicitly not blocked by this card — the same ruling says the spec seat may verify whether it is already covered and split it out on its own, ⛔ 不塞本单. It landed via PR fix(spec): declare summaryOperations sub-fields in the Field metadata form (#3257) #3258 per the 2026-08-06 disposition; if a future reader finds a residue, it becomes its own card, not a reason to unhold this one. - Restart conditions on record: (a) a new authorable-field capability again forcing objectui to hand-write an editor block (the [P0] Add native cross-object rollup/summary capability (parent aggregates of child rows) #1868 shape), or (b) the Studio metadata coverage gaps: surface remaining types + promote un-typed concepts #2657 audit refresh landing, at which point this and Studio metadata coverage gaps: surface remaining types + promote un-typed concepts #2657's Part B are decided as one Studio appetite call.
Generated by Claude Code
- The card's own "immediate small step" (
Maintainer ruling (delegated adjudication; delegation 2026-08-15 verbatim 「决策你直接帮我做」, batch confirmed 「同意」)
Ruled: the direction was already decided — this card moves to
pm:on-holdand leaves the decision inbox.The 2026-08-06 approval (批复「同意」) accepted the ratchet route in principle (design-time widget declarability → Studio consumes the registry →
fieldFormenforce-or-remove) and withheld only the appetite for a multi-week programme spanning framework + objectui. Nothing further is pending a maintainer decision; what is pending is a scheduling window. Carryingneeds-user-decisionmade this card impersonate an unanswered question in the inbox for nine days, which is exactly the label-lie the inbox cannot afford.Restart conditions, unchanged and on record:
- (a) a new authorable-field capability again forcing objectui to hand-write an editor block (the [P0] Add native cross-object rollup/summary capability (parent aggregates of child rows) #1868 shape); or
- (b) the Studio metadata coverage gaps: surface remaining types + promote un-typed concepts #2657 audit refresh landing — at which point this and Studio metadata coverage gaps: surface remaining types + promote un-typed concepts #2657's Part B are decided as one Studio appetite call.
If granted then, start with the B slice (simple field types render through
fieldForm, complex blocks stay hand-written), not the full ratchet.The card's "immediate small step" remains explicitly not blocked by this card and landed via PR #3258; any residue becomes its own card. ⛔ 不塞本单.
Label flipped
needs-user-decision→pm:on-hold.
Generated by Claude Code
- addedpriority:p2Medium: important, M3Medium: important, M3and removed
on Sep 19, 2026 Triage:
Path: studio-authoring; gradenone→p2; disposition keep (returned topm:queue).rationale: spec declares
fieldFormand registers it inmetadata-form-registry, but objectui consumes neither (grep-verified on the card), so the declared design-time form contract is inert — declared ≠ enforced, North Star rule 4. Not a broken path (Studio authors fields today through hand-written React), so p2.Re-graded against
docs/NORTH-STAR.mdas part of the maintainer-assigned full-board sweep.pm:on-holdwas removed because the charter permits that state only with a machine-readable wake condition. Closures in this sweep are the maintainer's call — this card was kept, not closed.
Generated by Claude Code
Triage: closing
not_plannedunder the maintainer-assigned full-board re-grade againstdocs/NORTH-STAR.md.North Star basis: none. This is an implementation route, not a capability gap —
studio-authoring.object-designer-roundtrip(P1) asks whether field add/edit/reorder persists, and the hand-written inspector passes it. 「不写代码在运行中改应用」 holds for fields today.What is kept, not lost. Closing an issue does not delete it — the body and its whole thread stay readable and reopening is free. What stays worth reading in the body: the asymmetry it names (runtime business forms render from Field metadata with zero hand-written code, while the design-time designer is ~1400 lines of bespoke React) and the grep-verified fact that
fieldForm+metadata-form-registryare declared in spec and consumed by nobody.Reopening is free if a new authorable field capability again forces a hand-written editor.
Generated by Claude Code
Correction — this seat's batch-1 re-grade on this card is withdrawn
Triage seat (#6015),
session_01Y6AwMBAN8zRUHgmgFdV1Qr. Earlier today this card was re-graded, then closednot_planned. The card has been reopened and itspm:on-holdrestored.⛔ The re-grade was made without reading this thread, and it contradicted the ruling recorded here:
Ruled: the direction was already decided — this card moves to
pm:on-holdand leaves the decision inbox (delegated adjudication, 2026-08-15, batch confirmed 「同意」)維護者裁決 outranks 北極星 in the priority order, so a North Star re-grade cannot override it. Nothing about this card had changed; the seat simply had not read it.
Root cause, stated rather than buried. The sweep detector read only the issue body, so every ruling and every
Restart-when:line living in a comment was invisible to it. All eight cards touched in batch 1 carry such a ruling; five were closed and all five are reopened. The sweep's 「112 cards carry no wake mechanism」 figure is withdrawn as measured on a broken instrument. The charter already required the fix — 「每张候选读全文 + 全部评论」 — and this seat did not do it.
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorMore actionsClosed
not_plannedby the director seat (summon #28 续,session_01GLdRPcbaCBQCTvVmU6YEUY), 2026-09-24T05:24Z, on the maintainer's word — closure review batch 3 of the opendomain:speccards under the restructured triage standard, presented from the business angle in this seat's chat; maintainer verbatim 「同意」 to the batch as presented.Why: this is an implementation route — a metadata-driven Studio field designer generated from
fieldForm/metadata-form-registry— not a capability gap. The Studio rows on NORTH-STAR's road (object designer round-trip, view authoring live, record page round-trip) pass with the hand-writtenObjectFieldInspector, so 北极星第 2 条 reads 「不在路上 ⇒ 不做」. ItsRestart-when:(closed #2657) fired only because #2657 closednot_plannedtoday, not because a Studio appetite call was made; the other trigger the body names — a new field capability forcing another hand-written editor (the #1868 shape) — arrives with its own card when it happens, and that card may cite this one.Supersedes the 2026-08-15 delegated-adjudication hold, on the maintainer's word. The asymmetry this card records (runtime forms are metadata-driven, the design-time designer is not) stays readable here. Ledger: seat post #12708, this summon's next block.
Path: studio-authoring
Restart-when: closed #2657 (the audit refresh) — decide this and #2657 Part B as ONE Studio appetite call, starting with the B slice (simple field types render through fieldForm, complex blocks stay hand-written); or a new authorable-field capability again forces objectui to hand-write an editor block (the #1868 shape)
Context
Surfaced while shipping filtered roll-up summaries (#1868 → framework #3227 + objectui#2669). Adding one new field capability (
summaryOperations.filter) required hand-writing a React editor in objectui (ObjectFieldInspector.tsx→SummaryConfigFields) in lockstep with the spec change — which raised the question: shouldn't the field designer be generated from the protocol?Observation
There is an asymmetry in how forms are produced:
showcase_expense_reportrecord form (incl. its six summary fields) renders from Field metadata with zero hand-written render code. ✅ This is the ObjectStack way.packages/app-shell/src/views/metadata-admin/inspectors/ObjectFieldInspector.tsx(~1400 lines of hand-coded React). Every field-type config there is hand-coded: select options, lookup picker +lookupFiltersrows,summaryOperations(object/function/field/relationship), CEL rules — and now the summaryfilterrow editor.The protocol already declares a metadata form for a Field —
packages/spec/src/data/field.form.ts(fieldForm), registered inpackages/spec/src/system/metadata-form-registry.ts. But objectui does not consumemetadata-form-registry/fieldFormat all (verified by grep); the Studio designer bypasses it. SofieldFormis effectively inert for the live authoring surface, and in itsummaryOperationsis a barecompositewith no declared sub-fields.Why this matters
A metadata-driven framework whose own primary authoring surface is not metadata-driven means every new authorable field capability costs a hand-coded editor in a separate repo (objectui), kept in lockstep with the spec by hand. That is exactly the kind of duplicated, drift-prone surface the platform elsewhere designs away. It also means the
fieldFormprotocol artifact is unenforced/inert (cf. the spirit of ADR-0078 "no silently-inert metadata" and ADR-0049 enforce-or-remove).Why it's hand-coded today (the real constraint)
A declarative
FormViewcan't yet express the interactions the field designer needs:visibleWhen: data.type == 'summary') — this one FormView already supports;useObjectFields(childObject));visibleWhen/readonlyWhen/requiredWhen);true, numeric operator →{ $gte: 500 }).These require imperative React today, so objectui made the pragmatic split: bespoke design-time inspector, metadata-driven runtime forms.
Proposed direction (ADR-worthy)
Converge the design-time authoring surface onto the protocol, as a ratchet (not a big-bang rewrite):
filter-conditionrow editor, a dependentref:object+ child-field picker (scope a field list by a sibling value), a CEL predicate editor. Some of these widget names already appear infield.zod.ts'swidgetdocs (filter-condition,object-ref,recipient-picker) but have no protocol-level form binding.fieldForm(consumemetadata-form-registry), starting with the simple field types, and keep the bespoke inspector only for the config blocks the protocol/renderer can't yet express — retiring them one at a time as the widgets land.fieldForm: while it's inert, either wire it up (above) or mark it experimental, per ADR-0049/0078, so it isn't a false "the field editor is metadata-driven" signal.Immediate small step (separate PR, will link)
Declare
summaryOperations' sub-fields —object/function/field/relationshipField/filter— explicitly infieldForm'scomposite(FormFieldSchema.fieldsalready supports this), withfilterbound towidget: 'filter-condition'. This makes the protocol path render structured fields instead of a raw JSON blob, and closes the gap that this issue's investigation found infieldFormitself, without waiting on the larger convergence.Affected
@objectstack/spec—field.form.ts(fieldForm),system/metadata-form-registry.ts, theFormView/widget protocol inui/view.zod.ts.objectui—app-shellmetadata-adminObjectFieldInspector,plugin-form/fieldsrenderers (would need the new design-time widgets + to consume the registry).Filed per AGENTS.md Prime Directive #10 (file out-of-scope findings rather than leave them buried). Found while runtime-verifying #1868.
Generated by Claude Code