Repository navigation
finding(app-shell): detectConditionWidget requires a string arm, but every SERVED metadata predicate key derives without one — the generic condition builder may mount for no served type at all #9830
Description
Blocked-by: objectstack-ai/objectstack#19295
Dedup keywords: detectConditionWidget · ExpressionInputSchema · anyOf string arm · condition builder never mounts · meta/types served derivation
Surfaced by the os-dev on objectui#9791, which stopped on a falsified remedy premise and reported this instead of widening its card. ⛔ Unassigned and ungraded. Class (c) on its face — a predicate an author can write that the runtime then refuses — but the population claim wants triage's own reading, so both halves are separated below by who measured them.
Half 1 — the mechanism. Verified at source by the domain:ui#2 seat, ⛔ not inherited
packages/app-shell/src/views/metadata-admin/SchemaForm.tsx:591-599:
function detectConditionWidget(name, schema) {
if (Array.isArray(schema?.enum)) return undefined;
const isString =
schema?.type === 'string' ||
(Array.isArray(schema?.anyOf) && schema.anyOf.some((b) => b?.type === 'string'));
if (!isString) return undefined;
if (CONDITION_FIELD_NAMES.has(name) || /When$/.test(name)) return 'condition';
return undefined;
}
⇒ the CEL condition builder is routed only when the property's JSON Schema is type: 'string' or carries an anyOf arm typed string. The name convention alone is not sufficient — isString gates it first. Read at SchemaForm.tsx:591, ⛔ not from a report.
Half 2 — the population. The dev's measurement, ⛔ NOT re-derived by this seat
Measured against the installed @objectstack/spec 17.4.0, by the same z.toJSONSchema call objectstack serves /meta/types with:
- every predicate key the dev could find —
hook.condition,sharing_rule.condition, flowedge.condition, page/action navvisible,field.visibleWhen/readonlyWhen/requiredWhen— isExpressionInputSchema, a union whose string arm is a transform, so the OUTPUT derivation erases it, leaving an anyOf of an empty schema plus the ADR-0089 envelope object — no string arm; action, the one type served from the authoring retry arm, derivesvisible/disabledas anyOf boolean-plus-anything — also no string arm.
⇒ if both halves hold, ConditionWidget mounts for no served type at all, and the two pin tests that exercise it pass only because they declare a bare type: 'string' predicate key themselves.
widget: 'condition', would change the answer; no such pin was found in spec or in this repo outside a test. ⇒ ⛔ this seat does not assert Half 2 as settled — it is a reading to reproduce, and the reproduction recipe is in the dev's report on objectui#9791.
Why this is class (c) if it holds
The affected keys' runtimes fail-CLOSE on an unevaluable predicate (objectql's rule validator binds { record, previous } and rejects the write; the hook tier likewise). ⇒ an author writing a condition on such a surface gets neither the builder nor any lint, and the record is refused at evaluation time. That is metadata written by one party and rejected by the runtime that consumes it.
⚠️ What it would mean for a ruling already in force
objectui#8167's director ruling (5716922008) pins a seven-row table mapping metadata type to lint scope, and ResourceEditPage derives from it. ⭐ If the builder never mounts for a served type, then the generic-form arm of that ruling is dark in production today — the table is correct and pinned, and the surface it governs is not reached. ⛔ That is a reading for the director seat, ⛔ not an edit: the table's values are ruled, and this card ⛔ does not propose changing them.
ResourceEditPage for a hook and DOES observe the engine's verdict — but it supplies the /meta/types schema row as a fixture with condition typed string. ⇒ that pin does not contradict Half 2; it is a hand-written schema of exactly the kind Half 2 says is the only thing that reaches the widget.
Options the dev laid out, ⛔ none chosen here
- File and let the director seat decide whether
detectConditionWidgetshould learn the ADR-0089 envelope shape. - Treat it as spec-side: have
/meta/typesserve the authoring (io input) derivation —⚠️ which objectstack's owntoJsonSchemaSafeheader records as proposed, measured, and deliberately REFUSED as a published-contract weakening. ⇒ ⛔ that arm is not freely available. - Do nothing; accept the generic builder is reachable only for hand-written schemas.
Dedup
⛔ Not run — filer attaches the words, triage runs them. Keywords at the top.
os-decision-facets
维护者速读
元数据设计器要为一个字段挑一个编辑控件。它从服务端拿到的那个字段的「形状」有时是空的({}),而空形状在 JSON Schema 里同时表示两件相反的事:「什么都收(包括字符串)」和「这个变换把自己的输入类型擦掉了」。⇒ objectui 从它被服务的文档里分不出这两者,所以它只能一律否决,作者就拿不到条件构建器。
交付物 ① 已经落地(PR objectui#9911:五份复制粘贴的判断收敛成一个具名 admitsString())。剩下的这一半不可能由本仓推出,必须由声明面回答。
问:A 让键名压过无信息的形状;还是 B 给「擦除族」一个标记,让消费端分得出来?(两条路都 ⛔ 不回滚已交付的 ①)
| 选项 | 做什么 | 代价 |
|---|---|---|
| A 名字压过形状 | 形状为 {} 时不再否决,按键名(condition / visibleWhen / readonlyWhen / requiredWhen)直接给条件构建器 |
{} 读作 admits-string」,而承接席的消融②已经反向 pin 住了这一点 ⇒ 选 A 必须同笔改那条 pin |
| B 给擦除族一个标记 | 在 ADR-0089 信封上加一位(或等价手段),让消费端把「擦除」与「什么都收」分开;标记落地前 objectui 维持现状 | 协议面改动,要走 objectstack 的发布与兼容流程;短期内作者仍需在 form spec 里显式钉 widget |
io: 'input')已被上游量过并明确拒绝,而且有一条会变红的 pin 守着(packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts:19-28:24/26 个类型在 input 下答案不同,方向是削弱已发布契约)⇒ ⛔ 路已关,不在选项里。
四棱
- ① 项目长远合理性:B 让声明面成为唯一真相,消费端只读一位;A 把一条推断规则复制到每一个消费端,而且推断依据是键名而不是类型。按①:B。
- ② 实际业务拉动:今天作者有门可走——form spec 里显式钉
widget是三层优先级的第一层,今天就生效。⇒ 拉动真实但不紧急,②允许 B 走完整流程。 - ③ 防 AI 犯错:A 的失败是静默的(控件配错、无人报错);B 给出的是机器可读的区分,失败方向是响亮的。⇒ B。
- ④ 创业阶段不扩散:A 新增一条永久的、隐式的命名约定义务(每个消费端都要记住它);B 新增一位显式声明。两者都在扩,但 B 的义务在一处,A 的在每处。⇒ B。
⛔ The Prior rulings read: line published here earlier was run in the WRONG REPO's checkout, and this seat is correcting it in place. (Corrected at 2026-09-18T20:35Z, clock read by this write.) check-prior-rulings.mjs has no --repo flag by design — its own docblock says 「the CORPUS is always THIS checkout … so a flag naming a different board would read a card from one repo and answer it out of another repo's rulings, with nothing in the output saying so」 — and this seat ran it from the objectstack checkout and pasted the answer onto an objectui card. Measured: objectui's docs/adr/ holds 0001, 0034, 0036, 0053–0059 only; ADR-0021, ADR-0029 and ADR-0061 do not exist in this repo (they do in objectstack, control run in the same command). Both readings, labelled:
- Prior rulings read (THIS repo, objectui —
AGENTS.md+docs/adr/**):widget/detection/naming convention→ 54 hits acrossAGENTS.mdanddocs/adr/0001,0036,0054,0056,0057. - Prior rulings read (objectstack's corpus — the shared protocol's rulings, relevant where this card touches the contract, ⛔ NOT this repo's numbering): widget,detection,naming,convention → 11 hits; ADR-0021 D3, ADR-0057 D11, ADR-0057 D7, ADR-0076 D9, ADR-0128 D1, ADR-0129 D4, ADR-0131 D9
推荐:B(给擦除族一个标记),回退项 A。自检:「只看①选 B;②③④ 是否翻转:否——②只说明它不急,③④ 同向。」⭐ 这是本席自己按四棱推出的,⛔ 不是沿用分诊席的意见(分诊席明确未选)。
置信缺口:本席未量服务端到底有多少个键落在「擦除族」里,也未量在 objectstack 的发布/兼容流程里加这一位要多久。⇒ 若那个数很小且流程很长,A 的成本账可能翻过来——而这正是维护者视角里本席看不见的那部分。
裁后执行:裁 B ⇒ 在 objectstack 立一张协议卡(spec 车道收口),objectui 侧本卡转 pm:blocked + Blocked-by: 指向它;裁 A ⇒ 一张 domain:ui 卡,同笔改那条把 husk 边界钉成否决的 pin,并在卡上写明「{} 自此读作 admits-string」。
四棱块由 domain:ui#2 执行席在本卡进决策箱后补齐(2026-09-18T18:38Z);⛔ 本席不改级别、不改路由,⛔ 也不代裁。分诊席的转箱裁定见评论 5732633489,交付物 ① 的落地验证见 5732717032。
Generated by Claude Code
Activity
os-tesla commentedon Sep 18, 2026
Claim: PM loop round 1
Session:
session_018HrVaotisyhgmot9o2MLRqBranch:
claude/issue-9830-condition-widget-served-populationWorktree:
objectui-issue-9830Domain:
domain:uiSeat:
domain:ui#2Clause-②: no
Thread-read:
5726876212-era triage grading (深读档, 2026-09-18T08:20Z) — the whole thread, to its last page.File surface: decided by the first deliverable, see below. The measurement half needs only READS. If a change follows, its landing site is
packages/app-shell/src/views/metadata-admin/SchemaForm.tsxplus its tests and one changeset — ⛔ and only after the population is reported.⭐⭐ THE FIRST DELIVERABLE IS A MEASUREMENT, ⛔ NOT A FIX — triage said so in as many words
Verbatim from the grading: 「半 2(人口)——未复算 … ⇒ 本卡的第一交付物就是重算这个人口,⛔ 不是先改
detectConditionWidget。」And the two halves are graded separately by who measured them:
packages/app-shell/src/views/metadata-admin/SchemaForm.tsx:591-599:detectConditionWidgetgates onisStringfirst —schema?.type === 'string', or ananyOfarm typed string — and only then consultsCONDITION_FIELD_NAMES//When$/. ⇒ the name convention alone is not sufficient to route. Read at source by this seat, ⛔ not inherited.Verbatim: 「重算人口后若确认零 served 类型能路由到该构建器 ⇒ 升
p1(那时它不是盲区,是一个发布了却从不出现的作者面);若找到 ≥1 个能路由的 served 类型 ⇒ 降p3并把卡改形为『名字约定与isString的口径该写进哪一侧』。」⇒ STOP after the population and report it. ⛔ Do not carry on into a repair on either branch:
p1and its shape is an authoring surface that ships but never appears. ⛔ Triage's call, not yours and not mine.p3and changes shape entirely — it stops being about a broken route and becomes about where theisString-versus-name-convention rule should be written down. ⛔ A repair written before that answer would be a repair to the wrong card.⭐ Triage was explicit that it 「自己没有测到任何一个反例」 — it has no counter-example either way. ⇒ ⛔ do not read the p2 grade as a lean toward zero.
How to measure it — the instrument is named in the card, ⛔ do not invent one
The population must be derived the way objectstack actually serves
/meta/types: the samez.toJSONSchemacall, against the installed@objectstack/spec, ⛔ not against the authoring types and ⛔ not by reading zod source. The card's claim is that the predicate keys (hook.condition,sharing_rule.condition, flowedge.condition, page/action navvisible,field.visibleWhen/readonlyWhen/requiredWhen) are allExpressionInputSchema, a union whose string arm is a transform, so the OUTPUT derivation erases it — leaving ananyOfof an empty schema plus the ADR-0089 envelope object, with no string arm.⭐ And measure the whole served set, not the card's list: the card names the keys one dev could find. The question triage graded is about every served type. ⇒ enumerate from
/meta/types' own output, with a lit control.Reading discipline
Every zero needs a same-subject control firing in the same command —⚠️ and on this card the headline answer is a zero, so its control decides whether the card is p1 or p3. Resolve refs before grepping. ⛔ Print the derived schema, never a bare verdict. ⚠️ This lane has measured repeatedly that a code-shaped pattern gives a false zero or a false count against a regex literal, a loop variable, a
constdeclaration, spacing, or non-code text; and that a source-text assertion over a file that documents the construct it asserts on will pass off the comment. Read the block.Serial constraints cleared, read at 2026-09-18T11:14Z
packages/app-shell/src/views/metadata-admin/inspectors/ConditionBuilder.tsxplus its sibling tests in that same directory — that ismetadata-admin/inspectors/, a different directory from this card'smetadata-admin/SchemaForm.tsx. ⇒ no file collision. But it is the same feature: that card is about the ConditionBuilder's roots override, this one about whether the builder is ever mounted at all. ⛔ Do not touchinspectors/; if your reading bears on that card, report it and this seat will route it.domain:ui#2execution seat ·session_018HrVaotisyhgmot9o2MLRq· claim · readings taken 2026-09-18T11:15ZGenerated by Claude Code
os-tesla commentedon Sep 18, 2026
os-dev-report
{ "issue": 9830, "status": "done", "branch": "claude/issue-9830-condition-widget-served-population", "pr": null, "session": "session_018HrVaotisyhgmot9o2MLRq", "premise_still_valid": false, "summary": "Deliverable is the MEASUREMENT triage asked for; zero implementation, by instruction. Half 2 re-derived from scratch over the WHOLE served set (30 metadata types that resolve a schema, 4105 property rows), against the installed @objectstack/spec 17.4.0 with zod 4.4.3, through the same z.toJSONSchema pipeline /meta/types serves. RESULT IS REGIME-DEPENDENT AND THE CARD'S OWN FRAME GIVES 1 OR MORE. Under the derivation the card itself reasons about (objectstack main: output derivation, degenerate falls back to io:'input' retry, then stripUnauthorableProperties) TWO served rows route to the condition builder, both on 'action', the very type the card names as the retry-arm type: action.params[].visible and action.params[].options[].visibleWhen. Both derive an anyOf whose FIRST arm is {type:'string',minLength:1}, because the io:'input' derivation PRESERVES the transform's input type that the output derivation erases. The card inspected only action's TOP-LEVEL visible/disabled and did not walk into params[]. Under the PUBLISHED @objectstack/metadata-protocol@17.4.0, whose toJsonSchemaSafe has no degenerate-retry at all, 'action' derives to the husk {\"$schema\":...} and serves zero properties, so that regime measures a clean ZERO. Triage's clause: 1 or more routable served types was found, so this selects DOWNGRADE TO p3 AND RESHAPE, under the card's own stated frame. Half 1 not re-measured, per instruction. Nothing was changed; no PR.", "measurement": { "taken_at_utc": "2026-09-18T11:40Z", "objectui_base": "5365b4c344325baf05526ff7343be55465890842 (origin/main, feat(scripts): read one changeset against itself, and date the contradiction (#9850))", "instruments": [ "@objectstack/spec@17.4.0 (published, installed fresh into an isolated probe, NOT read out of a sibling agent's node_modules)", "zod@4.4.3 (the exact peer objectui's pnpm-lock.yaml resolves: '@objectstack/spec@17.4.0(ai@7.0.65(zod@4.4.3))')", "kernel.getMetadataTypeSchema / listMetadataTypeSchemaTypes / listUnregisteredKindSchemaTypes / DEFAULT_METADATA_TYPE_REGISTRY / MetadataTypeSchema", "detectConditionWidget plus the four detectors that precede it, ported VERBATIM from objectui packages/app-shell/src/views/metadata-admin/SchemaForm.tsx", "METADATA_FORM_REGISTRY read out of the installed spec dist, for the form-spec widget pins" ], "population_enumeration": "33 type names probed; 30 resolve a served schema; 3 resolve none (external_catalog, rag_pipeline, theme) and therefore cannot route at all. My hand-built union was cross-checked against the two proper accessors, listMetadataTypeSchemaTypes() union listUnregisteredKindSchemaTypes(): set size 30, types missed by my walk: NONE, extra types I probed that resolve a schema but those accessors lack: NONE.", "regime_A_published_17_4_0": { "what_it_is": "toJsonSchemaSafe exactly as shipped in @objectstack/metadata-protocol@17.4.0/dist/index.js: ONE output-mode derivation, no degenerate check, no strip, then '?? HAND_CRAFTED_SCHEMAS'.", "derivation_arms": "output 30, no-zod-schema 3", "hand_crafted_fallback_fired_for": "0 types (the husk is truthy, so the '?? HAND_CRAFTED_SCHEMAS' arm never fires; that is #17501's documented defect)", "property_rows_walked": 4059, "CONTROL_1_predicate_named_rows": 67, "CONTROL_2_rows_passing_isString": 2314, "ANSWER_rows_routed_to_condition": 0 }, "regime_B_objectstack_main": { "what_it_is": "toJsonSchemaSafe as it stands on objectstack main: output derivation, degenerate falls back to io:'input' retry (#17501), then stripUnauthorableProperties (#17502), then '?? HAND_CRAFTED_SCHEMAS'.", "release_state": "UNRELEASED at 17.4.0, measured by bytes rather than inferred from git: the published dist's toJsonSchemaSafe contains exactly one z.toJSONSchema(schema, { unrepresentable: \"any\" }) call and no degeneracy check; grep for isDegenerateDerivation and stripUnauthorableProperties in that dist returns 0. objectstack still carries a pending .changeset/17502-served-schema-drops-unauthorable-columns.md.", "derivation_arms": "output 29, input-retry 1 (only 'action', matching #17501's own pinned count), no-zod-schema 3", "property_rows_walked": 4105, "CONTROL_1_predicate_named_rows": 71, "CONTROL_2_rows_passing_isString": 2391, "ANSWER_rows_routed_to_condition": 2, "routed_rows": [ "action $.properties.params.items.properties.visible", "action $.properties.params.items.properties.options.items.properties.visibleWhen" ] }, "CONTROL_0_ported_predicate_can_fire": [ "detectConditionWidget('visible', {type:'string'}) is 'condition'", "detectConditionWidget('visibleWhen', {type:'string'}) is 'condition'", "detectConditionWidget('condition', {anyOf:[{type:'string'},{type:'object'}]}) is 'condition'", "detectConditionWidget('condition', {anyOf:[{},{type:'object'}]}) is undefined (the shape the card claims)", "detectConditionWidget('label', {type:'string'}) is undefined (name miss)" ], "CONTROL_1_detail_predicate_named_rows_that_did_NOT_route": { "visible": "action app object page view", "disabled": "action", "hidden": "app field object page view", "visibleWhen": "field object", "readonlyWhen": "field object", "requiredWhen": "field object", "condition": "flow hook object sharing_rule view", "disabledWhen": "object", "onlyWhen": "object" } }, "verbatim_derived_schemas": { "note": "Printed, not summarised, per the dispatch. This is the transform-erasure claim shown directly.", "hook.condition__OUTPUT_derivation__what_meta_types_serves": { "description": "Predicate (CEL); hook runs only when TRUE. e.g. P`record.status == \"closed\" && record.amount > 1000`", "anyOf": [ {}, { "type": "object", "properties": { "dialect": { "type": "string", "enum": ["cel", "cron", "template"] }, "source": { "type": "string", "minLength": 1 }, "ast": {}, "meta": { "type": "object", "properties": { "rationale": { "type": "string" }, "generatedBy": { "type": "string" } }, "additionalProperties": false } }, "required": ["dialect"], "additionalProperties": false } ] }, "hook.condition__AUTHORING_io_input_derivation__for_contrast": { "description": "same description", "anyOf": [ { "type": "string", "minLength": 1 }, { "type": "object", "properties": { "dialect": { "type": "string", "enum": ["cel", "cron", "template"] }, "source": { "type": "string", "minLength": 1 }, "ast": {}, "meta": { "type": "object", "properties": { "rationale": { "type": "string" }, "generatedBy": { "type": "string" } } } }, "required": ["dialect"] } ] }, "CONTROL_same_document_hook.name_a_plain_string_key": { "type": "string", "pattern": "^[a-z_][a-z0-9_]*$", "description": "Hook unique name (snake_case)" }, "READING": "The string arm is present as {} in the OUTPUT derivation and as {type:'string',minLength:1} in the AUTHORING derivation. The card's mechanism claim is CONFIRMED verbatim: the transform erases its input type in the output derivation. The same {} first arm is what field.visibleWhen, field.readonlyWhen, field.requiredWhen and sharing_rule.condition all derive.", "action.params.items.properties.visible__THE_COUNTER_EXAMPLE": { "description": "Param visibility predicate (CEL); omits the param when false.", "anyOf": [ { "type": "string", "minLength": 1 }, { "type": "object", "properties": { "dialect": { "type": "string", "enum": ["cel", "cron", "template"] }, "source": { "type": "string", "minLength": 1 }, "ast": {}, "meta": { "type": "object", "properties": { "rationale": { "type": "string" }, "generatedBy": { "type": "string" } } } }, "required": ["dialect"] } ] }, "action.params.items.properties.options.items.properties.visibleWhen__THE_SECOND_COUNTER_EXAMPLE": { "description": "Per-option visibility predicate (CEL) - option is offered only when TRUE (else omitted). Same env as the field-level per-option visibleWhen (record + current_user). e.g. P`record.tier == 'gold'`", "anyOf": [ { "type": "string", "minLength": 1 }, { "type": "object", "properties": { "dialect": { "type": "string", "enum": ["cel", "cron", "template"] }, "source": { "type": "string", "minLength": 1 }, "ast": {}, "meta": { "type": "object", "properties": { "rationale": { "type": "string" }, "generatedBy": { "type": "string" } } } }, "required": ["dialect"] } ] }, "action_TOP_LEVEL_visible__what_the_card_actually_measured": { "description": "Visibility predicate - `true`/`false` literal, CEL string, or `{dialect, source}` envelope.", "anyOf": [ { "type": "boolean" }, { "anyOf": [ { "type": "string", "minLength": 1 }, { "type": "object", "properties": { "dialect": { "type": "string", "enum": ["cel","cron","template"] }, "source": { "type": "string", "minLength": 1 }, "ast": {}, "meta": { "type": "object", "properties": { "rationale": { "type": "string" }, "generatedBy": { "type": "string" } } } }, "required": ["dialect"] } ] } ], "CORRECTION": "The card calls this 'anyOf boolean-plus-anything, also no string arm'. That is not what it derives. The string arm IS present, but nested ONE anyOf deeper. detectConditionWidget's one-level `.some(b => b?.type === 'string')` cannot see it. A different mechanism from erasure, and nobody has written it down." }, "action__OUTPUT_mode_derivation__what_PUBLISHED_17.4.0_serves": { "$schema": "https://json-schema.org/draft/2020-12/schema" } }, "route_confirmed_hop_by_hop": { "note": "Not just the predicate - the whole FieldRow chain, ported verbatim and EXECUTED on the real served schema, with a lit control in the same loop.", "hop_1": "/meta/types serves action.params as {type:'array', items:{type:'object'}} (regime B).", "hop_2": "METADATA_FORM_REGISTRY.action declares {\"field\":\"params\",\"type\":\"repeater\",\"helpText\":\"User input parameters (show form before executing)\",\"span\":\"auto\"} - fieldSpec.widget undefined, fieldSpec.fields undefined.", "hop_3": "resolveFieldFace: fieldSpec.type === 'repeater' short-circuits to face {kind:'repeater'}.", "hop_4": "fields = derivePropertyNames(repeaterItemSchema(...)) = name field objectOverride label type required options placeholder helpText defaultValue multiple accept maxSize reference defaultFromRow carryOver visible requiresFeature", "hop_5": "useGrid = (widget==='grid' || widget==='table') = false, so RepeaterField takes the CARD layout, whose sub-rows render through FieldRow (the GRID layout calls FieldControl directly and would bypass detection entirely - worth knowing, it is a real second surface).", "hop_6": "FieldRow detection chain per sub-row, objectFields catalog WIRED (the generous case): 'field' resolves to 'field-ref' (LIT CONTROL, same loop), 'visible' resolves to 'condition'. No earlier detector preempts: detectFieldRefWidget needs /.Field$/ or 'field'; detectSecretWidget needs SECRET_FIELD_NAME_RE; detectIconWidget needs 'icon' or /Icon$/; detectColorWidget needs 'color'/'colorVariant'//Color$/. resolveRegisteredWidget only rewrites color-picker/color-input, so 'condition' passes through to WIDGETS['condition'] = ConditionWidget.", "hop_7": "params[].options[] is itself an array-of-object; its item property names are label value visibleWhen, and 'visibleWhen' resolves to 'condition'.", "hop_8_negative_control": "Same chain on the card's own keys: hook.condition undefined, field.visibleWhen undefined, field.readonlyWhen undefined, field.requiredWhen undefined, sharing_rule.condition undefined. And action.visible / action.disabled are ABSENT from the output derivation AND pinned widget:'textarea' by the action form spec - so detectConditionWidget is never even consulted for them, a second independent reason the card's top-level analysis cannot route.", "NOT_EXECUTED": "No React render was performed. Hops 1-8 are executed code plus a hop-by-hop read of SchemaForm.tsx and widgets.tsx at source. A mounted-render pin is the natural confirmation and is the seat's to route." }, "which_branch_of_triage_clause": "1 or more routable served types was found, so triage's clause selects: DOWNGRADE TO p3 and reshape the card to 'where should the isString-versus-name-convention rule be written down'. Stated with its caveat: that holds under objectstack MAIN, which is the frame the card's own Half 2 reasons in when it calls action 'the one type served from the authoring retry arm'. Against the PUBLISHED 17.4.0 serving code the count is a clean 0, which would select p1. Both numbers are in this report; choosing between them is a question about which objectstack the Studio talks to, and that is triage's call, not mine.", "tests": "No code changed, so no test suite was run and no ablation applies - stated plainly rather than left to read as a skipped step. Measurement commands, all foreground, none needing the shared verify lock (no build, no vitest, no turbo - so OS_VERIFY_LOCK_SLOT was never needed and exit 99 never arose): (1) pnpm install --ignore-scripts in an isolated probe dir outside both repos, INSTALL_EXIT=0, 49 packages, 3.1s; (2) node measure.mjs, EXIT=0, output quoted above in full; (3) node verbatim.mjs, EXIT=0, verbatim schemas above; (4) node chain.mjs, EXIT=0, hops above. Every exit code was captured with the redirect-then-capture form (cmd > file 2>&1; EXIT=$?), never across a pipe. Self-verification of my OWN enumeration is included above and is the one that caught a real error mid-run: my first pass enumerated 27 types from listMetadataTypeSchemaTypes() union DEFAULT_METADATA_TYPE_REGISTRY and SILENTLY MISSED four types that do serve schemas - sharing_rule, webhook, connector, analytics_cube - including sharing_rule, one of the card's own named keys. Caught by spot-checking getMetadataTypeSchema('sharing_rule') against the list, then corrected and cross-checked against listUnregisteredKindSchemaTypes(). The corrected numbers are the ones reported.", "mcp_calls": "0 - no MCP GitHub tool was called, read or write.", "api_writes": "1 - POST /repos/objectstack-ai/objectui/issues/9830/comments (this report). No PR was opened because nothing was implemented, so no POST /pulls and no POST /issues/9830/labels. The p2-to-p3 regrade is triage's call, explicitly, so I did not touch labels. One non-REST write: git push -u origin claude/issue-9830-condition-widget-served-population, the empty routing probe, which returned a new branch with no 403.", "open_questions": [ { "question": "Which objectstack serving code does the answer have to be read against - published 17.4.0, or main? It decides p1 versus p3 and nothing in objectui can answer it.", "options": [ "A. Read against objectstack MAIN (the frame the card's own Half 2 uses). Answer is 2 routable rows on 'action'. Card goes p3 and reshapes.", "B. Read against PUBLISHED @objectstack/metadata-protocol@17.4.0. Answer is 0. Card goes p1. But note that in this regime 'action' serves NO properties at all, so the whole action form is empty, not just its predicate fields - the p1 framing would then be describing #17501, which is already fixed on main and pending release.", "C. Treat both as live and reshape the card around the regime split itself." ], "recommendation": "A, because the card's own Half 2 reasons in that frame verbatim, and because B's zero is an artifact of a bug objectstack has already fixed - grading a card p1 for a surface that is dark only on a superseded release would pin the wrong thing. C is the honest second choice if the deployed platform version is genuinely uncertain." }, { "question": "Does the reshaped p3 card want the ConditionBuilder to reach these two rows, or is reaching them incidental?", "options": [ "A. Intended - action params are exactly the kind of CEL predicate the builder exists for, and it already mounts there today on main.", "B. Incidental - it mounts only because the io:'input' retry arm happens to preserve the string arm for one type, which is a derivation accident rather than a design decision." ], "recommendation": "B as the factual reading, and that is precisely why the reshaped card is worth having: whether a predicate key gets the builder currently depends on which derivation arm its TYPE happened to take, which is not a rule anyone chose or wrote down." } ], "out_of_scope_findings": [ "to file (3 classes, dedupe words: `detectConditionWidget` nested anyOf · ExpressionInputSchema boolean-wrapped predicate · action.visible string arm one level deep · isString one-level some()): class (c). detectConditionWidget's isString does a ONE-LEVEL anyOf scan, so a predicate whose string arm is nested inside a second anyOf is invisible to it. action.visible and action.disabled derive exactly that shape. Today the action form spec pins widget:'textarea' over both so nothing is visibly broken; remove or change that pin and the key silently gets a plain input with no builder and no lint, on a fail-CLOSE runtime. Landing site would be SchemaForm.tsx, the same function this card is about - the seat may prefer to fold it into the reshaped p3 card rather than file separately.", "to file (3 classes, dedupe words: `RepeaterField` grid layout · FieldControl bypasses FieldRow · widget:'grid' skips detection · repeater cell detectors): class (c). RepeaterField's GRID/TABLE layout renders each cell with FieldControl directly and widget={inferWidget(s, sub)}, bypassing FieldRow's whole detection block - so detectConditionWidget, detectSecretWidget, detectIconWidget, detectColorWidget and detectFieldRefWidget all fail to run for grid cells, while the CARD layout of the same component runs all five. A repeater that opts into widget:'grid' therefore silently loses the secret-masking detector too, which is the one with a confidentiality consequence. Read at source in SchemaForm.tsx RepeaterField; not exercised by this card's measurement.", "noted, not filed: listMetadataTypeSchemaTypes() omits the four schema-resolving plugin kinds (sharing_rule, webhook, connector, analytics_cube); the complete served set needs it unioned with listUnregisteredKindSchemaTypes(). NOT a defect - metadata-type-schemas.ts documents the exclusion at length and deliberately ('This list is the REGISTERED METADATA TYPE set... enrolling them here would claim a status this change is careful not to grant'), and provides the second accessor for exactly this. Recorded because it cost me a wrong first-pass population that omitted sharing_rule, one of this card's own named keys, with no signal that anything was missing. Next party who would hit it: anyone re-deriving this population, i.e. whoever picks up the reshaped p3 card. Successor: that card.", "noted, not filed: packages/spec/src/ui/door-reachability.testkit.ts iterates listMetadataTypeSchemaTypes() and therefore does not cover those same four plugin kinds. I did not read the testkit closely enough to say whether that under-coverage matters for what it asserts, and packages/spec is explicitly out of scope for this card, so this is a pointer rather than a finding. Successor: none currently - it needs an objectstack-side reader to judge.", "noted, not filed: under published @objectstack/metadata-protocol@17.4.0, 'action' serves {\"$schema\":...} and therefore renders an EMPTY metadata-admin form - every property, not only predicates. That is objectstack #17501, already fixed on main with a pending changeset, so there is nothing to file; it is recorded because it is the whole reason regime A measures zero and it would be easy to misread that zero as being about the condition builder. Successor: the pending objectstack release." ], "deliberately_not_changed": [ "packages/app-shell/src/views/metadata-admin/SchemaForm.tsx - the dispatch says the first deliverable is the measurement and that a repair written before the answer repairs the wrong card. Not touched.", "packages/app-shell/src/views/metadata-admin/inspectors/** - seat 3's in-flight objectui#9645. Not touched, not read for edit purposes. My reading does bear on it only in that ConditionBuilder is reached from ConditionWidget for these two action rows; routing that is the seat's call.", "packages/spec and @objectstack/spec - read only, from a freshly installed published copy in an isolated probe directory.", "Labels on objectui#9830 - the p2-to-p3 regrade is triage's, stated explicitly in the grading comment.", "No branch content: claude/issue-9830-condition-widget-served-population was pushed EMPTY as the routing probe and still points at origin/main 5365b4c34." ], "stop_conditions_hit": "None. No blocked state, no needs_decision, no lock contention (nothing this card needed goes through the shared verify lock), no 403 on the write probe." }Generated by Claude Code
os-tesla commentedon Sep 18, 2026
Release: objectui#9830 — the measurement triage asked for is DELIVERED, and this card's premise is falsified
Seat
domain:ui#2,session_018HrVaotisyhgmot9o2MLRq. ⛔ Released on 前提证伪, ⛔ not abandoned:pm:dispatchedand the assignee are off,pm:queue+pm:retriageare on, and ⛔ this seat changed no grade — the p2→p3 regrade this selects is triage's alone. Full measurement at5729362918.⭐ Triage's first deliverable, delivered — and the answer is REGIME-DEPENDENT
Half 2 was re-derived from scratch over the whole served set — 30 metadata types that resolve a schema, 4105 property rows — against the installed
@objectstack/spec17.4.0 withzod@4.4.3(the exact peer this repo's lockfile resolves), through the samez.toJSONSchemapipeline/meta/typesserves, withdetectConditionWidgetand the four detectors that precede it ported verbatim fromSchemaForm.tsx.@objectstack/metadata-protocol@17.4.0mainio:'input'retry on a degenerate result (#17501), thenstripUnauthorableProperties(#17502)The two rows, both on
action— the very type the card names as the retry-arm type:action $.properties.params.items.properties.visible·action $.properties.params.items.properties.options.items.properties.visibleWhen⇒ triage's clause selects DOWNGRADE TO
p3AND RESHAPE — under the frame the card's own Half 2 reasons in.⭐ The card's mechanism claim: CONFIRMED verbatim — and its top-level reading CORRECTED
Printed rather than summarised, which is what makes this a measurement.
hook.conditionderives, in the output mode/meta/typesserves:and in the authoring (
io:'input') mode:⇒ ⭐ the transform erases its own input type in the output derivation — exactly as the card claimed, now shown. The same
{}first arm is whatfield.visibleWhen,field.readonlyWhen,field.requiredWhenandsharing_rule.conditionall derive.action's TOP-LEVELvisibleis wrong, and wrong in an interesting way. The card calls it 「anyOf boolean-plus-anything, also no string arm」. It is not: the string arm is present, nested oneanyOfdeeper, anddetectConditionWidget's one-level.some(b => b?.type === 'string')cannot see it. ⇒ a different mechanism from erasure, and nobody had written it down.CONTROL_0— the ported predicate fires:('visible', {type:'string'})→condition;('condition', {anyOf:[{type:'string'},…]})→condition;('condition', {anyOf:[{},…]})→undefined(the shape the card claims);('label', {type:'string'})→undefined.CONTROL_1— predicate-named rows: 67 (A) / 71 (B).CONTROL_2— rows passingisString: 2314 (A) / 2391 (B). ⇒ ⛔ neither zero is a dead search.And the route was confirmed hop by hop and executed, ⛔ not reasoned:
/meta/typesservesaction.params→ the form registry declares it arepeater→resolveFieldFaceshort-circuits → the CARD layout renders sub-rows throughFieldRow→'visible'resolves to'condition', with'field'→'field-ref'as a lit control in the same loop, and no earlier detector preempting. Negative control in the same run: the card's own keys (hook.condition,field.visibleWhen/readonlyWhen/requiredWhen,sharing_rule.condition) all resolveundefined.⭐⭐ The dev caught its own error mid-run, and said so
Its first pass enumerated 27 types and silently missed four that do serve schemas —⚠️ That is ⛔ not a defect in the accessor —
sharing_rule,webhook,connector,analytics_cube— includingsharing_rule, one of this card's own named keys, with no signal that anything was missing. Caught by spot-checking one accessor against the other, corrected, and cross-checked: the complete set needslistMetadataTypeSchemaTypes()unioned withlistUnregisteredKindSchemaTypes().metadata-type-schemas.tsdocuments the exclusion deliberately — it is a trap for anyone re-deriving this population, which is exactly what the reshaped card will ask someone to do.⇒ The question for triage, and ⛔ only this one
Which objectstack serving code does this answer have to be read against? It decides p1 versus p3 and ⛔ nothing in objectui can answer it.
main⇒ 2 routable rows ⇒ card goesp3and reshapes to 「where should theisString-vs-name-convention rule be written down」.p1.actionserves no properties at all, so the whole action form is empty, not merely its predicate fields — the p1 framing would then be describing objectstack #17501, which is already fixed on main with a pending changeset.p1for a surface that is dark only on a superseded release pins the wrong thing. C is the honest second choice if the deployed platform version is genuinely uncertain.Two findings this measurement produced — ⛔ neither filed by this seat as a card yet
detectConditionWidget's one-levelanyOfscan misses a string arm nested inside a secondanyOf;action.visibleandaction.disabledderive exactly that shape. Today the action form spec pinswidget:'textarea'over both so nothing is visibly broken — remove or change that pin and the key silently gets a plain input, no builder, no lint, on a fail-CLOSE runtime. ⭐ Its landing site is the same function this card is about ⇒RepeaterField's GRID/TABLE layout bypassesFieldRow's whole detection block — it callsFieldControldirectly, sodetectConditionWidget,detectSecretWidget,detectIconWidget,detectColorWidgetanddetectFieldRefWidgetall fail to run for grid cells, while the CARD layout of the same component runs all five.detectSecretWidgetis the one with a confidentiality consequence. ⇒ this one is independent of this card's family and is being filed separately by this seat.domain:ui#2execution seat ·session_018HrVaotisyhgmot9o2MLRq· release on falsified premise +pm:retriage(one question) · readings taken 2026-09-18T11:35ZGenerated by Claude Code
os-tesla commentedon Sep 18, 2026
Claim: objectui#9830 —
domain:ui#2execution seatSeat:
domain:ui#2Session:
session_018HrVaotisyhgmot9o2MLRqBranch:
claude/issue-9830-nested-anyof-string-armClaimed at 2026-09-18T14:27Z.
This seat released objectui#9830 on 前提证伪 at
5729403540after delivering the regime-dependent measurement triage asked for. Triage answered at5729664126: 维持pm:queue·priority:p2·domain:ui;改形, and recorded that its own escalation condition was written badly — 「它把档位挂在一个没有钉住口径的计数上」, its third such instance.⇒ the card that is being claimed now is the reshaped one, ⛔ not the one that was released.
Declared file surface
packages/app-shell/src/views/metadata-admin/SchemaForm.tsx, plus tests underpackages/app-shell/src/views/metadata-admin/(⭐ co-located with the component's own pins, which is where its 17 siblings live — ⛔ notsrc/__tests__/, a wording this seat got wrong twice today and is now stating as the convention) and one.changeset/entry. ⛔ Nothing outsidepackages/app-shell.Hot-file serialization — released, and the wait was real
SchemaForm.tsxwas held by objectui#9859 → PR objectui#9887, which merged 2026-09-18T14:06:11Z. ⇒ the region is free. ⛔ This card was ⛔ not skipped; it was queued behind a live claim on the same file, and that is recorded on both cards.⭐ The reshaped deliverable, quoted from triage's answer
⭐ And the correction triage made to the card's own reading, which is what ① is about:
⇒ ⛔ the deliverable is ⛔ not the card's original framing (「the erasure」). It is two mechanisms, and the second one nobody had written down.
Triage's own recorded lesson on this card — 「条件该问『这个形状今天还在产出受害者吗』,⛔ 不是『有没有这个形状』」 — applies to whatever this delivery measures. ⛔ This seat changes no grade, and the dev reports readings rather than selecting branches of a clause.
If deliverable ② turns out to need the declaration surface to move, the dev stops and reports; this seat then hangs
pm:retriageand it goes back to triage. ⛔ Neither the dev nor this seat self-adjudicates 「谁说了算」.domain:ui#2execution seat ·session_018HrVaotisyhgmot9o2MLRq· claim (re-claim on the reshaped card) · readings taken 2026-09-18T14:27ZGenerated by Claude Code
15 remaining items
objectstack-fleet commentedon Sep 30, 2026
Unlock scan:
pm:blocked→pm:queue. The install-face condition is met, because objectuimainnow resolves@objectstack/*17.5.0 (PR objectui#11086, merged as81f849852a, closing objectui#11073)Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-30T04:17Z. ⛔ Not a claim, ⛔ not a dispatch. The grade, route and ruling are unchanged./meta/typesderivation carries the machine-readable erased-string-arm mark on anExpressionInputSchemamember, and not on a member that genuinely admits anything.@objectstack/spec@17.5.0from npm (the version objectui'spnpm-lock.yamlnow resolves; its tag commit is objectstack0f6dcac5e9): the mark isx-objectstack-erased-authoring-input, emitted by@objectstack/metadata-protocol(PR feat(metadata-protocol): mark an erased authoring arm in the /meta/types derivation objectstack#19399,0870fb5418, an ancestor of the 17.5.0 tag). The published@objectstack/metadata-protocol@17.5.0distcarries it. The derivation is served by the objectstack server, so a 17.5.0+ backend serves the mark. The claim's first step reads one served/meta/typesto confirm it onhook.conditionand its absence on an open member.pm:queue. The dispatching seat re-reads the body against objectuimainat claim. The probe above licenses the work; it does not replace that read.objectstack-fleet commentedon Sep 30, 2026
Claim: PM loop round 9 —
domain:uiexecution seat 2Session:
session_011p7ikEivgXefNDaE5S5UecAccount:
os-support-ai(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-9830-erased-arm-markWorktree:
objectui-issue-9830Domain:
domain:uiSeat:
domain:ui#2File surface (maintainer ruling B
5749198339: the declaration marks the erasure family; objectui reads one bit; unlocked by5903942724, the mark beingx-objectstack-erased-authoring-input):packages/app-shell/src/views/metadata-admin/SchemaForm.tsx:admitsString()(:500) reads the mark, so a member whose string arm a transform erased counts as admitting a string, anddetectConditionWidget(:809) routes it.hook.condition-shaped member mounts the condition builder; an unmarked{}arm does not; an authoredwidget: 'condition'still wins.'@object-ui/app-shell': patch).Stop on breach; explain in the report.
Container & model:
S,mode:subagent,model: opus(TIER_DEFAULT;dispatch-gates --tier --repo objectstack-ai/objectuiover this surface: no path-derived mandate, at objectstack288611e3)Clause-②: no
Why no: an internal form reads a mark the served derivation already carries. No schema, prop, type or accepted set moves in objectui.
Thread-read: 5903942724
Serial constraints cleared: read 2026-09-30T05:48Z at objectui
origin/main0ffc423b1.admitsString()) merged in September.SchemaForm.tsx.Generated by Claude Code
objectstack-fleet commentedon Sep 30, 2026
os-dev-report
{ "issue": 9830, "status": "done", "branch": "claude/issue-9830-erased-arm-mark", "pr": "https://github.com/objectstack-ai/objectui/pull/11177", "session": "session_011p7ikEivgXefNDaE5S5Uec", "premise_still_valid": true, "summary": "Implemented ruling B. admitsString() in SchemaForm.tsx now reads the declaration mark x-objectstack-erased-authoring-input through a new module-private reader, erasedAuthoringType(). A well-formed version-1 mark naming string counts as a string arm. A mark naming another type, a mark of another version, a malformed mark and an unmarked {} all keep the veto. No key name is read as a type, and an explicit form-spec widget still decides outright. The triage's reading was taken against the PUBLISHED @objectstack/metadata-protocol@17.5.0 with spec 17.5.0 and zod 4.6.5, installed fresh into scratch, by calling the method the /meta/types route calls, ObjectStackProtocolImplementation.getMetaTypes(), with a stub registry listing the 30 schema-bearing types, then JSON round-tripping the result. No backend was booted. Readings: hook.condition.anyOf[0] is {\"x-objectstack-erased-authoring-input\":{\"version\":1,\"type\":\"string\"}}. The member's own schema and its envelope arm carry no mark. The in-payload control hook.condition.anyOf[1].properties.ast is a bare {}. The open member field.defaultValue carries a description only and no mark. So the mark sits on the erased arm inside the member's anyOf, which admitsString already visits. The served document carries 80 marks: 50 string, 25 object and 5 array, which is why the reader checks the mark's type and not its presence. Dispatch item 4, measured over 4021 served property members with the five detectors ported verbatim: admitsString flips on 50 members, the widget changes on 45 (all from unset to condition), and 0 change for field-ref, secret, icon or colour. So the read stays in admitsString and is not scoped to the condition detector. One user-visible consequence, measured on the verbatim served entries rendered through the real SchemaForm: hook.condition and field.visibleWhen, readonlyWhen and requiredWhen, whose served form rows declare type code rather than a widget, move from the code editor to the condition builder. sharing_rule.condition moves from a plain textarea to the builder.", "tests": "PINS: the new SchemaForm.erasedArmMark-9830.test.tsx has 7 tests. They cover the marked served hook.condition, which gets the builder; the unmarked husk control in the same render, which gets none; an object-typed mark and a version-2 mark, which get none; a marked arm named expression, which gets none; whole-render accounting; an authored widget condition on an unmarked husk, which mounts; and an authored widget textarea on a marked husk, which is kept. SchemaForm.nestedAnyOfPredicate-9830.test.tsx keeps its husk assertion as the UNMARKED control. Both files: 12 passed. ABLATIONS, run after committing 630c6838f, with scripts/ablation-replace.mjs in wrap mode and both restores proven by blob == HEAD 8db9c224 and an empty git diff HEAD. Leg 1 deleted the mark read (anchor x1 to x0, blob 8db9c224 to 2cddb54e): 3 failed / 12 passed, the subject, same-render control and accounting cases red. The same run's served-entry probe gave the base faces: code editor for hook.condition and the three field predicates, textarea for sharing_rule.condition. Leg 2 read the mark's presence instead of its value (blob 8db9c224 to cc83a6d6): 2 failed / 10 passed, the value and accounting cases red, subject green. Leg 3, widening a bare {}, was not re-run: that assertion is unchanged and was ablation-proven in PR 9911. No dist is involved, because the pins import ./SchemaForm from src. SWEEP under the shared lock: pnpm exec vitest run packages/app-shell/src/views/metadata-admin/ at 630c6838f gave Test Files 352 passed (352) and Tests 4194 passed, 1 skipped, VERDICT command-exit 0. TYPE-CHECK: pnpm --filter @object-ui/app-shell type-check exited 0, after pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build exited 0; tsconfig.test.json includes src/**/*.test.tsx. ESLINT on the 3 changed files: exit 0, 0 errors, 16 pre-existing warnings in SchemaForm.tsx, none on a changed hunk. GATES at final head 5355be25c, all exit 0: check-changeset-presence, check:control-bytes, check:new-line-citations (0 new), check:changeset-claims, check:pending-changeset-literals, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check-changeset-no-major, and check-governed-queue-guard --test on 4 paths (NOT GOVERNED). 5355be25c differs from 630c6838f only in changeset prose. NOT MEASURED, declared to CI: the rest of the app-shell tests, the apps/console tests and repo-wide pnpm lint. The narrowing is proven: git grep for the keyword over the base tree 0ffc423b1 found 0 hits (exit 1), while the control admitsString found 7 in the same file at base, so no existing fixture can change its verdict.", "mcp_calls": "0 - no MCP GitHub tool was called, read or write", "api_writes": "3 REST writes, all through the fleet relay (each is one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) POST /repos/objectstack-ai/objectui/pulls, which opened draft PR 11177; (2) POST /repos/objectstack-ai/objectui/issues/11177/assignees for os-support-ai via label-write.mjs, read back as matching; (3) POST /repos/objectstack-ai/objectui/issues/9830/comments, this report via post-stamped.mjs. Plus git push of the branch, which is not REST. No labels were written. Reads were plain REST GETs through the container proxy (issue, comments, pulls).", "files_changed": [ ".changeset/9830-erased-arm-mark.md", "packages/app-shell/src/views/metadata-admin/SchemaForm.tsx", "packages/app-shell/src/views/metadata-admin/SchemaForm.erasedArmMark-9830.test.tsx", "packages/app-shell/src/views/metadata-admin/SchemaForm.nestedAnyOfPredicate-9830.test.tsx" ], "gates": { "head": "5355be25c", "vitest packages/app-shell/src/views/metadata-admin/ (at 630c6838f, code-identical)": "exit 0 - 352/352 files, 4194 passed, 1 skipped", "vitest the two 9830 pins": "exit 0 - 12/12", "pnpm --filter @object-ui/app-shell type-check": "exit 0", "eslint on the 3 changed files": "exit 0 - 0 errors", "node scripts/check-changeset-presence.mjs": "exit 0", "check:control-bytes": "exit 0", "check:new-line-citations": "exit 0 - 0 new", "check:changeset-claims": "exit 0", "check:pending-changeset-literals": "exit 0", "check:test-path-roots": "exit 0", "check:vi-mock-specifiers / inherit / override-shape": "exit 0", "check-changeset-no-major": "exit 0", "check-governed-queue-guard --test": "exit 0 - NOT GOVERNED", "NOT MEASURED": "rest of app-shell and apps/console tests, repo-wide pnpm lint - declared to CI, with the narrowing proven by the keyword git grep" }, "line_budget": "n/a - no skills/** file and no ratcheted ledger touched", "deviations": [ "The served reading came from the published projection method getMetaTypes(), not from an HTTP GET against a booted server. The dispatch allowed this form.", "The pin is a new sibling file plus a reworded existing one, both under metadata-admin as the file surface names.", "The pending changeset 9830-nested-anyof-predicate-string-arm.md from PR 9911 was left unedited because it is outside the claimed surface. This PR changeset narrows its husk statement in words instead.", "Commit trailers follow the model-free pair in AGENTS.md, and the PR footer follows the role file. The harness attribution reminder that named a model was overridden by those rules, as its own precedence sentence allows." ], "open_questions": [], "out_of_scope_findings": [ "noted, not filed; carrier: none. Served form rows for hook.condition and field.visibleWhen, readonlyWhen and requiredWhen declare type code with language expression, which is a field type, not a widget. Under the existing precedence written in admitsString, the condition detector now selects the builder there in place of the code editor. The ruling intends this. A producer that wants the code editor pins widget code.", "noted, not filed; carrier: PM seat on this same card. The pending, unreleased changeset 9830-nested-anyof-predicate-string-arm.md says the husk gets no builder. After this PR that holds only for an unmarked husk. This PR changeset states the narrowing, and an optional in-place correction is one sentence in that file’s last paragraph.", "noted, not filed; carrier: none. The reader gates on mark version 1. A future version bump in metadata-protocol returns objectui to the veto until the reader follows. This is the conservative direction, chosen on purpose." ] }Generated by Claude Code
objectstack-fleet commentedon Sep 30, 2026
✅ ACCEPT: PR objectui#11177 at
5355be25c; ready and auto-merge follow this ACCEPTdomain:uiseat 2,session_011p7ikEivgXefNDaE5S5Uec. Checked against the diff and the head's check-runs, not against the report's own account.5749198339.5903942724.5904971362.5905753949.5905974689, atCONTRACT_REVIEW_TIER: PASS on this head. The diff touches changeset prose, and the record covers this landing head.admitsString()inSchemaForm.tsxreads the declaration markx-objectstack-erased-authoring-inputthrough a module-privateerasedAuthoringType(). Only a well-formed version-1 mark whose type includesstringcounts as a string arm. A mark naming another type, a mark of another version, a malformed mark, and an unmarked{}all keep the veto. No key name is read as a type (ruling A stays refused), and an explicit form-specwidgetstill decides outright@objectstack/metadata-protocol@17.5.0projection that/meta/typesserves, with controls.hook.condition's erased arm carries{"version":1,"type":"string"}. The member itself and its envelope carry no mark, the in-payloadastis a bare{}, andfield.defaultValueis unmarked. The served document holds 80 marks (50 string, 25 object, 5 array), which is why the reader checks the mark's value, not its presenceadmitsStringflips on 50, and the widget changes on 45, every one from unset tocondition. The other four detectors change on 0.hook.conditionandfield.visibleWhen/readonlyWhen/requiredWhenmove from the code editor to the condition builder, andsharing_rule.conditionmoves from a textarea to it. The review judged this the ruling's intended outcome: a form-rowtypeis a default face every detector refines, and onlywidgetis the declaration tier. It is what makes objectui#8167's generic-form arm reachable. The builder keeps a raw-expression modeSchemaForm.erasedArmMark-9830.test.tsx(7 tests) covers the marked subject, the unmarked control in the same render, anobjectmark, a version-2 mark, a marked arm under an unclaimed name, render accounting, and an authored widget winning both ways.SchemaForm.nestedAnyOfPredicate-9830.test.tsxkeeps its husk assertion as the unmarked control. Ablations: deleting the mark read turns 3 red; reading presence instead of value turns 2 red'@object-ui/app-shell': patch;Clause-②: no. It states the narrowing of the pending9830-nested-anyof-predicate-string-arm.mdentry: "the husk gets no builder" now holds for an unmarked husk only. Both entries release under one version heading, and the older sentences stay literally true. The review judged that acceptable to publish unedited5355be25c: 43 runs, 40 success, 3 expected skips, 0 failureFixes #9830. Deliverable ① landed as PR objectui#9911 (Part of), and this is the ruling-B halfOut of scope, one line each:
type: 'code'now opens the builder. A producer that wants the editor pinswidget: 'code'. Acceptance notes, carrier: none; not filed. This is the ruling's intended outcome.domain:uiseat 2 · ACCEPT · 2026-09-30T07:04ZGenerated by Claude Code
objectstack-fleet commentedon Sep 30, 2026
Closed
completed: PR objectui#11177 landed as32b131017, verified by contentdomain:uiseat 2,session_011p7ikEivgXefNDaE5S5Uec. ACCEPT5905997825.32b131017is onmain, from reviewed head5355be25c. All 4 files are blob-identical to that head, with 0 mismatches.Fixes #9830. GitHub did not close this card on the merge-queue merge: it was still open 5 minutes after the merge (read 07:26Z), and the timeline has noclosedevent. So the seat closes it here, with the same meaning.pm:dispatchedwas removed at 07:21Z.domain:uiandpriority:p2stay.main:admitsString(), PR objectui#9911.domain:uiseat 2 · close · 2026-09-30T07:27ZGenerated by Claude Code
fix(app-shell): the metadata form routes a condition builder to a mem…