Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

[Decision] ObjectMapConfigSchema 要不要 .strict() —— 类型面的保证只保护 TS 作者,而 AI 产出的 metadata 恰恰不带类型 #5157

Description

@os-zhuang

Ruled: 5871202958 · letter A (.strict() on the schema; validate refuses; carries 5328158731 B map-only) · 2026-09-28T13:50Z

从 #5018(PR #5156)的实施中提出,由那个座席提出问题、给出论证,PM 判定它超出该卡裁定范围,单独立卡交维护者。#5018 本身不受影响,已按 A 落地(保持宽松),这张卡决定要不要走 B。

问题

#5018 把 MapConfigSchema 从 plugin-map 抬进 @object-ui/types(改名 ObjectMapConfigSchema),声明面与运行时校验从此共用一份 schema。抬升是行为等价的——schema 保持宽松(非 .strict())。

问题是要不要让它 .strict(),这样 map 块里拼错的键在运行时也会被诊断(经现有的 safeParse 走 console.warn),而不只是编译期。

为什么这不是 #5018 的一部分

.strict() 改变的是一个已发布面对作者输入的接受/拒绝行为,而 #5018 的裁定只说了「对齐声明与消费」。在一张对齐卡底下顺手改接受/拒绝,正是 clause ② 要拦的那个动作。所以座席按 A 落地并把问题报上来 —— 这个处理是对的。

实施座席的论证(原话,值得直接读)

B 在防 AI 犯错这一维上是更强的位置 —— 一个只存在于类型面的保证,保护的是 TypeScript 作者,别人一个都保护不到;而 AI 产出的 metadata 恰恰就是不带类型的那股流量。

这条论证的分量来自 #5018 里另一条实测(已单独立为 #5155):BaseSchema 带 [key: string]: any,所以顶层键拼错在任何情况下都不是类型错误。#5018 之所以能把卡面那个 latitudeFieId 变成编译错误,唯一原因是 map 块是闭合的。也就是说:

.strict() 补的正是第二行的后半格,而那一格恰好是这个平台上最主要的作者流量。

代价

.strict() 之后,作者在 map 块里写任何额外键都会新增一条 warn。要裁的是:

  1. 现存的 app metadata 里有没有在 map 块里放额外键的写法?若有,这是一次新的噪音,不是新的正确性。
  2. warn 是不是够——还是说该走更硬的拒绝?(座席只提了 warn,因为现有 safeParse 的结果就是这么用的。)
  3. 如果要做,该只对 map 块做,还是这是一条应该推广到所有组件子块 schema 的通则?第三条才是真正的裁决点 —— 单独给地图开个特例,和确立一条「闭合子块」的通则,是两件不同的事,后者影响面大得多。

四维速览(供裁定)

建议

倾向 B,但限定在 map 块,并且先测一遍现存 metadata 会不会产生噪音再落地。不建议在这张卡里顺手确立「所有子块都闭合」的通则 —— 那是更大的一刀,应该拿着实测数据单独裁。

关联:#5018(对齐卡,已按 A 落地)、PR #5156、#5155(BaseSchema 索引签名让顶层键无法被拒的实测)、#4631(三个声明面互不一致的类问题)。

Activity

  1. os-support-ai commented on Aug 18, 2026

    @os-support-ai
    Collaborator

    Maintainer ruling (2026-08-18, live PM chat with the triage seat, session session_01EUZUjvnLsqwHVkXkTv8TKQ; verbatim: 「其他接受你的建议」, applying the triage recommendation delivered for this card).

    Ruled: B, limited to the map block. ObjectMapConfigSchema goes .strict(), diagnosed through the existing safeParse → console.warn channel (warn, not reject — the channel the surrounding code already uses). Two conditions of the ruling:

    1. Pre-measure before landing: sweep existing app metadata (examples + real apps reachable in-repo) for extra keys inside map blocks; the change lands only with that measurement quiet (zero or knowingly-accepted findings) attached to the PR.
    2. ⛔ No generalization in this card: the "all component sub-block schemas go closed" rule is a separate decision card, to be filed WITH the map-block's post-landing data (warn volume, real catches) — not assumed here.

    Promoted to pm:queue. #5155 (BaseSchema index signature / top-level keys) remains its own card, untouched by this ruling.


    Generated by Claude Code

  2. os-support-ai commented on Aug 18, 2026

    @os-support-ai
    Collaborator

    Mislabel report from the repo:objectui execution seat (session session_01RV6yuVCxymHYE16PL9vQkE) — ⛔ reporting only, no label changed. Routing/grading is the triage seat's to produce (objectstack#6015).

    The state machine and this card disagree. It is labelled pm:queue + enhancement, i.e. dispatchable. But its title is [Decision], and its content is a proposal about the shape of a published contract — whether ObjectMapConfigSchema should be .strict(). That is a contract-shape question on the manual floor, not a fix with a named landing point.

    Why this matters more than a cosmetic label slip: an execution seat is required to trust labels and to treat every unassigned pm:queue card as fair game. Under the current labels, any seat scanning this lane is entitled to claim this card and dispatch a dev at it — and the dev would be implementing a contract decision nobody has ruled on. needs-user-decision is the only label that stops that, and it is absent. I skipped it on the strength of its content, but the next seat may not read the body before claiming.

    Suggested disposition (yours to make, not mine): needs-user-decision with the four-facet block, or — if triage judges it inside the adjudication lane — an explicit ruling comment plus auto-adjudicated, the way #5190 was handled this morning. Either produces a card an execution seat can act on correctly. What it should not stay is silently dispatchable.

    Noted in the seat post's queue snapshot so it is not lost if this session ends.


    Generated by Claude Code

  3. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 21, 2026
  4. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 4 — domain:ui execution seat 2
    Session: session_014mXUNuFomfj24w7s1pZzhN
    Branch: claude/issue-5157-map-config-strict
    Worktree: objectui-issue-5157
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: packages/types/src/zod/objectql.zod.ts (ObjectMapConfigSchema region only: .strict()); the existing safeParse → console.warn read in packages/plugin-map/src/ObjectMap.tsx only if its message has to name the refused key; tests beside them; the pre-measure the ruling requires (examples and in-repo apps, with extra keys inside map blocks) attached to the PR; one .changeset/5157-…md (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus — dispatch-gates --tier: 「no path-derived mandate … floor sonnet · default opus · ceiling fable」; executes ruling 5328158731 (B, limited to the map block)
    Clause-②: yes
    Thread-read: 5328750768
    Serial constraints cleared: the objectql.zod.ts predecessor PR objectui#10477 (objectui#10327) merged ae98f1d44. Open-PR file lists read 2026-09-25T05:21Z: no open PR touches objectql.zod.ts or plugin-map/src/ObjectMap.tsx. This seat's in-flight objectui#10392 fold edits plugin-map/src/index.tsx, a different file. Live pm:dispatched claims of seats 1, 3 and 4 name neither file. The mislabel report 5328750768 came after the ruling 5328158731 (same day), and the ruling stands.

    Clause-②: yes — a published schema's accept set narrows: unknown keys in a map block stop parsing clean. Per the ruling the diagnostic channel is the existing safeParse → console.warn (warn, not reject). The dev measures whether the same schema also reaches a validate-face refusal through ObjectMapSchema.map, and stops and reports if it does.


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 5157,
      "status": "needs_decision",
      "branch": "claude/issue-5157-map-config-strict",
      "pr": null,
      "session": "session_014mXUNuFomfj24w7s1pZzhN (mode:subagent, the dispatching PM session)",
      "premise_still_valid": true,
      "summary": "Stopped at dispatch hypothesis H2, as the order directs: `.strict()` on `ObjectMapConfigSchema` reaches a hard refusal on the validate face. `ObjectMapSchema.map` is `ObjectMapConfigSchema.optional()`, so `safeValidateSchema` (`AnyComponentSchema`, the function `objectui validate` and `objectui check` call) returns success=true for a map block with a misspelled key at BASE and success=false with the mutation. Everything else measured quiet. The ruling-condition-1 sweep found 20 `map` blocks that use only declared keys: 18 in objectui examples/apps/content and 2 in objectstack examples. The existing warn already names the refused key, so ObjectMap.tsx needs no change under either option. The card premise holds: at BASE the misspelled key parses clean on every tolerant face and the runtime prints no warn. No code, PR, changeset or label was written. The only artefact is the empty branch at BASE, pushed as the claim landing marker.",
      "tests": "BASE = cc139d820fdeade7dcf5931f7430dfb5c31f2f2a (objectui origin/main when the worktree was made). No diff exists, so no gate family was run. NOT MEASURED: typecheck, unit tests, check-changeset-presence, check:new-line-citations, check:control-bytes; reason: needs_decision stop at H2 before implementation. || H1 holds at BASE (cited by symbol): `export const ObjectMapConfigSchema = z.object({...})` has no `.strict()`. ObjectMap.tsx `getMapConfig` runs `ObjectMapConfigSchema.safeParse(config)` and then `console.warn('[ObjectMap] Invalid map configuration:', result.error.format())`. `FLAT_MAP_CONFIG_KEYS` is derived from `.shape`, and `.shape` is unchanged under `.strict()`: the probe measured the same 8 keys, latitudeField,longitudeField,locationField,titleField,descriptionField,zoom,center,style. Both `authoredEverything` pins exist, in ListView.mapFlatten.test.tsx and ObjectView.mapFlatten.test.tsx. || H2 probe (tsx over the worktree source; documents: clean = object-map with map {latitudeField,longitudeField}; typo = map {latitudeFieId,longitudeField}; child = div whose children hold the typo node). BASE: block typo success=true; safeValidateSchema clean success=true; safeValidateSchema typo success=true; safeValidateSchema child success=true; StrictAnyComponentSchema typo success=false, unrecognized_keys keys [latitudeFieId] at path [map]. || H2 probe with the mutation `}).strict();` on the ObjectMapConfigSchema closer. The write landed on disk: catchall def type read as never, and the anchor-marker grep counted 1. Results: block typo success=false (unrecognized_keys [latitudeFieId]); safeValidateSchema clean success=true; safeValidateSchema typo success=false (unrecognized_keys [latitudeFieId] at path [map]); safeValidateSchema child success=false (invalid_union at path [children], message Invalid input). The restore used `git checkout HEAD --` under a trap. It was proven three ways: git diff HEAD = 0 bytes, marker grep = 0, and git hash-object matching the HEAD blob. || H4 probe, same mutation and the same restore proof: `result.error.format()` on the typo block = {\"_errors\":[\"Unrecognized key: \\\"latitudeFieId\\\"\"]}. The existing warn therefore names the refused key. At BASE the typo parses success=true and no warn fires, which is the card symptom. || Spec cross-read, @objectstack/spec 17.4.0 as resolved from packages/types: `ListViewSchema.map` is an object with catchall never and 7 keys (the objectui set minus style). `safeParse({locationField, latitudeFieId})` returns success=false, unrecognized_keys [latitudeFieId]. The same logical block authored on a view is therefore already refused by the spec. || H3 sweep. Population is enumerated with git ls-tree at the ref, and content is read with git show from the same ref. The script is a one-time instrument kept in the seat scratchpad, not in the repo. Every hit is reconciled against a raw grep for a map key. (1) objectui BASE examples+apps+content: 967 files, 17 brace-literal map blocks plus 1 variable-bound block (plugin-map.mdx, TypeScript Support section, `mapConfig` with 6 declared keys), 0 undeclared keys. The 17 are content/docs/fields/location.mdx x1, content/docs/plugins/plugin-map.mdx x13, and examples/schema-catalog plugin-map JSON x3 (event-venue-finder, real-time-delivery-tracking, store-locator-map). The other raw-grep hits are package names, type annotations and test helpers. (2) objectstack origin/main aa04ea2 examples+apps: 284 files. There are 2 real map config blocks, both {titleField, locationField}: app-showcase task.view.ts on the view named map, and test task-map-marker-title.test.ts. The 2 other hits are false positives: a view whose key is `map`, and a translations label map. (3) objectui BASE packages/ plus console and catalog tests: 96 brace blocks. No object-map fixture authors an undeclared key inside `map`. The only `bogusKey` is in ListView.mapViewLevelConfig.test.tsx, on the ListView flatten path, which never parses ObjectMapConfigSchema. The remaining EXTRA rows are unrelated `map` configs (flow nodes, recipient maps) or spreads of declared keys. Verdict: measurement quiet, 0 findings. || Door census, by git grep of the symbol in non-test source at BASE. Four sites use `ObjectMapConfigSchema`. (i) `ObjectMapSchema.map` flows through ObjectQLComponentSchema and AnyComponentSchema into safeValidateSchema/validateSchema, reaching packages/cli validate.ts and check.ts (check uses it as a recogniser, and a refused file moves to its UNVALIDATED_CANDIDATE bucket), plus every SchemaNodeSchema child slot. (ii) The ObjectMap.tsx getMapConfig warn, reached only when a `map` key is present on the node; the ListView/ObjectView flatteners emit flat keys and no `map` key. (iii) ObjectMap.tsx FLAT_MAP_CONFIG_KEYS, which reads shape only. (iv) StrictAnyComponentSchema, which already refuses and has no non-test consumer at BASE. ListViewSchema in objectql.zod.ts does not reference ObjectMapConfigSchema. packages/core validateSchema (SchemaRenderer dev validation) imports no zod face.",
      "mcp_calls": "0 — none",
      "api_writes": "1 — POST /repos/objectstack-ai/objectui/issues/5157/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). The git push of the empty branch is not REST. Reads: GET /repos/objectstack-ai/objectui/issues/5157 and GET .../issues/5157/comments?per_page=100.",
      "deviations": [
        "Stopped at H2 per the order: no PR, no PR-assignee label-write, no changeset, no code. The write budget used is 1 of 4 (the report comment), plus the empty-branch git push.",
        "The role file was read at objectstack origin/main aa04ea2, not a4ca69a: origin/main had moved. `git diff a4ca69a origin/main -- .claude/agents/os-dev.md` is empty, so the text is the same.",
        "The sweep ran wider than the order named (examples/apps/content). It also covered objectstack origin/main examples+apps and objectui packages/ fixtures. These are reported separately so the named scope stays readable on its own.",
        "The worktree objectui-issue-5157 had no changes. It is removed after this report, and the branch stays on the remote at BASE for the claim."
      ],
      "open_questions": [
        {
          "question": "Does ruling 5328158731 (B, map block only, \"warn, not reject\") cover the validate-face refusal that `ObjectMapConfigSchema.strict()` brings with it? `ObjectMapSchema.map` IS `ObjectMapConfigSchema.optional()`. Closing the schema therefore also makes `safeValidateSchema`, and `objectui validate` / `objectui check` with it, refuse a map block with a misspelled key. Measured: BASE success=true, strict success=false, unrecognized_keys [latitudeFieId] at [map]; a nested document fails with invalid_union at [children]. The runtime channel stays a warn under both options: ObjectMap still renders and warns.",
          "options": [
            "A — strict everywhere: `.strict()` on ObjectMapConfigSchema itself. The runtime warns and renders as before; the validate face refuses at authoring time. [Business need] The sweep found 20 of 20 existing map blocks clean, so A newly refuses 0 measured documents. The symptom is real and measured: at BASE a typo leaves an empty map with no diagnostic. validate/check is the only authoring-time check that untyped metadata passes through. [Long-term] A keeps the one-schema property objectui#5018 lifted the schema for (declared face = runtime check = validate face). It matches the spec's own view-level block (spec 17.4.0 ListViewSchema.map, catchall never) and the objectui#5250 strict twin, which already refuses. Precedent for strict sub-blocks on the tolerant face: DashboardWidgetSchema (objectui#6002) and CalendarDayRangeSchema (objectui#10304). [Anti-AI-error] Strongest: a loud refusal at validate/publish that names the key. [Startup focus] Smallest diff: one `.strict()`, the pins, and a minor changeset that states the narrowing. No new gate, no second schema, no transition window needed, since measured breakage is 0. Costs: (1) at the validate face A goes beyond the literal \"warn, not reject\", so it needs the maintainer's explicit yes; (2) bare safeValidateSchema reports a nested document as a generic invalid_union at children (NOT MEASURED: how the CLI arm-selection diagnostics render it).",
            "B — strict on the runtime warn path only. The node `map` member keeps the tolerant schema, and ObjectMap.tsx parses the block with a strict variant, for example `ObjectMapConfigSchema.strict()`. [Business need] B gives the same runtime catch (the warn names the key, as measured) but only on nodes that directly author a `map` block. The view flatten path never reaches it. `objectui validate` keeps printing valid for a misspelled block. [Long-term] B re-creates two accept sets for one declared block (declared tolerant, runtime strict), which is the drift shape objectui#5018 removed. It is also transitional: once the objectui#5250 devx half wires validate to StrictAnyComponentSchema, validate refuses the same typo anyway. [Anti-AI-error] Weaker: AI-authored metadata is judged at validate/check, which stays silent; a dev-console warn never reaches a publish pipeline. [Startup focus] Slightly more code: a second parse spelling in plugin-map, plus a pin that the node face stays tolerant. B is literally compliant with \"warn, not reject\"."
          ],
          "recommendation": "A, for the maintainer to confirm (not chosen or implemented here, per the order). All four axes point the same way: 0 measured documents would be refused; one schema, matching the spec's own strict view-level block and the strict twin; a loud authoring-time refusal is what protects untyped AI metadata, and a runtime warn does not; and A is the smallest change with no new gate. Choose B only if \"warn, not reject\" is meant to bind the validate face as well as the runtime."
        }
      ],
      "out_of_scope_findings": [
        "Not class a/b/c: a stale code comment in ObjectMap.tsx `warnOnTopLevelStyleUrl` says `@objectstack/spec` list-view schemas \"declare no `map` block at all\". Measured on spec 17.4.0: ListViewSchema declares `map`, a strict 7-key object. Noted, not filed. carrier: the implementation round of this same claim, if it touches ObjectMap.tsx; otherwise no carrier (承接者:无), acceptance notes only."
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    维护者速读 · decision box: does "warn, not reject" bind the validate face too? · domain:ui seat 2 · 2026-09-28T11:0xZ

    session_014mXUNuFomfj24w7s1pZzhN. The claim 5827254568 stopped at needs_decision before any code (dev report 5827348034). The seat left the card at pm:dispatched from 2026-09-25 and never routed it. This comment routes it now: release to needs-user-decision. The claim is spent.

    The question. Ruling 5328158731 (B, the map block only) makes ObjectMapConfigSchema .strict(), diagnosed through the existing safeParse → console.warn channel ("warn, not reject"). But ObjectMapSchema.map is ObjectMapConfigSchema.optional(). So closing the schema also makes safeValidateSchema, and with it objectui validate / objectui check, refuse a map block with a misspelled key. Does the ruling cover that refusal?

    Measured (dev report):

    • At the base, success=true. Strict gives success=false, unrecognized_keys [latitudeFieId] at [map].
    • At runtime, ObjectMap still renders and warns under both options.
    • The pre-measure sweep the ruling requires found 20 of 20 existing map blocks clean, so 0 documents would be newly refused.
    • ObjectMapConfigSchema is still tolerant on main today.

    Options, on the four axes:

    • A (recommended). .strict() on ObjectMapConfigSchema itself. The runtime warns and renders as before, and validate / check refuse the typo at authoring time.
      • Business need: the symptom is real and measured. At the base, a typo leaves an empty map with no diagnostic. The sweep shows A newly refuses 0 of the 20 existing blocks.
      • Long-term: it keeps the one-schema property objectui#5018 set up (declared face = runtime check = validate face). It matches the spec's strict view-level map block and the objectui#5250 strict twin. Strict sub-blocks on the tolerant face have precedent in objectui#6002 and objectui#10304.
      • AI-error: the strongest option. It is a loud refusal at validate / publish that names the key, which is where untyped AI-authored metadata is judged.
      • Startup focus: the smallest diff: one .strict(), pins and a minor changeset. No new gate, and no transition window, because measured breakage is 0.
      • Against: at the validate face it goes past the literal "warn, not reject", so it needs an explicit yes. A nested document fails as a generic invalid_union at children. How the CLI renders that was not measured.
    • B. Strict on the runtime warn path only. ObjectMap.tsx parses with a strict variant, and the node's map member stays tolerant.
      • Business need: the same runtime catch, but only on nodes that author a map block directly. objectui validate keeps printing valid for a misspelled block.
      • Long-term: two accept sets for one declared block, the drift shape objectui#5018 removed. It is also transitional: once objectui#5250's devx half wires validate to StrictAnyComponentSchema, validate refuses the typo anyway.
      • AI-error: weaker. Validate / check stay silent, and a dev-console warning never reaches a publish pipeline.
      • Startup focus: slightly more code: a second parse in plugin-map and a pin that the node face stays tolerant. It is literally "warn, not reject".

    After the decision: the next dispatch re-claims the card. The ruling's "no generalization in this card" condition stands under either option.

    domain:ui seat 2 · decision box and release · 2026-09-28T11:0xZ

  7. 1 remaining item

  8. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    Decision-box entry check by the triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-09-28T12:06Z. ⛔ Not a claim; labels are untouched (needs-user-decision stays).

    Prior rulings on this card: 5328158731 (Ruled: B, limited to the map block; .strict() diagnosed through safeParse → console.warn, "warn, not reject"). What changed since: the dev measured that ObjectMapSchema.map is ObjectMapConfigSchema.optional(). Closing that schema therefore also makes safeValidateSchema, and with it objectui validate / check, refuse a misspelled key, which is a face the ruling's "warn, not reject" did not address. The question is only whether the ruling covers that refusal. ⛔ The ruling's scope, the map block only, is not reopened.

    Entry check.

    • The 维护者速读 is present (5868608591).
    • The four-facet block is present.
    • The ruling's pre-measure is reported: 20 of 20 map blocks are clean.
    • The only thing missing was the line above. The seat's question paragraph already states the change, so triage completed the line from that measured text instead of bouncing the card back through pm:retriage for one line.
  9. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    Ruling: batch #238 item 2 · letter A · maintainer 「同意」 2026-09-28T13:48Z

    Director seat (objectstack#12708, summon #30 续 2, session_01AsCNgFBs8HCjwhyHQsFbx3). Provenance: maintainer, live PM chat with the director seat, 2026-09-28, replying 「同意」 to batch #238 as presented — this card item 2, recommendation A, fallback B, the full four-axis body in chat.

    Ruled: A. .strict() goes on ObjectMapConfigSchema itself (packages/types/src/zod/objectql.zod.ts), per ruling 5328158731 (B, the map block only), and the refusal that produces on the validate face is COVERED: because ObjectMapSchema.map is ObjectMapConfigSchema.optional() (objectql.zod.ts:1954), safeValidateSchema — and with it objectui validate / objectui check — refuses a map block with an unknown key at authoring time, while the runtime ObjectMap keeps rendering and warning through the existing safeParse → console.warn channel. 「Warn, not reject」 in the earlier ruling described the runtime channel; it does not bind the validate face. B (a strict variant on the runtime warn path only, the node's map member left tolerant) is not taken.

    Readings that decided it:

    Execution parameters (ruled in the same stroke):

    • This card → pm:queue; domain:ui, priority:p2 stay; the next dispatch re-claims it and executes A. The claim 5827254568 is spent.
    • Change: ObjectMapConfigSchema closes with .strict(). FLAT_MAP_CONFIG_KEYS (derived from .shape) is unchanged (8 keys, measured). packages/plugin-map/src/ObjectMap.tsx needs no change: the existing warn already names the refused key (Unrecognized key: "latitudeFieId", measured H4).
    • Pins, both sides: (i) runtime — a map block with an unknown key still renders and warns, and the warn names the key; (ii) validate face — safeValidateSchema refuses the same block with unrecognized_keys at path [map] and the clean block passes; (iii) the sweep's 20 existing blocks parse clean under strict (a fixture pin, so the sweep's result is held rather than remembered).
    • Nested documents: a nested typo fails as invalid_union at [children]. The dev measures how objectui validate / check render that refusal and records it in the PR; if the rendered message does not name the key, that is a follow-up card, not this one.
    • Clause-②: yes (a published schema's accept set narrows); @object-ui/types changeset minor.
    • Standing condition kept: no generalisation to other component sub-blocks in this card.

    State: needs-user-decision → pm:queue; domain:ui, priority:p2 stay; the Ruled line goes on the body in the same stroke.

  10. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 16 (serial) — domain:ui execution seat 2
    Session: session_014mXUNuFomfj24w7s1pZzhN
    Branch: claude/issue-5157-map-config-strict-a
    Worktree: objectui-issue-5157
    Domain: domain:ui
    Seat: domain:ui#2
    Ruling-ref: 5328158731 (B, the map block only) and 5871202958 (batch #238 item 2, letter A, maintainer 「同意」 2026-09-28T13:48Z)
    File surface, per the execution parameters of 5871202958:

    • packages/types/src/zod/objectql.zod.ts: ObjectMapConfigSchema closes with .strict(). FLAT_MAP_CONFIG_KEYS stays at 8 keys. packages/plugin-map/src/ObjectMap.tsx is unchanged (its warn already names the key).
    • Pins on both faces: (i) runtime, where a map block with an unknown key still renders and the warn names the key; (ii) validate face, where safeValidateSchema refuses it with unrecognized_keys at [map] and a clean block passes; (iii) the sweep's 20 existing blocks parse clean under strict, held as a fixture.
    • The nested-document refusal (invalid_union at [children]): the rendered objectui validate / check message is measured and recorded in the PR. If it does not name the key, that is a follow-up card.
    • A '@object-ui/types': minor changeset that states the narrowing.
    • ⛔ No generalisation to other component sub-blocks (the standing condition).
      Container & model: S, mode:subagent, model: opus (default judgment tier)
      Clause-②: yes
      Why yes: a published schema's accept set narrows (ruled so). The PR carries needs:contract-review.
      Thread-read: 5871202958
      Serial constraints cleared:
    • Dispatch is serial, and this is the one card in development. PR objectui#10983 (objectui#10963) is in the merge queue, and PR objectui#10989 (objectui#10951) is in a base-merge landing chore. Neither counts.
    • I read the open-PR file lists at 2026-09-28T14:3xZ, at objectui origin/main 40c076fc2d. None touches objectql.zod.ts's ObjectMapConfigSchema region or plugin-map.
    • The claim 5827254568 is spent (per 5871202958), and its empty branch claude/issue-5157-map-config-strict is not reused.
    • If another seat claims at the same time, the earliest live claim wins and this seat yields.
  11. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 5157,
      "status": "done",
      "branch": "claude/issue-5157-map-config-strict-a",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10997",
      "session": "session_014mXUNuFomfj24w7s1pZzhN (mode:subagent, the dispatching PM session)",
      "premise_still_valid": true,
      "summary": "Implemented ruling A. `ObjectMapConfigSchema` now closes with `.strict()`, so `safeValidateSchema` and `objectui validate` refuse an undeclared `map` key with `unrecognized_keys` at `map`, naming it, while `ObjectMap` still renders and warns with the key named; `.shape` keeps its 8 keys and ObjectMap.tsx is unchanged. The re-derived sweep found 20 of 20 existing map blocks clean (18 objectui, 2 objectstack), and it is held as a fixture pin. The CLI names the key at both depths (the nested case as arm sub-line 1.6 of 10 under an `invalid_union` at `children`), so no follow-up card is needed. PR #10997 is draft, assigned to os-elon-musk, with a `'@object-ui/types': minor` changeset. The earliest live Claim at start was 5872032029 (5827254568 spent per 5871202958), and the card assignee os-elon-musk was already set.",
      "tests": "HEAD d68b1086ed. BASE 40c076fc2ddbc418c833a2be607b975fde77a9cb. All test runs went through os-verify-lock.sh; verdict lines quoted. || (1) `pnpm exec vitest run --maxWorkers=2 packages/types/ packages/cli/` + the 31 outside test files that read ObjectMapConfigSchema / ObjectMapSchema / FLAT_MAP_CONFIG_KEYS or author an object-map (from git grep) + `examples/schema-catalog/test/`: `Test Files 358 passed (358)`, `Tests 9311 passed (9311)`, `VERDICT command-exit 0`. (2) `pnpm exec vitest run --maxWorkers=2 packages/plugin-map/` + the new types pin: `Test Files 34 passed (34)`, `Tests 243 passed (243)`, `VERDICT command-exit 0`. (3) type-check of @object-ui/types, @object-ui/plugin-map and @object-ui/cli (all three echoed), `VERDICT command-exit 0`, after building the `@object-ui/plugin-map^...` closure. `--listFilesOnly` on each package's tsconfig.test.json lists the new test file (1 hit each). || ABLATION (red on base, green on head). Tool: objectstack origin/main `scripts/ablation-replace.mjs`, copied to the scratchpad, WRAP mode. Anchor `}).strict();` x1 to x0, replacement `});` x23 to x24, blob 7ef2545fa19c to 4d0b11cad207. Restore: blob == HEAD 7ef2545fa19c and `git diff HEAD` empty. Resolution: the root vitest alias maps `@object-ui/types/zod` to packages/types/src/zod/index.zod.ts, so no dist is involved. Mutated run: `Tests 5 failed / 46 passed (51)`. Red: the 4 (ii) rows (safeValidateSchema typo, ObjectMapSchema typo, ObjectMapConfigSchema typo, nested typo), plus the (i) warn row `expected 0 to be greater than 0`. Green: the (ii) clean control, the 2 shape rows, the 40 (iii) sweep rows, the (i) render row, and the (i) no-warn control. Head: `Tests 51 passed (51)`. || CLI measurement on built dist. HEAD: `objectui validate` on the root typo exits 1, `1. Unrecognized key: \"latitudeFieId\" / Path: map / Code: unrecognized_keys`. The nested typo exits 1, `1. Invalid input / Path: children / Code: invalid_union`, with sub-line `1.6 [arm 1/6] Unrecognized key: \"latitudeFieId\" / Path: children → 0 → map / Code: unrecognized_keys` among 10 sub-lines. The clean node exits 0 valid. `objectui check` exits 0 and lists only the root typo file under `did not validate`, then `✓ All checks passed`. BASE (types rebuilt with `.strict()` ablated; dist preflight read the ObjectMapConfigSchema closer as `});`): validate exits 0 valid on all three files; check exits 0 with no list. Types dist rebuilt after the restore, and the closer reads `}).strict();`. || Gates at HEAD d68b1086ed: check-changeset-presence `✅ 3 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)`; check-changeset-no-major `✅ No changeset declares a major bump`; check-changeset-overwrite `✅`; check:changeset-claims exit 0 (22 pending changesets name touched files; the 7 mentioning map, strip or tolerance were re-read, none is falsified); check:pending-changeset-literals `✅`; check:new-line-citations `VERDICT new-cross-file-line-citations: 0 new citation(s)`; check:control-bytes `✅ OK`, plus a self-grep of the changed files for control bytes with 0 hits; check:spec-symbols `✅`; check-doc-links `Links are valid across 17 scan roots`; check:doc-fences, check:doc-types, check-prompt-component-keys, check:test-path-roots, check:vi-mock-specifiers all `✅`; check-governed-queue-guard --test on the 7 paths `✅ NOT GOVERNED`. ESLint (narrowed, --no-inline-config --format json) on the 3 changed TS files: 0 errors, 0 warnings on the new files, 1 pre-existing warning in objectql.zod.ts; no type-aware linting is configured, so the diff cannot move another file's verdict; the repo-wide lint is CI's. Eager closure: the console vite build guard printed `[plugin assert-types-zod-stays-lazy] ... none in the eager closure`, and `pnpm check:eager-closure` printed `✅ Console eager closure is 3103.9 KB gzipped ... (budget: 3104.5 KB, headroom: 0.6 KB)`, equal to the dispatch's reading, so the delta is 0.0 KB. `git merge-tree --write-tree origin/main HEAD` against fresh origin/main f667c1df97 exits 0. NOT MEASURED: check:doc-snippets / check:doc-examples; reason: they need every package built, and this diff adds no code fence and changes no TS declaration. CI: in_progress, not awaited.",
      "mcp_calls": "0 — none",
      "api_writes": "3 — all via the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, run conclusion success, as objectstack-fleet[bot]): (1) pr_create → POST /repos/objectstack-ai/objectui/pulls, draft, PR #10997, relay run 36441807972; (2) label-write --assign os-elon-musk → POST /repos/objectstack-ai/objectui/issues/10997/assignees, relay run 36441947624, read-back MATCHES (labels untouched: documentation, package: types, plugin, tests from labeler); (3) this os-dev-report via post-stamped.mjs → POST /repos/objectstack-ai/objectui/issues/5157/comments. Not REST: 4 git pushes (empty branch, then 3 commits). Reads: GET issues/5157, issues/5157/comments, pulls?head=…, pulls/10997.",
      "files_changed": [
        "packages/types/src/zod/objectql.zod.ts",
        "packages/types/src/__tests__/object-map-config-strict-5157.test.ts",
        "packages/plugin-map/src/ObjectMap.strictConfigWarn-5157.test.tsx",
        ".changeset/5157-object-map-config-strict.md",
        "packages/plugin-map/README.md",
        "content/docs/plugins/plugin-map.mdx",
        "packages/types/README.md"
      ],
      "deviations": [
        "Order said pin (i) is green on base. Measured, only its render half is green on base. The warn half is red on base, because the tolerant schema never fails the safeParse, so nothing is warned. That silence is the card's symptom. The pin asserts the ruled head behaviour (renders AND warns naming the key), and the ablation shows the split.",
        "Order said FLAT_MAP_CONFIG_KEYS stays at 8 keys. `.shape` has 8 keys. FLAT_MAP_CONFIG_KEYS (module-private, `.shape` minus `style`) has 7, before and after. The pin asserts the 8-key `.shape` list.",
        "During the base-CLI measurement, the script's EXIT trap restore failed with `not inside a git repository`. ablation-replace.mjs resolves the repo root from process.cwd(), and the script had cd'd into the scratchpad. I caught it in that step's log and restored by hand in the next command with `ablation-replace.mjs --restore` from the repo root: blob == HEAD 7ef2545fa19c, `git diff HEAD` empty, `git status` clean. Then I rebuilt types dist, and the closer reads `}).strict();`. No commit or test ran on the mutated tree. The pin ablation itself ran in WRAP mode, and its own restore passed.",
        "The write tools and the ablation tool ran from objectstack origin/main 3cf6449389. The write tools ran from a detached worktree at /home/user/objectstack-pm-5157, removed after this report. The ablation tool was copied into the scratchpad. The reason: the shared objectstack checkout's HEAD 5581d3000f differs from origin/main in scripts/pm/post-stamped.mjs, dispatch-gates.mjs and three others, and objectui has no ablation tool.",
        "dispatch-gates.mjs is objectstack-only, so the objectui gate families were derived by hand from package.json and .github/workflows. Added beyond the order's list: check-changeset-overwrite, check:doc-fences, check:doc-types, check-prompt-component-keys, check:test-path-roots, check:vi-mock-specifiers, the governed-surface test, and a narrowed ESLint run.",
        "Docs item 5: no sentence anywhere said the map block tolerated unknown keys or that validate accepted them. The types README said the whole rendering face, every named mirror included, is tolerant, so it now names the map block as closed. Per AGENTS #2, a one-paragraph closure note was also added to the plugin-map README and plugin-map.mdx.",
        "The eager closure was measured with a head console build only, with no base build. The reading equals the dispatch's quoted 3103.9 KB, and types-zod is outside the closure by the build's own guard.",
        "The PR title keeps `feat(types)!`: check-changeset-no-major reads only changeset front matter, not the title, and the bump is minor."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "Not class a/b/c (a comment): the ObjectMap.tsx `warnOnTopLevelStyleUrl` docblock says @objectstack/spec list-view schemas \"declare no `map` block at all\". Measured on spec 17.4.0: ListViewSchema declares `map`, and safeParse of a view with map {locationField, latitudeFieId} gives unrecognized_keys [latitudeFieId] at [map]. carrier: 承接者:无 — noted in the PR's Acceptance notes, not filed.",
        "Not class a/b/c (by design, advisory): `objectui check` admits a file whose root carries a structural key (children, className, …) without validating it. So a map typo in such a file, nested or at the root, never reaches check; only a root object-map with no structural key lands in its `did not validate` list. The command's own description sends the verdict to `objectui validate`. carrier: 承接者:无 — Acceptance notes only.",
        "Not class a/b/c (readability): the nested refusal names the key as sub-line 1.6 of 10 under a generic invalid_union at children; the other nine lines are non-candidate arms. The key IS named, so the ruling's follow-up condition is not met. carrier: 承接者:无 — Acceptance notes only.",
        "Siblings with the same shape (standing condition, not touched, not measured): the object-calendar `calendar` block (spec-derived, `.passthrough()`), plus plain z.object config schemas such as SortConfigSchema, DrillDownConfigSchema and ReportExportConfigSchema. Listed in Acceptance notes for the separate generalisation decision."
      ]
    }

    Generated by Claude Code

  12. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    Contributor

    ✅ ACCEPT: PR objectui#10997 (Fixes) at d5c28d4da2; ready and auto-merge follow this ACCEPT

    domain:ui seat #2, session_014mXUNuFomfj24w7s1pZzhN. I checked this against the diff and CI, not against the reports' own account.

    • Claim 5872032029.
    • Ruling-ref 5328158731 and 5871202958 (A).
    • Dev report: the newest os-dev-report on this card, plus the fix-round reports relayed in-session.
    Implemented-by:  claude/issue-5157-map-config-strict-a
    Reviewed-by:     session_014mXUNuFomfj24w7s1pZzhN
    
    item reading
    the change ObjectMapConfigSchema closes with .strict(). objectui validate refuses an undeclared map key and names it: at map on a root node, and in the arm detail under invalid_union at children when nested. ObjectMap does not throw: it warns and names the key. .shape is unchanged, and so is ObjectMap.tsx
    sweep 20 of 20 existing map blocks are clean (18 objectui, 2 objectstack, one of them on a named view), held as a fixture. Measured breakage: 0
    review contract review 5873208537 FAIL (the ledger entry for CI's shard 1/8, and three wording defects), then fix round 1 (bf4797f85f, d5c28d4da2), then the in-seat delta record PASS (posted with this landing)
    changesets '@object-ui/types': minor, Clause-②: yes: a published schema's accept set narrows, following the objectui#6002 / #10304 precedent
    follow-up objectui check never names the refused key, and skips nested typos. One card, filed after landing
    CI head d5c28d4da2: every check final and green before ready. Eager closure +0
    closing keywords Fixes #5157
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatenhancementNew feature or requestpriority:p2

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions