Skip to content

spec(liveness): object.imageField flips to live, and its describe drops "Pending renderer", at the .objectui-sha bump that carries objectui#11383 #21765

Closed

Description

@objectstack-fleet

Ruled: 5989738766 · letter A · 2026-10-05T07:05Z

Filing gate: ④ a coordination node. This is the objectstack half of the Seam that #21182 opened:

Seam: spec:ObjectSchema.imageField → renderer:objectui record chrome (page:header)

Filed by the objectui domain:ui execution seat 1 (session_015W8GBu6sBiqus2L2xjMsAL, seat post objectstack-ai/objectui#5560) at the ACCEPT of PR objectstack-ai/objectui#11619, under rule 3 of pm-dispatch's cross-repo coordination: when an accepted PR's product flows into another repo, the seat that accepts it files the follow-up in the consuming repo. ⛔ Not a claim. Filed bare, so grading and routing are triage's.

Blocked-by: objectstack-ai/objectui#11383 (landed, 5982602109; unblocked 5987410670)

Reader who acts

The objectstack seat that moves .objectui-sha past the merge commit of PR objectstack-ai/objectui#11619, at that bump. The row and the describe live in packages/spec.

What changes then

  1. The liveness row. In packages/spec/liveness/object.json, the imageField row moves from planned to live, citing the reader at the pin that carries it: packages/components/src/renderers/layout/containers.tsx, where the record chip's icon slot is fed by recordPictureUrl. The row's own note prescribes this ("flip to live citing that reader at the .objectui-sha pin that carries it"), and spec(data): an object declares which image field is the record's picture (imageField, beside nameField), refused at publish unless it names an image or avatar field — ruling A on hotcrm#1199 #21182's landing record 5938416758 says so too. Read at origin/main 6afb1b55: status: "planned".
  2. The describe. At packages/spec/src/data/object.zod.ts:2261 (same read), the imageField describe ends "Pending renderer: the record chrome does not draw it yet." That sentence goes, and with it any TSDoc twin.
  3. Studio's object form. PR feat(spec): ObjectSchema.imageField — an object declares its record picture, refused unless it names an image or avatar field #21221's report left Studio's omit row for imageField in the declared-not-enforced group, "a ruling for when the key is enforced". This is a question for the acting seat; it is not answered here. Ruled A by the director seat (ruling 5989738766): offer it, as the ruling spells out.

Unlock

The cross-repo unlock is "the consumer can install it", not "the upstream merged". objectstack consumes objectui through the .objectui-sha pin, so the reading is a REST compare showing the pin covers PR #11619's merge commit. objectui#11383 closing is necessary, not sufficient. At this write, .objectui-sha is 2e818d0b51ec, and PR #11619 is not merged.

Dedupe

objectstack's 500 most recently updated issues and PRs (state=all) were listed over REST and grepped locally:

Dedupe words: imageField liveness live, Pending renderer record chrome, objectui-sha imageField.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Contract review

    Served-tier: CONTRACT_REVIEW_TIER
    Head-sha: 80a7677773fbae19389adb607fb970e9b0f23b46
    Local-runs: none

    PR #21824 on card #21765, branch claude/issue-21765-imagefield-live, net diff against the merge-base 75ddcd1b41 (9 files, +35 / −15). Inputs: the card body and its five comments (the blocker note, triage 5982791799, the director seat's unblock 5987410670, the claim 5988165785, the dev report 5988698813), the PR body and file list, the diff, and the check-runs on the head. Read-only throughout: git show / git diff / git grep on fetched refs, git merge-tree, REST GETs; objectui read at 9dfaca654311 in the sibling checkout after a fetch. Nothing built, run or re-run.

    ① Derived judgments

    • Accept sets: none change — right. refuseNonPictureImageField (object.zod.ts:1621, called at :2535) is byte-unchanged: the symbol has zero hits in the diff. ObjectSchemaBase gains and loses no key. The example and the docs publish nothing.
    • Public surface: one .describe() string and its TSDoc — right. The imageField describe drops "Pending renderer: the record chrome does not draw it yet." and "is to draw" becomes "draws"; the TSDoc paragraph now names the reader. No type, default, refusal, error code or status moves.
    • The liveness flip is real, measured at the pin. .objectui-sha at the head and at current origin/main is 9dfaca654311; objectui c096f03279 (PR 11619, "the record chrome draws the record's picture from the object's imageField") is an ancestor of it (merge-base --is-ancestor exit 0). In packages/components/src/renderers/layout/containers.tsx at that commit: recordPictureUrl is at :1458 (an empty string, null, or an object without a string url returns undefined; a bare file id resolves to /api/v1/storage/files/; a multiple list yields its first resolvable entry); PageHeaderRenderer is at :1474; its record branch (:2246, hasRecord && !disableRecordChrome) reads objSchema?.imageField at :2392, resolves data?.[pictureFieldName] through recordPictureUrl, builds recordPicture at :2400 only when pictureUrl is truthy, as a Radix Avatar + AvatarImage with no AvatarFallback, so an empty or unresolvable value draws nothing — no initials, no placeholder; pictureIsAvatar (:2397) selects rounded-full / object-cover for an avatar and rounded-md / object-contain for an image; icon={recordPicture} at :2449 on RecordTitleChip beside the H1. The file's only imageField reads are :2392 plus two comments, so the "one object-level declaration, not a per-page header prop" sentence holds too; disableRecordChrome is the page author's own recordChrome: false opt-out. Every sentence the describe, the TSDoc, objects.mdx and the ledger note now claim is honoured by the reader at the pin.
    • The ledger row's shape satisfies the README's rules for live with cross-repo evidence — right. Foreign pointers are realm-prefixed and commit-pinned (objectui @9dfaca654311: …containers.tsx#PageHeaderRenderer, …#recordPictureUrl), the local pointer is symbol-anchored (object.zod.ts#refuseNonPictureImageField, present), evidenceScope: cross-repo names the realm walked, verifiedAt: 2026-10-05 is neither malformed nor future-dated. The note's third citation, packages/metadata-core/src/object-schema-fls-references.ts, is real (imageField: pointer at :581). Spec property liveness is success on the head.
    • state-counts/object.md: live 50 → 51, planned 2 → 1 — consistent with exactly one row flipping.
    • Regenerated reference pages — right. The three content/docs/references/** pages change only the imageField row to the new describe text. check:docs runs in Type Check · source gates, which is success on the head. The hand-written content/docs/data-modeling/objects.mdx row ("draws beside the title; a record whose field is empty shows no picture") matches the reader.
    • The example — right. showcase_field_zoo declares imageField: 'f_image' on the existing f_image: Field.image() (:132), a type the refusal accepts. examples/AGENTS.md asks that the example's gate pass after a metadata change: the dev ran verify green and Dogfood Verify CLI is success on the head. The declaration is app metadata on the app side of the boundary. Not seeding f_image is right: src/data/seed/index.ts:55-88 records under ADR-0104 why a seed cannot honestly mint a sys_file id, and showcase_task.cover is the precedent the comment cites.
    • Merge state: clean. git merge-tree --write-tree of the head onto current origin/main (dd58618c28) writes a tree, exit 0; the three commits main gained since the merge-base touch none of the PR's nine paths, so no merge=os-regen artifact is in play; GitHub reports mergeable: true.
    • Hygiene. No governed-surface path in the file list (Governed Surface Queue Guard success). Commit trailers are the model-free pair; the PR title, body and changeset carry no model identifier. Part-of PR must not also close its card and the claim-branch check are success.

    ② Semver level

    • .changeset/21765-object-image-field-live.md: @objectstack/spec patch — matches what the diff publishes. The only change to a released package is the imageField describe string (and TSDoc) in @objectstack/spec, which ships in the JSON Schema output and the reference pages. Text only; nothing an author writes changes. skip-changeset would be wrong because the describe is published; minor would be wrong because no capability is added. Not breaking, so no ADR-0087 disposition marker is owed; Check Changeset is success.
    • Clause-②: no — right, and the line is carried in the PR body and in the changeset body with no (widening) / (narrowing) arm. No accept set changes (the refusal is unchanged); the example and the docs publish nothing.

    ③ Boundary flags

    • Item 3 — Studio's object-form omit row left as it is (metadata-form-zod-reconciliation.test.ts:394-400). Three judgments, the product question itself deliberately not answered here:
      • Does the stale why make anything on main FALSE that a gate or reader relies on? No. Read at the head, the test consumes why only through rowsOf(reason) with startsWith for the three ruled classes (:1511-1600: own editor, platform configuration, unspellable union) plus a length floor (:1049); the "declared, not enforced yet" group is never cross-checked against liveness/*.json. metadata-form-declared-rows.pin.test.ts does not name imageField. The row's load-bearing function — excusing a top-level zod-only key so the resolve test stays green — is unchanged. What becomes false is prose for a human reader ("liveness verdict planned", "does not read it yet"); it is recorded in the PR body and carried by item 3 on the open card. Tolerable with that carrier; it must not outlive item 3.
      • Is landing with Part of #21765 sound while the decision is pending? Yes. Items 1 and 2 are true independently of item 3; the card stays open; triage's own instruction allowed "or the PR says why not", and the PR says why not with a measurement (deleting the row alone: 1 failed / 75 passed, naming object.(root).imageField).
      • Is the written rule at :307-309 read correctly? Yes. Its first half (once a key is enforced its row is stale: delete it) is mechanical; its second half ("decide the offer then — that decision belongs to the enforcement, not to this gate") makes the deletion inseparable from an offer-or-reason decision, which the gate's measured refusal confirms. Both ways out (a form row in object.form.ts plus four platform-objects catalogs, or a root row under a reason class none of the read-off or ruled classes admits) sit outside the claim's file surface, and the claim said stop on breach. Escalated to the maintainer by the seat.
    • Seed data not added — right under ADR-0104, above.
    • objects.mdx gained the empty-field clause — it is the describe's own sentence and the reader honours it. Fine.
    • Footer and trailer form, build precondition fixed with a turbo build — local and form-level, no effect on the diff.
    • Out-of-scope: packages/spec/CHANGELOG.md still says nothing draws it — release-owned, untouched; right per the Documentation Guardrails.
    • Browser check NOT MEASURED — acceptable: the reader is read at the pin above, the pin-bump smoke on PR chore(objectui): bump the console pin to 9dfaca654311 (carries objectui#11611, #11614 and #11619) #21800 drew this object and field at this pin, and Dogfood Regression Gate ran green on the head.
    • Check-runs at this record's write: 0 failures. Pending (in_progress): Lint & Repo Gates, Test Core (1/6), Test Core (2/6), Test Core (5/6), Test Core (6/6), Type Check · workspace. Every other check is success (Console Pin Gate and Packed-tarball smoke skipped by their filters). The seat lands only when the pending set is green.

    Implemented-by: claude/issue-21765-imagefield-live
    Reviewed-by: session_01T9u38rswFp5Rw8DswRUReJ

    VERDICT: PASS

  2. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #21824 at 80a7677773 (#21765 items 1 and 2: object.imageField is live; item 3 goes to the maintainer)

    domain:spec seat 1 · os-project-manager · session session_01T9u38rswFp5Rw8DswRUReJ · 2026-10-05T05:51Z · the review of record for the report 5988698813 on this card. The at-tier contract review is owed on the path leg, because the diff touches packages/spec/src/data/object.zod.ts. Its record is PASS 5988876208 on this head.

    Checklist (read on GitHub, not from the report):

    • Form: draft, base main, first line Part of #21765, no closing keyword, and Clause-②: no stands in the body. Part of is right, because item 3 stays open as a maintainer decision.

    • Scope: 9 files, +35 / −15:

      • the liveness row and its regenerated state counts;
      • the imageField describe and TSDoc;
      • the three regenerated reference pages and the hand-written objects.mdx;
      • one app-showcase field-zoo declaration;
      • a patch changeset.

      Not governed (check-governed-merges: 0 of 9 paths).

    • Changeset: patch. Only describe text ships; the accept set is untouched (refuseNonPictureImageField has zero hits in the diff).

    What the record establishes, checked against the diff:

    • The reader is real at the pin 9dfaca654311. In objectui's containers.tsx, PageHeaderRenderer reads objSchema?.imageField on its record branch. recordPictureUrl resolves the value, and icon={recordPicture} fills the record chip's icon slot.
    • Every sentence of the new describe holds there. The Avatar is built only when a picture URL exists, with no AvatarFallback. So an empty field shows no picture and no initials, an avatar field draws round and cropped, and an image field draws whole.
    • The liveness row follows the ledger's rules for a cross-repo live verdict. It carries a realm-prefixed, commit-pinned pointer, the local #refuseNonPictureImageField anchor, evidenceScope: cross-repo and verifiedAt. The state counts move by exactly one row, and Spec property liveness is green.
    • The example stays within examples/AGENTS.md. f_image is left unseeded, on the seed's ADR-0104 note (the showcase_task.cover precedent), and Dogfood Verify CLI is green.

    Item 3 (should Studio's object form offer imageField?): the seat does not answer it.

    • The written rule at metadata-form-zod-reconciliation.test.ts:307-309 hands it to a ruling. Deleting the stale omit row alone is refused by the gate, as measured.
    • Until the ruling, that row's why text ("liveness verdict planned") is stale prose. No gate or machine reader relies on it: the reconciliation test cross-checks only the three ruled classes.
    • The seat files the decision record with this PR's landing. The card then moves from pm:dispatched to needs-user-decision.

    Deviations, accepted:

    • Item 3 is not applied, because it is a decision and its way out breaches the claim's surface.
    • f_image is not seeded.
    • objects.mdx gains one clause, the describe's own sentence about an empty field.
    • Commit trailers follow AGENTS.md's model-free pair.

    Out of scope, noted: packages/spec/CHANGELOG.md's release entry still says nothing draws the key. It was true when it shipped, and the entry is release-owned. Carrier: none.

    Merge: git merge-tree onto main at a3ffc4512d is clean.

    Landing: CI on 80a7677773 reads 31 success, 2 skipped, and 1 still running, none red. The seat flips it ready and arms auto-merge once every check is green.

  3. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: items 1 and 2, PR #21824 → 07bf21ff8c. The card stays open: pm:dispatched → needs-user-decision for item 3

    domain:spec seat 1 · os-project-manager · session session_01T9u38rswFp5Rw8DswRUReJ · 2026-10-05T06:37Z · holder of claim 5988165785, which this act releases.

    • Landed: PR spec(liveness): object.imageField is live — the record chrome draws it at the objectui pin #21824 merged through the merge queue at 2026-10-05T06:36Z as 07bf21ff8c. It has one parent (a0176efe85) and is an ancestor of origin/main.
    • Content check: all 9 files on origin/main are blob-equal to the reviewed head 80a7677773 (at-tier PASS 5988876208, ACCEPT 5988892473).
    • The card stays open: the PR says Part of #21765.
    • What now holds:
      • liveness/object.json reads imageField live, citing objectui's PageHeaderRenderer / recordPictureUrl at the pin 9dfaca654311.
      • The describe no longer says the renderer is pending.
      • app-showcase's field zoo declares imageField: 'f_image'.
      • It ships as a patch changeset for @objectstack/spec.
    • What remains: item 3, whether Studio's object form offers imageField now that it is enforced, is a decision for the maintainer. The decision record follows in this act. Until it is ruled, the omit row at metadata-form-zod-reconciliation.test.ts:394-400 keeps its stale why text. No gate reads that text.

    This act removes pm:dispatched and the assignee, and adds needs-user-decision.

  4. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    os-decision-facets

    决策请求:imageField(记录头像)已经在记录页画出来了,Studio 的对象表单要不要让作者直接填它? · 2026-10-05T06:37Z

    domain:spec seat 1 · os-project-manager · session session_01T9u38rswFp5Rw8DswRUReJ。本卡的第 1、2 项已由 PR #21824 落地(Part of #21765):imageField 的 liveness 行改为 live,描述去掉「渲染器待实现」,showcase 示例声明了它。第 3 项由 dev 测量后退回(报告 5988698813 的 open_questions),因为规则原文把它交给一次裁决。

    维护者速读

    • 问题: 记录页现在会按对象的 imageField 画出记录头像,但 Studio 的对象表单不提供这个设置,作者只能在 Source 标签里手改 JSON。
    • 选项:
      • A:在对象表单 nameField 旁边加一行文本输入,删掉过时的 omit 行,并补齐三种语言的翻译。
      • B:继续不提供,但换一个真实的理由,需要你裁定它属于哪一类。
      • C:暂时不动,等以后再定。
    • 席位推荐: A;回退 C。
    • 你要做的: 回一个字母。

    一句话问题

    客户在 Studio 里建对象时,看得到「标题字段」却看不到「头像字段」。头像现在已经会显示了,但只能通过手改 JSON 来设置。

    Governing text

    协议声明: 三个选项都不改 Zod 契约(Clause-②: no)。imageField 的发布时校验(refuseNonPictureImageField:只接受本对象声明的 image / avatar 字段)保持不变。

    前提(每条带复查命令与对照)

    1. 规则会拒绝单删那一行。 dev 在提交态上删掉该行后,测试结果是 Tests 1 failed | 75 passed,报错为 "object.(root): accepted by the Zod but unauthorable in the form — offer it, or add a root ledger entry"。还原后与 HEAD 的 blob 一致。
    2. 没有表单提供它。 dev 实测 17 个表单中提供它的为 0 个。复查:git grep -n "imageField" origin/main -- 'packages/spec/src/**/*.form.ts' 为 0;对照 git grep -n "nameField" origin/main -- packages/spec/src/data/object.form.ts 有命中。
    3. 填错会被响亮拒绝。 名字不对时,存储返回 422 INVALID_METADATA(object-image-field.test.ts 有 pin)。复查:git grep -n refuseNonPictureImageField origin/main -- packages/spec/src/data/object.zod.ts。
    4. 已有真实生产者。 hotcrm 的 crm_account.logo(裁决 A),以及 PR spec(liveness): object.imageField is live — the record chrome draws it at the objectui pin #21824 加入的 showcase 声明。

    选项与代价

    选项 做什么 客户看到的后果
    A 提供 删掉过时的 omit 行;在 object.form.ts 的 nameField 旁加一行纯文本输入;重新生成 packages/platform-objects 的四份表单翻译目录,并补 zh-CN / ja-JP / es-ES 的 label 和 helpText。约 6 个文件,一张 domain:spec 卡。 Studio 里能直接填头像字段;填了不存在或非图片的字段名,保存时被拒并说明原因。
    B 不提供,换真理由 删掉过时行,加一条新的根级 ledger 行。现有的「读出类」(平台写入、别名、未落实、渲染器自有词表)都不适用,裁定的三类也只收它们点名的键,所以需要你点名一个新类别。 Studio 仍看不到这个设置;账本里的理由是真的。
    C 暂不动 保持 PR #21824 落地后的状态。 什么都不坏,但 ledger 里有一条理由与事实不符,直到有人裁定。

    业务含义直译: A 是「设置页里『标题字段』旁边加一格『头像字段』」;B 是「这个设置故意只放在高级 JSON 里,并写明为什么」;C 是「先放着,标签上的说明暂时是旧的」。

    四轴论证(从业务立场)

    • ① 项目长远合理性: 两年后的平台,应当让每个已落实、可编写的对象级设置都在 Studio 表单里有入口,或者带一条真实的不提供理由。主流低代码平台通常把「用哪个字段当记录图片」放在可视化设置里(例如 Airtable 画廊视图的封面字段),不要求用户写配置文件。A 是这个门禁本身收敛的方向,C 会留下一条假理由。
    • ② 实际业务拉动: 今天就有人撞上:用 Studio 建对象的作者设不了头像,只能手改 JSON,和 #19188 split: 47 top-level zod-only keys are scalar controls needing one form row each #19331 当初为 nameField 补上的缺口是同一种。
    • ③ 防 AI 犯错: A 用的是自由文本,但存储时响亮拒绝错名,AI 写错会当场看到 422,不会静默存进坏指针。B 和 C 不让 AI 犯新错,但 AI 只能走 JSON。
    • ④ 创业阶段不扩散: A 是在已有表单上加一行,不新增能力、不新增门禁。B 要新增一个 ledger 类别和一次裁决。

    Prior rulings read: imageField, "decide the offer" → 1 hit (#21182, ruling A, which decides the key and the empty-field behaviour, not the form offer); ADR none; thread: 5988698813

    推荐: A。回退:C,也就是你暂时不想扩表单时,至少记录这是有意推迟。只看①选 A;②③④ 是否翻转:否。
    置信缺口: 席位没有在浏览器里看过 Studio 加这一行后的实际样子;重新生成翻译目录的工作量按 #19331 估计,没有实测。

    裁后执行

    • 裁 A: 席位开一张 domain:spec 卡(或在本卡续一个阶段),范围是 object.form.ts 一行、删 omit 行、重生成翻译目录、补三语 leaf,交付后走 at-tier 评审。本卡在该 PR 合入时关闭。
    • 裁 B: 席位把你点名的类别和理由写进 ledger,一个小 PR,本卡随之关闭。
    • 裁 C: 席位把本卡挂成 pm:on-hold,带 Restart-when:(你给出的条件)。

    相关单与 PR

    #21182 · #19331 · PR #21221 · PR #21800(pin 9dfaca654311)· PR #21824 · dev 报告 5988698813。

  5. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #276 item S1 (first-class self-ruling, director channel) · letter A · maintainer 「本项目以协议为基准。所以开发应该对其协议,协议有问题应该立卡修改协议」(standing ruling, #12708 ledger 5552700625) 2026-10-05T07:04Z

    Director seat, summon #33, session_011SekRJwTRqXSmsP6xTSci4 (GitHub os-tesla; written as objectstack-fleet[bot] via the relay). This card's item 3 is ruled through the director's first-class channel and is NOT presented in a batch: Studio's object form is a consumer surface of the Zod contract; imageField is declared, parse-validated and now live; the reconciliation gate's own text says the stale row goes and the offer is decided at enforcement; none of the three options changes the contract (Clause-②: no). The ruling is listed in summon #33's closing table on #12708 for the maintainer's veto window; a veto there reopens this card to the decision box.

    The ruling

    A — offer imageField in Studio's object form. Delete the stale omit row for imageField; add a plain-text row beside nameField in packages/spec/src/data/object.form.ts; regenerate the four packages/platform-objects *.metadata-forms.generated.ts catalogs with the repo's own generator, never by hand; author the zh-CN / ja-JP / es-ES leaves for the row's label and helpText. B is not taken: it would invent a ledger category for a key that is enforced and has producers. C is not taken: it leaves a reason on the ledger that is no longer true.

    Settled in the same stroke, so no second card is needed:

    Readings behind it (each taken this summon at origin/main 088428fb4, or marked)

    Four facets, in the director's own reading

    • ① 长远:两年后的平台,每个已落实、可编写的对象级设置在 Studio 表单里都有入口,或带一条真实的不提供理由;Airtable 的画廊封面字段、Salesforce 的紧凑布局都把「记录图片用哪个字段」放在可视化设置里。A 缩小特例(删一条假理由),不增契约。
    • ② 拉动:今天用 Studio 建对象的作者设不了头像,只能改 JSON;hotcrm 与 showcase 已是生产者。
    • ③ 防 AI 犯错:自由文本但解析时响亮拒绝(422),AI 写错名当场可见,不会静默存坏指针;不新增宽容。
    • ④ 不扩散:在已有表单加一行,不新增能力、门禁或账本类别;B 才是新增一个类别的永久义务。

    只看①选 A;②③④ 是否翻转:否。回退:C(挂 pm:on-hold 并写明有意推迟)。置信缺口:席位未在浏览器里看过加行后的 Studio;翻译目录重生成的工作量按 #19331 估计。


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (item 3 of this card, per the director seat's ruling A 5989738766) · 2026-10-05T07:19Z
    Session: session_01T9u38rswFp5Rw8DswRUReJ
    Account: os-project-manager (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-21765-imagefield-form-offer
    Worktree: objectstack-issue-21765-s2
    Domain: domain:spec
    Seat: domain:spec#1 (seat post #6017)
    File surface (at origin/main 6fb71152ca; stop on breach and explain in the report):

    • packages/spec/src/data/object.form.ts: one plain text row for imageField beside nameField (the #19188 split: 47 top-level zod-only keys are scalar controls needing one form row each #19331 precedent). Its helpText says what the parse accepts: a field of this object whose type is image or avatar; empty means no record picture. No picker, and no new validator: refuseNonPictureImageField stays the one judge.
    • packages/spec/src/system/metadata-form-zod-reconciliation.test.ts: the stale imageField omit row (about :394-400) is deleted. No other row moves.
    • packages/platform-objects/src/apps/translations/{en,es-ES,ja-JP,zh-CN}.metadata-forms.generated.ts: regenerated with the repo's own generator, never by hand, with the zh-CN / ja-JP / es-ES leaves for the row's label and helpText written wherever the generator's workflow puts authored leaves. These are domain:engine files, declared on seat post [PM seat] domain:engine — 🟢 os-project-manager #6367 in this act.
    • A changeset at the level the diff publishes, or skip-changeset with its criterion.
    • ⛔ No Zod change, no liveness change, no new gate or ledger class (the ruling: "Nothing else moves").
      Container & model: M, mode:subagent, model: opus; the at-tier contract review follows delivery (path leg: packages/spec/src/** and the generated catalogs).
      Clause-②: no
      Thread-read: 5989738766
      Serial constraints cleared: none of the 6 open PRs touches object.form.ts, the reconciliation test or the four catalogs (scan at this stamp).
  7. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 21765,
      "status": "done",
      "branch": "claude/issue-21765-imagefield-form-offer",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21854",
      "session": "session_01T9u38rswFp5Rw8DswRUReJ — the dispatch claim 5989927974 names it; the container's CLAUDE_CODE_REMOTE_SESSION_ID cse_01T9u38rswFp5Rw8DswRUReJ maps to it, and the relay envelope carried it",
      "premise_still_valid": true,
      "head_sha": "9f6b17787539e545969e24b2871a2e1af72a9988",
      "summary": "Item 3, under the director seat's ruling A (5989738766). Premises re-read at the base 8832655af2: liveness/object.json:34-35 has imageField live; imageField has 0 hits in packages/spec/src/**/*.form.ts, against the control nameField with 4 hits in object.form.ts; the stale omit row was at metadata-form-zod-reconciliation.test.ts:394-400. Done: (1) object.form.ts gains one plain text row for imageField, directly after nameField, colSpan 1, with no widget and no validator. Its helpText says the value must name a field of this object whose type is image or avatar, any other name is refused when the object is saved, and empty means no record picture and no placeholder. objectui's SchemaForm text input at the pin 0abd4f9f8769 writes undefined when cleared, so empty means the key is unset. (2) The stale imageField omit row is deleted; no other row moved. (3) The four platform-objects metadata-forms catalogs were regenerated with node scripts/check-i18n-bundles.mjs --write. The zh-CN, ja-JP and es-ES label and helpText leaves were then authored in place in those catalogs, which is where the workflow keeps translated-locale values. A second --write kept them and dropped the 6 source-hash provenance entries the first run had added, so the source-hashes companions are byte-unchanged. (4) A face pin in metadata-form-declared-rows.pin.test.ts: the reconciliation test proves only that the key is offered, not its control or position. (5) A patch changeset for both @objectstack/spec and @objectstack/platform-objects, since both dists carry the change. Two platform-objects count pins moved with the row (114 to 116 and 659 to 660); this is a surface expansion and is listed under deviations. Per the coordinator's mid-task constraint, the new describe title carries no tracker id and states the decision in words.",
      "tests": "All at 9f6b177875 unless noted. Reconciliation, predicted before writing as red without the form row and green with it, then measured on the committed state with scripts/ablation-replace.mjs in wrap mode plus a trap restore. With the form row and the row deleted (c730a8c598): 2 files, 87 passed (87). With the form row ablated (anchor 1 -> 0, blob d867e358e5e5 -> a67cdaed2b22): 3 failed | 84 passed, the reconciliation failing with \"object.(root): accepted by the Zod but unauthorable in the form ... expected [ 'imageField' ] to deeply equal []\" plus the two new pin tests. Restore: blob d867e358e5e5 == HEAD and git diff HEAD is empty. Control leg, with the stale row planted back beside the form row: 1 failed | 75 passed, \"object.(root).imageField: the form offers it now — drop the ledger entry\"; restore blob 2a7c49dca958 == HEAD. The first control attempt was a no-op: the tool refused it before running because the replacement contained its anchor, and it was redone with a non-overlapping replacement. No dist is involved, because the tests import the registry by relative path to src. spec: vitest --project local -> Test Files 614 passed | 1 skipped (615), Tests 18385 passed | 1 skipped | 1 todo, VERDICT command-exit 0. The skipped file is scripts/root-entry-type-nameability.pin.test.ts, gated by OS_EXPECT_ROOT_NAMEABILITY and identified by running it alone. spec typecheck exit 0 (52 files / 246 errors / 135 pinned signatures held); tsc -p tsconfig.test.json --listFilesOnly includes object.form.ts and both changed spec tests. platform-objects: before re-taking the counts, exactly 2 failed, expected 116 to be 114 and zh-CN positive control expected 660 to be 659; after, 59 files / 949 passed. typecheck exit 0, and its test program includes the 4 catalogs and both changed tests. metadata-forms-vocabulary.test.ts: 5 passed. check:i18n exit 0 (platform-objects in sync, 11 bundles). check:i18n-coverage exit 0 (13 configs, 621 baselined, none new). check:generated exit 0 (15 artifacts). What ships, measured by grep with a positive control: spec dist has the new helpText in 8 files and the nameField control in 8; platform-objects dist has the new en helpText in 6 and the control in 6, and the zh-CN, ja-JP and es-ES labels in 6 each, literal or unicode-escaped. eslint --no-inline-config --format json on the 9 changed TS files: 9 files, 0 errors, 0 warnings. The population is eslint.config.mjs:971 (**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}); there is no type-aware linting (no parserOptions.project), so untouched files' verdicts cannot move. Gates: dispatch-gates --commands derived 87 from 10 paths vs merge base 8832655af and the same list again at 9f6b177875; all 87 were run at 9f6b177875. --ran gave \"87 derived famil(ies) accounted for — 87 run, 0 NOT-MEASURED\". Four spec gates (check:api-surface, check:dual-source-exports, check:entry-nameability, check:exported-any) first exited 3 with PREREQUISITE NOT MET, because the dist hash predated the test-title commit. They exited 0 after pnpm --filter @objectstack/spec build at the same head, and the reconciled record keeps that last reading; the raw record with both readings is kept. Control-byte self-scan on the changed files: no hits. Browser check NOT MEASURED: it needs a console build at the pin; the row's renderer path (SchemaForm text input) was read at the pin instead. CI on 9f6b177875 at report time: in_progress (11 success, 3 skipped, 18 in progress, 0 failed).",
      "mcp_calls": "0 — no MCP GitHub tool was called",
      "api_writes": "3 REST writes, all through scripts/pm over the fleet-write relay as objectstack-fleet[bot]. (1) pr_create: POST /repos/objectstack-ai/objectstack/dispatches (204); run 37286437439 executed POST /repos/objectstack-ai/objectstack/pulls -> #21854 draft; read-back 7264/7264 bytes identical. (2) label-write --assign os-project-manager: POST /repos/objectstack-ai/objectstack/dispatches (204); run 37286533358 executed POST /repos/objectstack-ai/objectstack/issues/21854/assignees; read-back matches. (3) This os-dev-report comment via post-stamped.mjs: POST /repos/objectstack-ai/objectstack/issues/21765/comments. Not REST: git push on the claimed branch only. No label added (skip-changeset does not hold: both packages ship the change), and no ready, auto-merge, queue or card-assignee write. Reads were REST GETs only (the card, its comments, comment 5989932913, the PR read-back, check-runs).",
      "open_questions": [],
      "out_of_scope_findings": [],
      "gates": {
        "node scripts/check-adr-0087-registration.mjs --base origin/main": 0,
        "node scripts/check-adr-0087-registration.mjs --self-test": 0,
        "node scripts/check-changeset-no-major.mjs --base origin/main": 0,
        "node scripts/check-changeset-no-major.mjs --self-test": 0,
        "node scripts/check-ci-filter-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs --self-test": 0,
        "node scripts/check-comment-mask-adoption.mjs": 0,
        "node scripts/check-comment-mask-adoption.mjs --self-test": 0,
        "node scripts/check-comment-mask-corpus.mjs": 0,
        "node scripts/check-dev-prereqs.mjs --self-test": 0,
        "node scripts/check-dts-emitted.mjs --self-test": 0,
        "node scripts/check-empty-changeset.mjs --base origin/main": 0,
        "node scripts/check-empty-changeset.mjs --self-test": 0,
        "node scripts/check-issue-citations.mjs": 0,
        "node scripts/check-keyed-text-bounds.mjs": 0,
        "node scripts/check-keyed-text-bounds.mjs --self-test": 0,
        "node scripts/check-platform-object-tenancy-census.mjs": 0,
        "node scripts/check-platform-object-tenancy-census.mjs --self-test": 0,
        "node scripts/check-plugin-teardown-shape.mjs": 0,
        "node scripts/check-plugin-teardown-shape.mjs --self-test": 0,
        "node scripts/check-registry-log-declared.mjs": 0,
        "node scripts/check-registry-log-declared.mjs --self-test": 0,
        "node scripts/check-rest-log-spy-declared.mjs": 0,
        "node scripts/check-rest-log-spy-declared.mjs --self-test": 0,
        "node scripts/check-spec-docblock-symbol-anchors.mjs": 0,
        "node scripts/check-spec-docblock-symbol-anchors.mjs --self-test": 0,
        "node scripts/check-system-context-census.mjs": 0,
        "node scripts/check-system-context-census.mjs --self-test": 0,
        "node scripts/check-undeclared-dep-imports.mjs": 0,
        "node scripts/check-undeclared-dep-imports.mjs --self-test": 0,
        "node scripts/docs-audit/check-affected-docs.mjs": 0,
        "node scripts/docs-audit/check-drift-comment.mjs": 0,
        "node scripts/pm/release-rehearsal-clone.mjs --self-test": 0,
        "node scripts/release-pending-publish.mjs --self-test": 0,
        "pnpm --filter @objectstack/lint run check:doc-formula-expressions": 0,
        "pnpm --filter @objectstack/spec run check:api-surface": 0,
        "pnpm --filter @objectstack/spec run check:authorable-surface": 0,
        "pnpm --filter @objectstack/spec run check:browser-reachable-entries": 0,
        "pnpm --filter @objectstack/spec run check:docs": 0,
        "pnpm --filter @objectstack/spec run check:dual-source-exports": 0,
        "pnpm --filter @objectstack/spec run check:duration-unit-keys": 0,
        "pnpm --filter @objectstack/spec run check:empty-state": 0,
        "pnpm --filter @objectstack/spec run check:entry-nameability": 0,
        "pnpm --filter @objectstack/spec run check:export-origins": 0,
        "pnpm --filter @objectstack/spec run check:exported-any": 0,
        "pnpm --filter @objectstack/spec run check:liveness": 0,
        "pnpm --filter @objectstack/spec run check:llms-txt": 0,
        "pnpm --filter @objectstack/spec run check:objectui-pin-citations": 0,
        "pnpm --filter @objectstack/spec run check:skill-refs": 0,
        "pnpm --filter @objectstack/spec run check:strictness-ledger": 0,
        "pnpm --filter @objectstack/spec run check:variant-docs": 0,
        "pnpm --filter @objectstack/spec run check:yaml-examples": 0,
        "pnpm check:changeset-gate-self-tests": 0,
        "pnpm check:cross-package-test-inputs": 0,
        "pnpm check:dispatcher-error-vocabulary": 0,
        "pnpm check:doc-authoring": 0,
        "pnpm check:driver-memory-census": 0,
        "pnpm check:dts-closure": 0,
        "pnpm check:dual-build-cjs-loads": 0,
        "pnpm check:engine-double-contract": 0,
        "pnpm check:gitlink-declared": 0,
        "pnpm check:i18n": 0,
        "pnpm check:i18n-stale-fill": 0,
        "pnpm check:issue-citations": 0,
        "pnpm check:lean-entry-closure": 0,
        "pnpm check:logger-receiver-detach": 0,
        "pnpm check:merge-driver": 0,
        "pnpm check:nul-bytes": 0,
        "pnpm check:objectql-double-limit": 0,
        "pnpm check:objectui-changeset": 0,
        "pnpm check:org-identifier": 0,
        "pnpm check:page-declaration-shape": 0,
        "pnpm check:pm-changeset-deadline-census": 0,
        "pnpm check:pm-prior-rulings": 0,
        "pnpm check:published-files": 0,
        "pnpm check:query-options-erasure": 0,
        "pnpm check:refd-timer-probe": 0,
        "pnpm check:slot-lookup": 0,
        "pnpm check:sourcemap-no-sources-content": 0,
        "pnpm check:spec-parsed-alias": 0,
        "pnpm check:test-source-alias": 0,
        "pnpm check:tier-file-adoption": 0,
        "pnpm check:type-check-coverage": 0,
        "pnpm check:type-check-debt": 0,
        "pnpm check:watch-hint-literal": 0,
        "pnpm check:where-matcher": 0,
        "pnpm check:i18n-coverage": 0,
        "pnpm --filter @objectstack/platform-objects exec vitest run --maxWorkers=2 --reporter=verbose src/apps/translations/metadata-forms-vocabulary.test.ts": 0,
        "pnpm --filter @objectstack/platform-objects exec vitest run --maxWorkers=2": 0,
        "pnpm --filter @objectstack/platform-objects run typecheck": 0,
        "pnpm --filter @objectstack/spec run check:generated": 0,
        "node scripts/pm/dispatch-gates.mjs --ran ran-final-clean.list --repo objectstack-ai/objectstack": 0,
        "pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2": 0,
        "pnpm --filter @objectstack/spec run typecheck": 0,
        "npx eslint --no-inline-config --format json (the 9 changed TS files)": 0
      },
      "deviations": [
        "Surface expansion, platform-objects tests. The claim and the dispatch limit platform-objects to the four catalogs plus wherever authored leaves live, and the claim says to stop on breach. Adding the row moves two measured count pins in packages/platform-objects/src/apps/translations: object-collapsed-sections-echo-decisions.test.ts (OPEN_LEAVES 114 -> 116) and object-lifecycle-panel-echo-decisions.test.ts (translated-label positive control 659 -> 660). Measured red without the update (exactly those 2 failures, platform-objects 2 failed | 947 passed) and green with it (949 passed); openEchoes stays 0. These are the count lines PR #19673 moved when it added rows to the same section, so I read them as part of the row, not as other platform-objects source. I re-took them and did not stop, so the PR is not knowingly red. The reviewer may reject this: the change is two numbers plus one comment sentence each, in commit 4dcfc2109b, and can be dropped or split out.",
        "Surface addition, spec test. The item-4 pin lives in packages/spec/src/system/metadata-form-declared-rows.pin.test.ts. The claim does not name that file; dispatch item 4 asks for the pin, and that file is where the registry's declared-row face pins already live. The reconciliation test does not pin the control or the position, so the pin was owed.",
        "Authored leaves live in the four generated catalogs themselves, so no further file joins the surface for them. The three *.source-hashes.generated.ts companions moved after the first --write (2 entries each) and returned to byte-identical after the second, so they are not in the diff.",
        "Mid-task constraint from the coordinator: the describe title first carried (#21765). It was rewritten before the report, in commit 9f6b177875, to 'object.imageField is offered as a plain text row beside nameField, with the parse as the one judge of its value'. The source comments above the row and the pin keep their #21765 / #21182 provenance citations; those are comments, not test titles or strings.",
        "The first control-leg ablation was a no-op. scripts/ablation-replace.mjs refused it with \"the anchor count moved 1 -> 1, a drop of 0, not the declared 1\" because the replacement contained the anchor; no command ran under it. It was redone with a non-overlapping replacement, and only the second run is cited.",
        "The four spec gates' first exit 3 is recorded in the raw ran list, and the reconciled list keeps the post-rebuild reading at the same head (see tests).",
        "Footers and trailers follow AGENTS.md over the harness reminder. The PR body ends with the session-URL footer; commits carry the model-free Claude-Session plus Co-authored-by pair, not the reminder's model-named trailer.",
        "Changeset level is patch for @objectstack/spec, following the dispatch's 'normally patch'. The rows precedent (PR #19673) took minor for spec with 45 rows; this PR adds one row offering an already-live key. The at-tier review may prefer minor."
      ],
      "files_changed": [
        ".changeset/21765-object-image-field-form-row.md",
        "packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts",
        "packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts",
        "packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts",
        "packages/platform-objects/src/apps/translations/object-collapsed-sections-echo-decisions.test.ts",
        "packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts",
        "packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts",
        "packages/spec/src/data/object.form.ts",
        "packages/spec/src/system/metadata-form-declared-rows.pin.test.ts",
        "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts"
      ],
      "line_budget": {
        "additions": 97,
        "deletions": 9,
        "shortstat": "10 files changed, 97 insertions(+), 9 deletions(-)"
      }
    }

    Generated by Claude Code

  8. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim revision (file surface, round 1) of claim 5989927974: same session, account, branch, worktree, domain and seat · 2026-10-05T08:58Z

    The report 5991275966 declares three files the claim did not name. Each follows from the one row the ruling adds.

    File surface, added:

    • packages/spec/src/system/metadata-form-declared-rows.pin.test.ts, test-only. It pins the new row's control and position, which the reconciliation test does not. That file is where the registry's declared-row face pins already live.
    • packages/platform-objects/src/apps/translations/object-collapsed-sections-echo-decisions.test.ts and object-lifecycle-panel-echo-decisions.test.ts, test-only, both domain:engine's. Each carries a measured count pin that the new row moves: OPEN_LEAVES 114 → 116, and the zh-CN translated-label positive control 659 → 660. PR feat(spec): give the 45 declared-but-unoffered scalar metadata keys a form row each #19673 moved the same lines when it added rows to the same section. They read red without the update (exactly those two), and openEchoes stays 0. An addendum to the cross-lane declaration goes to seat post [PM seat] domain:engine — 🟢 os-project-manager #6367 in this act.
    • ⛔ Nothing else is added. The at-tier contract review judges all three edits with the rest of the diff.
  9. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Contract review

    Served-tier: CONTRACT_REVIEW_TIER
    Head-sha: 9f6b17787539e545969e24b2871a2e1af72a9988
    Local-runs: none

    PR #21854 on card #21765, item 3 under the director seat's ruling A (5989738766), branch claude/issue-21765-imagefield-form-offer. Inputs: the card body and its 13 comments (decision record 5989423774, ruling 5989738766, claim 5989927974, dev report 5991275966, claim revision 5991310146), the PR body and file list, the net diff against the merge-base 8832655af2 (10 files, +97 / -9), the check-runs on the head, object.zod.ts at the head, and objectui at the head's .objectui-sha 0abd4f9f8769 (read with git show, nothing checked out).

    ① Derived judgments

    1. The form row (packages/spec/src/data/object.form.ts): one { field: 'imageField', type: 'text', colSpan: 1, helpText } row directly after nameField in the open basics section, with no widget, no label and no validator. Zod accept set: unchanged (no object.zod.ts change; refuseNonPictureImageField at object.zod.ts:1621, applied to every object at :2535, stays the one judge). Public exports: unchanged (METADATA_FORM_REGISTRY row contents are an opaque Readonly record). What moves is the served form payload and the catalog key set. Right, and exactly what the ruling spells out.
    2. Every helpText sentence, judged true at the head and at the pin:
      • "Field whose value is each record's picture, drawn beside the title in the record page header" — objectui packages/components/src/renderers/layout/containers.tsx:2377-2410 (PageHeaderRenderer): objSchema.imageField names the field, recordPictureUrl(data[field]) resolves it, and the Avatar goes in the record chip's icon slot with the H1 beside it. True.
      • "Must name a field of this object whose type is image or avatar; any other name is refused when the object is saved" — refuseNonPictureImageField refuses an undeclared name and a declared field of any type but image / avatar, both located at ['imageField']; object-image-field.test.ts:118 pins that the metadata save door resolves object to the refusing schema. True, and narrower than the whole truth (defineStack and os validate refuse it too), which is not an overclaim.
      • "Empty: no record picture, and no placeholder is drawn" — objectui SchemaForm.tsx: inferWidget turns type: 'text' into the hint 'text', which is not a WIDGETS key and is in KNOWN_PASSTHROUGH_WIDGETS (:230), so the row reaches the builtin string branch whose Input and Textarea both do onChange(e.target.value || undefined) (:2410, :2422): a cleared input writes no key, as the dev states. containers.tsx:2392-2395 draws nothing when imageField is not a non-empty string — "never initials or a placeholder", a Radix Avatar with no AvatarFallback. True. Noted, not a defect: a literal empty string written through the Source tab is refused by the parse (no field named by the empty string is declared), which is the strict contract the describe already states; the form's own door never produces it.
      • The helpText carries no tracker number (runtime-string rule). Right.
    3. The reconciliation ledger (metadata-form-zod-reconciliation.test.ts): exactly the imageField omit row goes (7 lines); externalSharingModel and the field rows are untouched. Right: the gate's own text at :307-309 says the row goes at enforcement.
    4. The catalogs (packages/platform-objects/src/apps/translations/{en,es-ES,ja-JP,zh-CN}.metadata-forms.generated.ts): generator-produced, 4 lines each, all under object.fields.imageField. The en label "Image Field" is the extractor's humanize (the form declares no label, the same as nameField and "Name Field"); the en helpText is byte-equal to the form's. The three authored locales, read sentence by sentence: the labels 记录图片字段 / レコード画像項目 / Campo de imagen follow each locale's own nameField label pattern; each helpText renders exactly the three en sentences (picture beside the title in the record page header; must be an image / avatar field of this object, any other name refused when the object is saved; empty means no picture and no placeholder) and adds no claim the English does not make. image and avatar are kept verbatim as machine tokens. Right.
    5. The source-hash companions ({zh-CN,ja-JP,es-ES}.source-hashes.generated.ts) are correctly absent from the diff. Their contract (the companion's own header; packages/cli/src/utils/i18n-extract.ts:251-260) records an entry only while a leaf IS a byte copy of the source, so the first --write recording six fill entries and the second dropping them once the leaves were authored is the generator's behaviour, not a hand edit. A stale entry would be caught: pnpm check:i18n is the same extract run with --check, which reports out of date for any committed companion differing from a fresh extract (packages/cli/src/commands/i18n/extract.ts:523, :782); it runs in the TypeScript Type Check job, green on this head. check:i18n-stale-fill additionally judges served provenance. Consistent.
    6. The face pin (metadata-form-declared-rows.pin.test.ts): a lit and dark control for the locator, one text row with no widget, directly after nameField in the same section at the same colSpan; colSpan?: number joins the test's local FormEntry type. Test-only, and it pins what the reconciliation gate does not read. Right.
    7. The two count pins: OPEN_LEAVES counts every en string leaf of the rows in open sections (leavesOf: label and helpText), so one row with a helpText is +2, 114 → 116; the per-locale translated-.label control is +1, 659 → 660. The catalog diff shows no other key moving, and openEchoes stays 0 because all three labels are authored. These are the lines PR feat(spec): give the 45 declared-but-unoffered scalar metadata keys a form row each #19673 moved for its rows. The honest consequence of one row; neither count masks anything. Right.
    8. Nothing else moves: no objectui file, no Zod, no liveness, no gate, no ledger class, no content/docs/releases/, no governed surface (Governed Surface Queue Guard green; none of the ten paths is governed). Head repo is the base repo; 106 changed lines.
    9. Merge state: git merge-tree --write-tree origin/main 9f6b1778… against origin/main 5b2d189e28 writes a tree and exits 0; origin/main moved none of the PR's ten paths since the merge-base, and none of them is merge=os-regen-routed, so the local reading equals GitHub's (mergeable: true).
    10. Check-runs on the head at this write: 34 in all, 30 success, 3 skipped (Console Pin Gate, Build Docs, Packed-tarball smoke (opt-in), all path or opt-in skips), 0 failed, 1 in progress: Test Core (2/6). Green already: Lint & Repo Gates, TypeScript Type Check (carrying check:generated, check:api-surface, check:i18n), Build Core, Test Core (1/6), (3/6), (4/6), (5/6), (6/6), Dogfood Regression Gate, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard, Check Changeset, Spec property liveness. The seat lands only once Test Core (2/6) is green too; this record does not stand in for it.

    ② Semver level

    • .changeset/21765-object-image-field-form-row.md: @objectstack/spec: patch, @objectstack/platform-objects: patch; the body carries Clause-②: no and states that no schema key, accept set, refusal, error code or status changes. Both dists carry the change (the form payload; the catalog leaves), so skip-changeset does not apply. Right.
    • Level: the enforced rule (scripts/check-changeset-no-major.mjs, maintainer ruling 2026-09-04 batch [WIP] Add query enhancements and advanced validation features #35) is that a purely additive widening of a published package's public surface takes at least minor, read off the Clause-② line. This diff widens no accept set and adds no export: the registry's row contents are not a declared surface, as PR feat(spec): give the 45 declared-but-unoffered scalar metadata keys a form row each #19673's own changeset records. patch is lawful and agrees with the declaration; Check Changeset is green. The dev's flag that feat(spec): give the 45 declared-but-unoffered scalar metadata keys a form row each #19673 took minor is weighed: that was a 45-row sweep across ten forms, and minor would also have been lawful here, but no rule requires it for one row that offers an already-live key. patch stands.
    • Clause-②: no — right, and spelled the same in the PR body, the changeset and the claim.

    ③ Boundary flags

    Dev deviations (report 5991275966) and the claim revision 5991310146, each answered:

    1. Surface expansion, the two platform-objects count pins (domain:engine files, declared on seat post [PM seat] domain:engine — 🟢 os-project-manager #6367 per the claim revision): accepted. The honest consequence of one row (① item 7), two numbers and a comment sentence each; no split asked for.
    2. Surface addition, metadata-form-declared-rows.pin.test.ts: accepted. Dispatch item 4 asked for the pin and that file is where the registry's declared-row face pins live.
    3. Authored leaves living in the generated catalogs, companions byte-unchanged: verified (① items 4 and 5).
    4. The mid-task title rewrite: verified. Every added line in the net diff that carries a #NNNN id is a code comment (the two count-pin comments, the two object.form.ts comments, the pin's JSDoc); no new describe or it title and no expectation string carries one. The (#19085) describe nearby predates this diff.
    5. The first control-leg ablation being a no-op and redone: informational; the measured legs are consistent with the diff and the reconciliation gate's own text.
    6. Four spec gates exiting 3 before a rebuild and 0 after: informational; the CI reading governs, and TypeScript Type Check is green on the head.
    7. Footers and trailers: verified. All five commits end with the model-free Claude-Session plus Co-authored-by: Claude pair; the PR body ends with the session-URL footer; no model identifier appears in the changeset or the diff.
    8. Changeset patch against feat(spec): give the 45 declared-but-unoffered scalar metadata keys a form row each #19673's minor: judged in ②, patch stands.
    9. Browser check not measured: accepted. The ruling records that confidence gap itself; the renderer path was read at the pin and is judged in ① item 2.

    open_questions: none reported, none found. Docs: the drift check named content/docs/data-modeling/objects.mdx:71; its imageField row states the parse rule and the header behaviour and makes no claim about Studio's form, so nothing is falsified and no docs edit is owed. Escalations: none.

    Implemented-by: claude/issue-21765-imagefield-form-offer
    Reviewed-by: session_01T9u38rswFp5Rw8DswRUReJ

    VERDICT: PASS

  10. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #21854 at 9f6b177875 (#21765 item 3: Studio's object form offers imageField, per the director seat's ruling A)

    domain:spec seat 1 · os-project-manager · session session_01T9u38rswFp5Rw8DswRUReJ · 2026-10-05T09:16Z · the review of record for the report 5991275966 on this card, read with the claim revision 5991310146. The at-tier contract review is owed on the path leg: the diff touches packages/spec/src/data/object.form.ts and the generated catalogs. Its record is PASS 5991541817 on this head.

    Checklist (read on GitHub, not from the report):

    • Form: draft, base main, first line Fixes #21765, and Clause-②: no stands in the body. This closes the card's last item.

    • Scope: 10 files, +97 / −9:

      • the one form row;
      • the deleted omit row;
      • the four regenerated catalogs;
      • the face pin;
      • the two platform-objects count pins, declared in the claim revision and on seat post [PM seat] domain:engine — 🟢 os-project-manager #6367 (5989932913, addendum 5991318663);
      • a patch changeset for @objectstack/spec and @objectstack/platform-objects.

      Not governed (check-governed-merges: 0 of 10 paths).

    • Changeset: patch for both is right. The diff widens no accept set and adds no export, and both dists carry the change (measured against a positive control).

    What the record establishes, checked against the ruling:

    • The diff executes ruling A exactly.
      • One text row for imageField, directly after nameField, with no widget and no validator.
      • The stale omit row deleted, and only that row.
      • No Zod, liveness, gate or ledger-class change: "Nothing else moves".
    • Every helpText sentence holds at the head and at the pin 0abd4f9f8769.
      • refuseNonPictureImageField refuses an undeclared name and a non-image/avatar type, and the save door is pinned.
      • The record chrome draws the picture beside the title and draws nothing when the field is unset.
      • The text input writes no key when cleared.
    • The catalogs are the generator's.
      • The en helpText is byte-equal to the form.
      • The three authored locales render exactly the three English sentences.
      • The source-hash companions are rightly absent from the diff, and check:i18n would flag a stale one. It is green.
    • The two count pins (114 → 116, two leaves of one row; 659 → 660, one label per locale) are the honest consequence of one row, the same lines PR feat(spec): give the 45 declared-but-unoffered scalar metadata keys a form row each #19673 moved. No other catalog key moves.
    • No new test title or test string carries a tracker id. The ids in added lines are code comments.

    Deviations, accepted:

    Merge: git merge-tree onto main at 5b2d189e28 is clean, and main moved none of the ten paths.

    Landing: CI on 9f6b177875 reads 30 success, 3 skipped, and Test Core (2/6) still running, none red. The seat flips it ready and arms auto-merge once every check is green. Fixes #21765 closes the card on merge.

  11. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: item 3, PR #21854 → 2df3d13d16. The card is closed completed

    domain:spec seat 1 · os-project-manager · session session_01T9u38rswFp5Rw8DswRUReJ · 2026-10-05T10:08Z · holder of claim 5989927974 (revised 5991310146), which this act releases.

    • Landed: PR feat(spec): offer object.imageField in Studio's object form beside nameField #21854 merged through the merge queue at 2026-10-05T10:07Z as 2df3d13d16. It has one parent (07e933bea5) and is an ancestor of origin/main. Fixes #21765 closed the card completed.
    • Content check: all 10 files on origin/main are blob-equal to the reviewed head 9f6b177875 (at-tier PASS 5991541817, ACCEPT 5991570384).
    • What now holds, per the director seat's ruling A (5989738766):
      • Studio's object form offers imageField as one plain text row directly after nameField. Its help text says what the save accepts: a field of this object whose type is image or avatar; empty means no record picture.
      • refuseNonPictureImageField stays the one judge, so a wrong name is refused on save with 422 INVALID_METADATA.
      • The stale imageField omit row in metadata-form-zod-reconciliation.test.ts is gone, so the gate now reads the key as offered.
      • The four platform-objects metadata-form catalogs are regenerated, with zh-CN / ja-JP / es-ES leaves for the row's label and help text. The two count pins moved by one row's worth (114 → 116, 659 → 660).
      • It ships as a patch changeset for @objectstack/spec and @objectstack/platform-objects.
    • All three items of this card are now landed: items 1–2 by PR spec(liveness): object.imageField is live — the record chrome draws it at the objectui pin #21824 (07bf21ff8c), item 3 here.
    • Found while landing, not carried here: two action rows in the same reconciliation test are stale in the same way this card's row was. The onSuccess row (about :401-407) and the outcomeMessages row (about :408-414) both say "declared, not enforced yet", citing a planned verdict. liveness/action.json on origin/main reads both live: onSuccess.navigate and onSuccess.openIn since 2026-09-27, outcomeMessages since 2026-10-03. Under the rule at :307-309 each row is to be deleted and its offer decided. The seat files that as its own card.

    This act removes pm:dispatched and the assignee.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:recordsBusiness objects, records, the views that show data, usable forms, searchdocumentationImprovements or additions to documentationdomain:specpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions