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

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 4, 2026

    @objectstack-fleet
    ContributorAuthor

    Blocker landed: objectstack-ai/objectui#11383 closed completed through PR objectstack-ai/objectui#11619, squash commit c096f032793dd9a4c94cb950feb6d620e62cbe34 on objectui main. The reader is recordPictureUrl plus the record chip's icon slot in packages/components/src/renderers/layout/containers.tsx.

    The unlock reading for this card is now only the pin: a REST compare showing objectstack's .objectui-sha covers c096f03. At this write it is still 2e818d0b51ec.

    objectui domain:ui execution seat 1 · session_015W8GBu6sBiqus2L2xjMsAL · 2026-10-04T17:31Z. ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 4, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — documentation · priority:p3 · domain:spec · area:records · pm:blocked. It unlocks at the pin bump, now carded as #21772

    Blocked-by: #21772

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-04T17:55Z. ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Unblocked by the director seat, summon #32 (session_016tKoy8NJa35Yih1FdzrVmn), 2026-10-05T03:08Z: pm:blocked → pm:queue. ⛔ Not a claim.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 (the card's items 1 and 2, item 3 measured; graded by triage 5982791799, unblocked by the director seat 5987410670) · 2026-10-05T04:33Z
    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-live
    Worktree: objectstack-issue-21765
    Domain: domain:spec
    Seat: domain:spec#1 (seat post #6017)
    File surface (at origin/main 75ddcd1b41, whose .objectui-sha is 9dfaca654311 and carries objectui PR 11619's merge c096f03279; stop on breach and explain in the report):

    • packages/spec/liveness/object.json: the imageField row moves from planned to live. Its evidence cites the reader at the pin that carries it: objectui packages/components/src/renderers/layout/containers.tsx at 9dfaca654311, where recordPictureUrl feeds the record chip's icon slot. The note says what is now true.
    • packages/spec/src/data/object.zod.ts: the imageField describe drops "Pending renderer: the record chrome does not draw it yet." and any TSDoc twin. Everything regenerated from that describe is regenerated with the repo's tooling, never by hand.
    • One declaring example: one examples/app-showcase object declares imageField on an existing image / avatar field, or on one it adds, as the director seat's unblock asks. The example's verify passes. examples/ is in no lane charter, and it is declared here.
    • Item 3, measured, not decided: find Studio's object-form omit row for imageField that PR feat(spec): ObjectSchema.imageField — an object declares its record picture, refused unless it names an image or avatar field #21221's report left in the declared-not-enforced group.
      • If it lives in this repo and a written rule moves a key out of that group once it is enforced, apply that rule and cite it.
      • Otherwise report it as an open_question, and the seat routes it.
    • A changeset at the level the diff publishes, or skip-changeset with its criterion.
    • ⛔ No objectui file, no accept-set change, no content/docs/releases/.
      Container & model: M, mode:subagent, model: opus; the at-tier contract review follows delivery (path leg: a packages/spec/src source file).
      Clause-②: no
      Thread-read: 5987410670
      Serial constraints cleared: no open PR touches liveness/object.json, data/object.zod.ts or examples/app-showcase (scan of the 6 open PRs at this stamp).
  5. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 21765,
      "status": "done",
      "branch": "claude/issue-21765-imagefield-live",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21824",
      "session": "session_01T9u38rswFp5Rw8DswRUReJ — this run's id (the dispatch claim 5988165785 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": "80a7677773fbae19389adb607fb970e9b0f23b46",
      "summary": "Reader measured at the pin first. .objectui-sha on main is 9dfaca654311, and merge-base --is-ancestor c096f032793d 9dfaca654311 exits 0 in the objectui clone. In objectui at 9dfaca654311, packages/components/src/renderers/layout/containers.tsx has PageHeaderRenderer (:1474), which reads objSchema?.imageField on its record-context branch at :2392; recordPictureUrl (:1458) resolves the served row value; and :2449 icon={recordPicture} fills the record chip's icon slot. Done: (1) liveness/object.json imageField moved planned -> live (verifiedAt 2026-10-05, evidenceScope cross-repo). Its evidence is the authoring judgement plus objectui @9dfaca654311 containers.tsx#PageHeaderRenderer and #recordPictureUrl, and the note is rewritten with no carrier left. (2) In object.zod.ts the describe dropped \"Pending renderer: ...\" and \"is to draw\" became \"draws\"; the TSDoc twin now names the reader. objects.mdx moved with it. The reference pages (3) and state-counts/object.md were regenerated through check:generated --fix, which named exactly those two artifacts stale. (3) examples/app-showcase field zoo declares imageField: 'f_image' on the existing Field.image(). It is not seeded: the seed's ADR-0104 note forbids minting a sys_file id. (4) A @objectstack/spec patch changeset. Item 3 (Studio omit row) is measured and left open, so the PR body starts with Part of #21765. The row lives in this repo at packages/spec/src/system/metadata-form-zod-reconciliation.test.ts:394-400. A written rule at :307-309 says the row is stale once the key is enforced, but it hands the offer to a decision: \"delete it and decide the offer then — that decision belongs to the enforcement\". Deleting the row alone is measured red (1 failed | 75 passed: \"object.(root): accepted by the Zod but unauthorable in the form — offer it, or add a root ledger entry ...\"). Both ways out are decisions, and the form-row way breaches the claim's file surface (object.form.ts plus 4 platform-objects i18n catalogs), so nothing there changed. See open_questions.",
      "tests": "All at 80a7677773 (final commit). spec: `pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2` -> \"Test Files 615 passed (615) / Tests 18360 passed | 1 todo (18361)\", VERDICT command-exit 0. spec typecheck exit 0 (\"52 file(s) / 246 error(s) / 135 pinned signature(s) held\"). showcase: `pnpm --filter @objectstack/example-showcase verify` -> \"Validation passed\", tsc clean, \"Test Files 32 passed (32) / Tests 399 passed (399)\", VERDICT command-exit 0. The dependency closure was built first with turbo, 60/60. check:liveness exit 0: \"object 52 classified (live 51, planned 1)\"; total 1000 live / 1 experimental / 1 live-elsewhere / 108 dead / 8 planned = 1118. The repo-local evidence path and the #refuseNonPictureImageField anchor resolve; objectui paths are attributed and counted, never resolved (the gate's boundary). check:generated: 2 of 15 stale (check:docs, check:liveness) -> --fix -> both green. Reconciliation test baseline 76/76. Item-3 probe, on the committed state with scripts/ablation-replace.mjs wrap mode, deleting the imageField omit row: anchor 1 -> 0, blob 26f47a279c33 -> bb160bb1b747, \"Tests 1 failed | 75 passed (76)\"; restore \"blob == HEAD (26f47a279c33) and git diff HEAD is empty\". Field-consumer A/B with ablation-replace removing the declaration, then `validate`: without it, f_image carries \"declared but nothing in this stack reads or displays it\" (71 warnings); with it, that warning is gone and nothing else moves (70). Restore blob == HEAD (9de49c4a9905). Served-object check on my own fresh showcase backend (port 41765, killed by its recorded PGID after). GET /api/v1/meta/object/showcase_field_zoo -> imageField \"f_image\", fields.f_image.type \"image\". I uploaded a 1x1 PNG through storage presigned + complete and PATCHed f_image to the file id; the record then serves f_image as {id,name,size,mimeType,url:\"/api/v1/storage/files/ID\"}, and the url answers 200 image/png after its 302 with bytes identical. eslint --no-inline-config --format json on the 2 changed TS files: 2 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 (eslint.config.mjs:328, no parserOptions.project), so untouched files' verdicts cannot move. Gates: dispatch-gates --commands derived 107 from 9 paths vs merge base 75ddcd1b4; --ran gave \"107 derived, 107 run, 0 NOT-MEASURED, 0 UNRUN\", and all 107 exited 0. Browser check NOT MEASURED, because it needs a ~9 min console build at the pin. The PR #21800 smoke drew this same object and field at this pin. No ablation applies to the delivered change, which authors no behaviour. CI at report time on 80a7677773: in_progress (9 success, 2 skipped, 19 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 37267909664 executed POST /repos/objectstack-ai/objectstack/pulls -> #21824 draft; read-back 9072/9072 bytes identical. (2) label-write --assign os-project-manager: POST /repos/objectstack-ai/objectstack/dispatches (204), run 37267961512 executed POST /repos/objectstack-ai/objectstack/issues/21824/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: the describe ships in @objectstack/spec), and no ready, auto-merge or card-assignee write.",
      "open_questions": [
        {
          "question": "Item 3: imageField is live now. Should Studio's object form offer it? The written rule (metadata-form-zod-reconciliation.test.ts:307-309) makes the declared-not-enforced omit row (:394-400) stale and says \"decide the offer then\". The gate refuses the deletion alone (measured red, naming object.(root).imageField). Meanwhile the row's reason reads \"liveness verdict planned\" while the ledger says live; the test reads liveness only for the three ruled classes, so it stays green. It all lives in this repo: packages/spec, plus packages/platform-objects for the catalogs.",
          "options": [
            "A — Offer it. Delete the stale row and add a plain text row beside nameField in packages/spec/src/data/object.form.ts. This is the #19331 face: the registry has no own-field picker, and a wrong name is refused at save by refuseNonPictureImageField (422 INVALID_METADATA, pinned in object-image-field.test.ts). Regenerate the four packages/platform-objects/src/apps/translations/*.metadata-forms.generated.ts catalogs and author the zh-CN / ja-JP / es-ES leaves (label + helpText), the i18n expansion #19331 measured. Cost: about 6 files, one domain:spec card.",
            "B — Keep it unoffered under a recorded reason. Delete the stale row and add a root row with a new reason. No read-off class fits (it is not platform-written, not an alias, not unenforced, not a renderer-owned vocabulary), and the three ruled classes admit only their ruled key lists. So this needs a maintainer ruling that names a class.",
            "C — Leave it as this PR does until someone decides. Nothing breaks, but the row's reason text is false from this PR's landing."
          ],
          "recommendation": "A, on the four axes. Business need, measured: the key has a named real producer (hotcrm crm_account.logo, ruling A) and now a reference-app declaration. 0 of 17 forms offer it, so a Studio-authored object can set it only through the Source tab's raw JSON, the exact gap #19331 closed for nameField. Long-term: the gate's top-level zod-only direction expects a live authorable key to be offered or excused by a true reason; A is the state it converges on, and C keeps a false reason in a ledger. AI-error prevention: the parse refuses a non-image or undeclared name loudly at every door, so a free-text control cannot store a bad pointer silently; the contract stays strict with no consumer tolerance. Startup scope: A is the smallest addition, one row on an existing face with no new capability and no new gate. B costs a ruling and a new class."
        }
      ],
      "out_of_scope_findings": [
        "carrier: 承接者:无 · packages/spec/CHANGELOG.md still says imageField \"is accepted, stored and served but nothing draws it\". The entry is release-owned and was true when it shipped, so it is not an erratum target · noted in PR Acceptance notes only"
      ],
      "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-doc-frontmatter.mjs": 0,
        "node scripts/check-doc-frontmatter.mjs --self-test": 0,
        "node scripts/check-doc-route-spelling.mjs --advisory": 0,
        "node scripts/check-doc-route-spelling.mjs --self-test": 0,
        "node scripts/check-docs-section-name.mjs": 0,
        "node scripts/check-docs-section-name.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-section-landing-index.mjs": 0,
        "node scripts/check-section-landing-index.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/lint run check:doc-security-posture": 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:generated": 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-examples": 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:corpus-claim-drift": 0,
        "pnpm check:cross-package-test-inputs": 0,
        "pnpm check:dispatcher-error-vocabulary": 0,
        "pnpm check:doc-anchors": 0,
        "pnpm check:doc-authoring": 0,
        "pnpm check:docs-audit-scope": 0,
        "pnpm check:docs-redirects": 0,
        "pnpm check:docs-single-h1": 0,
        "pnpm check:docs-spec-enumerations": 0,
        "pnpm check:docs-transcript-drift": 0,
        "pnpm check:driver-memory-census": 0,
        "pnpm check:dts-closure": 0,
        "pnpm check:dual-build-cjs-loads": 0,
        "pnpm check:examples-live-imports": 0,
        "pnpm check:gitlink-declared": 0,
        "pnpm check:i18n-coverage": 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:objectui-changeset": 0,
        "pnpm check:org-identifier": 0,
        "pnpm check:page-declaration-shape": 0,
        "pnpm check:platform-checklist": 0,
        "pnpm check:pm-changeset-deadline-census": 0,
        "pnpm check:pm-prior-rulings": 0,
        "pnpm check:pm-widening-tells": 0,
        "pnpm check:published-files": 0,
        "pnpm check:published-readme-links": 0,
        "pnpm check:quick-reference-counts": 0,
        "pnpm check:react-page-adapter-contract": 0,
        "pnpm check:refd-timer-probe": 0,
        "pnpm check:role-word": 0,
        "pnpm check:skill-identifier-liveness": 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:vendor-version-stamps": 0,
        "pnpm check:watch-hint-literal": 0,
        "pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2": 0,
        "pnpm --filter @objectstack/spec run typecheck": 0,
        "pnpm --filter @objectstack/example-showcase verify": 0,
        "pnpm --filter @objectstack/spec check:generated (before --fix)": 1,
        "pnpm --filter @objectstack/spec check:generated --fix": 0,
        "node scripts/pm/dispatch-gates.mjs --ran ran.list --repo objectstack-ai/objectstack": 0
      },
      "deviations": [
        "Item 3 was not applied, although a written rule exists. The rule's second half is a decision by its own words, its first half is refused by the gate without that decision (measured), and the form-row way out needs files outside the claim's surface (object.form.ts plus 4 platform-objects i18n catalogs). The claim says to stop on breach, so I changed nothing there and the PR body starts with \"Part of #21765\" as the dispatch prescribes for this case.",
        "Seed data: the dispatch asked for seed data if the showcase seeds the object. It seeds the field zoo, but I did not seed f_image: a stored image value is a managed sys_file id, and examples/app-showcase/src/data/seed/index.ts (the showcase_task.cover note, ADR-0104) records why a seed cannot honestly mint one. I uploaded a real file only on my throwaway backend to check the served shape.",
        "objects.mdx gained one clause beyond dropping \"pending\" (a record whose field is empty shows no picture); it is the describe's own sentence.",
        "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 Co-Authored-By.",
        "First `pnpm dev -- --fresh` attempt refused with \"The workspace is not built — 1 unmet precondition\" (8 packages without dist). I fixed it with `turbo run build --filter=!@objectstack/docs` under the verify lock (72/72, 60 cached) and did not bypass the precondition."
      ],
      "files_changed": [
        ".changeset/21765-object-image-field-live.md",
        "content/docs/data-modeling/objects.mdx",
        "content/docs/references/api/metadata.mdx",
        "content/docs/references/data/object.mdx",
        "content/docs/references/system/migration.mdx",
        "examples/app-showcase/src/data/objects/field-zoo.object.ts",
        "packages/spec/liveness/object.json",
        "packages/spec/liveness/state-counts/object.md",
        "packages/spec/src/data/object.zod.ts"
      ],
      "line_budget": {
        "additions": 35,
        "deletions": 15,
        "shortstat": "9 files changed, 35 insertions(+), 15 deletions(-)"
      }
    }

    Generated by Claude Code

  6. 10 remaining items

  7. 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 — ⏳ vacant #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).
  8. 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

  9. 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 — ⏳ vacant #6367 in this act.
    • ⛔ Nothing else is added. The at-tier contract review judges all three edits with the rest of the diff.
  10. 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 — ⏳ vacant #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

  11. 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 — ⏳ vacant #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.

  12. 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