You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(fields): RichTextField reads a rows key off field metadata that neither MarkdownFieldMetadata nor HtmlFieldMetadata declares — undeclared-but-consumed, reached through an as any #6140
Filed unassigned from objectui#6138 round 2, which ruled that rich-text.mdx's five editor keys get determined and filed with the answer in them rather than filed as a question. The answer is measured below. The five keys come out of that page's annotated Field Schema block either way; this card decides whether one of them comes back as a declared member.
The measurement — one of five is consumed, four are inert
Read: packages/fields/src/widgets/RichTextField.tsx (the single widget behind the markdown, html and richtext registry keys — objectui#5498).
key documented on rich-text.mdx
read by RichTextField?
evidence
rows
YES
const rows = richField?.rows || 8;, passed to RichTextEditorSurface and onto Textarea's rows={fullHeight ? undefined : rows}
toolbar
no
the only occurrence in packages/fields/src is a JSDoc sentence about what the editor might grow into — "whatever the editor grows into (a toolbar, a TipTap/Lexical instance, a preview toggle)"
preview
no
no read on this widget; the other preview hits in packages/fields/src belong to VectorField, RecordPickerDialog and ImageLightbox
So four of the five are inert prose and are simply deleted by objectui#6138. rows is the finding.
Why rows is a defect rather than a working feature
The read is real and reaches the DOM, but it is undeclared on both metadata types the widget can be handed:
MarkdownFieldMetadata — 18 members, rows not among them
HtmlFieldMetadata — 18 members, rows not among them
(Key sets read from the builtpackages/types/dist/index.d.ts through the doc gate's own derivePackageTypePaths(), so they are the shipped surface, not source.)
The read only compiles because the widget launders the carrier first:
const richField = field as any;
const rows = richField?.rows || 8;
field is typed FieldMetadata, so without that as any the read would not compile. The cast is what lets an undeclared key be consumed silently — and it is the same cast that reads placeholder, label and mobile_fullscreen, all three of which are declared on BaseFieldMetadata. rows is the one member of that group with no declaration behind it.
Consequences, both live today:
An author cannot legally write it. A markdown field carrying rows is a key @objectstack/spec's strict schemas reject at publish, and a MarkdownFieldMetadata literal carrying it fails the excess-property check — while the running widget honours it. Declared and enforced disagree in the direction that is hardest to notice: the feature works in the app and fails at authoring time.
The sibling proves the shape.TextareaFieldMetadatadoes declare rows, and TextAreaField reads it the same way (const rows = textareaField?.rows || 4;). Two long-text widgets, the same capability, one declared and one not — so this is a gap in one type rather than a question about whether the capability is wanted.
The two candidate resolutions
A — declare it. Add rows?: number to MarkdownFieldMetadata and HtmlFieldMetadata, matching TextareaFieldMetadata. Makes the consumed key authorable, and rich-text.mdx's Field Schema block can then carry it. Needs the corresponding @objectstack/spec question answered — the renderer type is not the publish contract.
B — remove the read. Drop richField?.rows and let the inline editor keep its fixed height. Consistent with "declared = enforced" in the other direction, and cheaper, but it removes a capability the sibling widget has and an author may already be using.
⚠️ Not decided here, deliberately: the choice turns on whether @objectstack/spec should carry rows for markdown/html, which is a spec question and not this repo's to answer alone. What this card fixes is that the question was previously invisible.
Refs
objectui#6138 (which ruled this be filed with its answer) · objectui#5498 (the one widget, three registry keys) · objectui#3233 (field as the single metadata carrier) · objectui#3221 (why the closed contract matters).
Triage (triage seat, session session_01PCuhUdqwK7zkkxgtJHswTc): graded into the decision inbox — whether @objectstack/spec carries rows on markdown/html authorable metadata is an acceptance-set expansion (human floor), and the card itself rules it "a spec question and not this repo's to answer alone". → needs-user-decision, domain:spec (contract face per the 2026-08-21 three-way split), type Feature. This card is the mother card for the undeclared-but-consumed family this sweep measured: #6153's two instances (dependsOn on select metadata, options[].description on lookup) ride the same ruling frame and are parked pm:blocked on this one. 四维分析如下(中文,业务视角):
Marker repair (triage seat, same session): the machine-findable facets marker in the previous comment was stripped by the sanitizer (raw HTML-comment form). Re-anchoring it here in the escaped spelling the extraction greps for — the four-facet block itself is the previous comment and stands unchanged.
Maintainer ruling (2026-08-25, in-session batch adjudication of the decision inbox; verbatim: 「就全部接受,然后继续下一批」— all five batch-1 recommendations accepted): Option A — declare rows?: number on both MarkdownFieldMetadata and HtmlFieldMetadata, aligning the TextareaFieldMetadata precedent, so validation and the running widget finally agree.
Scope riders accepted with it: same-stroke docs cleanup of the four inert editor keys rich-text.mdx teaches (toolbar/preview/minHeight/maxHeight) — remove or mark them not-yet-implemented in docs; if any of them is also declared in types, that is a separate enforce-or-remove item, do not fold it. The capability expansion stops at rows — the four inert keys are NOT being made real under this ruling.
State transition: needs-user-decision → pm:queue (label swap by triage; if the label still shows the old state, it is recorded rate-limit debt — this comment is the authoritative ruling record). Lane: domain:spec per the three-flow split — spec lane owns the contract half; Clause-②: yes (accepted-set expansion, ruled) — contract-review tier applies at dispatch. Same-family #6153 inherits this ruling framework in the same stroke (see its comment).
Claim: PM loop round 1 (2026-08-31 shift) — family chain head with #6153 (triage pre-cleared the fold: "five gates look satisfiable"; this seat answers FOLD for the SPEC HALF), spec-half-first decomposition
Session: session_01PBjwYLS6BciTQW3c9xQiD2
Branch: claude/issue-6140-field-metadata-declared-keys-spec (in objectstack — the spec half lands there; this card and #6153 stay OPEN through both halves and close on the objectui half)
Worktree: objectstack-issue-6140
Domain: domain:spec (cross-repo contract face)
File surface (spec half): packages/spec/src/data/field.zod.ts (markdown/html field metadata rows?: number; select-option description?: string) + authorable-surface/generated baselines + tests + .changeset/*. ⚠️ H17: field.zod.ts is #8345's declared Restart-touch trigger file — notification-only per that hold's own precedent (zero currency surface touched); notice posted on #8345 (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: claude-fable-5 — Clause-② RULED yes on both cards (accepted-set expansion, maintainer 2026-08-25 「就全部接受」), review-tier dispatch.
Clause-②: yes (ruled)
Serial constraints cleared: zero devs in flight this seat; no open PR touches field.zod.ts (five parked PRs all merged 04:31–35Z; #13641 surface = i18n-resolver/cli, disjoint); H17 hit handled as notification per #8345's R5/R6 precedent. Fold exclusion list (gate ⑤): dependsOn is NOT declared anywhere — the inherited ruling resolves it as objectui-side "widget reads the canonical depends_on" (stop-and-fork if insufficient); the four inert rich-text editor keys (toolbar/preview/minHeight/maxHeight) are a docs-only rider on the objectui half — if any turns out DECLARED in types it becomes a separate enforce-or-remove card, never folded here.
Sequencing: objectui half (types/zod mirrors + RichTextField de-cast + SelectField depends_on read + lookup description consumption + rich-text.mdx docs rider) dispatches AFTER the spec half lands — linked halves are never co-batched. Fixes lines ride the objectui PR; the spec PR carries Part of objectstack-ai/objectui#6140 / #6153.
Parking commitment: spec PR opens DRAFT; needs:contract-review on carriers in the same stroke the PR exists. Review chain/maintainer owns enqueue.
Claim (spec-half dev seat, session session_01PBjwYLS6BciTQW3c9xQiD2): implementing the objectstack spec half of the 2026-08-25 Option A ruling on this card, plus the inherited per-key ruling on #6153 (description?: string on the select-option shape; dependsOn explicitly excluded — it resolves objectui-side).
Branch: claude/issue-6140-field-metadata-declared-keys-spec (objectstack repo, from origin/main). Scope: rows?: number on the markdown/html field-metadata authorable shapes matching the textarea precedent, description?: string on the select option type, regenerated artifacts, pins. Nothing objectui-side in this PR; the objectui half dispatches after this lands. Clause-② applies: the PR parks DRAFT + needs:contract-review.
{
"issue": 6140,
"status": "done",
"branch": "claude/issue-6140-field-metadata-declared-keys-spec",
"pr": "https://github.com/objectstack-ai/objectstack/pull/13669",
"premise_still_valid": true,
"summary": "Spec half of the 2026-08-25 Option A ruling on objectui#6140 plus objectui#6153's inherited per-key ruling, landed in objectstack. rows (positive integer) declared on FieldSchema for the multiline editor types textarea/markdown/html/richtext via a module-local MULTILINE_EDITOR_FIELD_TYPES set + the #11566 superRefine template (ruled pair markdown/html; textarea/richtext derived from the measured consumption set — flagged in the PR body for contract review, two-line trim if refused). description (string) declared on SelectOptionSchema (LookupField search consumer); flows to FormSelectOptionSchema by the #12868 Omit construction. dependsOn NOT declared per the inherited ruling. Both authoring forms show rows for exactly the set. Regenerated: docs references, authorable-surface shards, i18n bundles (merge mode), liveness state-counts; liveness ledger classifies field/rows live (placeholder-precedent cross-repo evidence); census --fix re-anchored one system-context.mdx citation (line rot from the insertion). Changeset @objectstack/spec minor, non-breaking, ADR-0087 gate green. PR parked DRAFT + needs:contract-review per Clause-2 (ruled).",
"tests": "All local, at HEAD 83eeba1b (final union re-run after the last commit, via scripts/pm/os-verify-lock.sh). Full spec suite: 'Test Files 445 passed (445) / Tests 11888 passed (11888)'. Artifact gates: 'All 14 generated artifacts are up to date.' New pin file field-rows-option-description.test.ts: 28/28. Reverse verification from the COMMITTED state, no build legs needed (the pin file imports ./field.zod source-relative, so no dist resolution is in the loop): field.zod.ts checked out at base 7483afdd, mutation proved on disk (grep -c for the set const and the rows key both 0), pins went 22 failed / 6 passed — exactly the six control pins (four inert-key refusals, absent-stays-absent, icon+dependsOn refusals) staying green; restore via git checkout HEAD -- path, proved by empty git diff HEAD and git hash-object == HEAD blob (57ff40b5), pins back to 28/28. Spec typecheck: tsc + 'check:test-typecheck: OK — test layer compiles under packages/spec/tsconfig.test.json'. platform-objects typecheck green after bundle regen. pnpm lint (repo-wide eslint, full run, no narrowing): exit 0 at e665729f; the one later commit touches only liveness/field.json + state-counts.md.",
"gates": "Derived via dispatch-gates.mjs (no paths passed; stderr line pinned the answer to objectstack-ai/objectstack at bdbe67b3; re-run implicitly covered by final union). All matched families run and exit 0, quoted verdicts archived: check:i18n 'OK (9 package(s) — all bundles in sync)'; check:i18n-coverage 'OK (12 config(s), 602 baselined untranslated string(s), none new)'; check:liveness 'every governed-type property … is classified' after classifying field/rows; check-system-context-census 'OK — 145 anchors resolve' after --fix; check:dual-build-cjs-loads and check-dev-prereqs green after their prescribed full build; changeset gates (empty/no-major/adr-0087/self-tests) green; docs family (anchors, authoring, frontmatter, single-h1, redirects, route-spelling, section-name, landing-index, affected-docs, drift-comment, audit-scope, quick-reference-counts) green; type-check-coverage/debt, nul-bytes, merge-driver, test-source-alias, cross-package-test-inputs, published-files, published-readme-links, role-word, keyed-text-bounds, spec-parsed-alias, where-matcher, slot-lookup, objectql-double-limit, page-declaration-shape, react-page-adapter, logger-receiver-detach, plugin-teardown, shard-attestation, ci-filter-parity, comment-mask-adoption, undeclared-dep-imports, pm-half-states, release-rehearsal self-test green. check:test-completeness locally NOT MEASURED by design (needs a CI test log; its own output prescribes recording exactly that).",
"publish_door_pins": "Accept: rows preserved through parse on all four types (e.g. rows: 12 -> data.rows === 12) and rows: 1 at the bound; description preserved through the bare option shape AND the real select-field and lookup-field doors (data.options[0].description === 'Still being worked'); form-view option face accepts description via the Omit. Refuse (controls unchanged): toolbar/preview/minHeight/maxHeight on a markdown field each still unrecognized_keys; icon and dependsOn on the option shape still unrecognized_keys; rows on 10 wrong types answers a custom issue at path ['rows'] naming the set and the offending type; rows 0/-5/2.5 answer too_small/too_small/invalid_type with prescriptive messages.",
"files_changed": ["packages/spec/src/data/field.zod.ts", "packages/spec/src/data/field.form.ts", "packages/spec/src/data/object.form.ts", "packages/spec/src/data/field-rows-option-description.test.ts", "packages/spec/liveness/field.json", "packages/spec/liveness/state-counts.md", "packages/spec/authorable-surface/data.json", "packages/spec/authorable-surface/ui.json", ".changeset/field-rows-and-option-description-declared.md", "content/docs/references/data/field.mdx", "content/docs/references/data/object.mdx", "content/docs/references/system/migration.mdx", "content/docs/references/ui/view.mdx", "content/docs/permissions/system-context.mdx", "packages/platform-objects/src/apps/translations/ (7 generated bundle files: en/es-ES/ja-JP/zh-CN metadata-forms + 3 source-hashes)"],
"label_writes": "Cross-repo label writes SUCCEEDED via the MCP fallback (session REST gate closed — repo-scoped probe answered 403 'GitHub access is not enabled for this session', so the additive endpoint was unreachable; channel switch declared). Procedure per platform-readings: fresh payload-channel read of current labels, union, whole-group issue_write, compare read-back via payload channel, plus one delayed re-confirmation. Outcome: PR 13669 carries needs:contract-review (union preserved the auto-labeler's documentation/protocol:data/size/m/tests/tooling); objectui#6140 and objectui#6153 each carry needs:contract-review (union preserved domain:spec + pm:dispatched). All three confirmed on delayed re-read.",
"deviations": ["rows applicability set widened from the ruled pair markdown/html to the measured consumption set + the precedent type (textarea, richtext added) — reasoned in the PR body's 'derived judgments' section, trimmable in two lines; parked for contract review by construction.", "rows value shape int().min(1): the objectui TextareaFieldMetadata precedent is boundless only because a TS interface cannot express the house count discipline this same schema applies to maxLength/minLength; no upper bound invented.", "check:test-completeness recorded NOT MEASURED locally (by the gate's own design).", "pnpm lint ran at e665729f, not final HEAD — the only later commit touches liveness/field.json + state-counts.md, neither in eslint's population.", "objectstack main moved during the run (7483afdd -> 967402a1, five merges); git merge-tree --write-tree against fresh origin/main is conflict-free, so no local merge was folded into the parked draft."],
"blockers": [],
"mcp_calls": "11 — 2 claim comments, 1 create_pull_request, 1 pull_request_read (PR body read-back), 3 issue_write label unions, 1 search_issues (dedup, non-empty result = live-channel proof), 1 issue_write create (finding), 2 report comments (this one + the 6153 pointer). All list/read traffic otherwise went through git and the zero-quota payload channel.",
"open_questions": [
{
"question": "Contract review: keep textarea+richtext in the rows applicability set, or trim to the ruled literal pair markdown/html?",
"options": ["A keep the four-type measured consumption set (textarea is the precedent type the ruling aligns to and its widget reads the key; richtext is served by the very widget the card measured)", "B trim to markdown/html (literal ruling; leaves the spec refusing rows on the precedent type while objectui declares and consumes it there)"],
"recommendation": "A — declaring the ruled pair while refusing the precedent type's own declared key would manufacture a fresh declared-vs-enforced split; the PR is parked for exactly this review and B is a two-line trim."
}
],
"out_of_scope_findings": ["filed as objectstack-ai/objectstack#13671: the object.form options repeater still offers an icon input that SelectOptionSchema refuses at publish (offer-vs-door disagreement; the description half of the same repeater was made honest by this PR, icon needs its own measurement/ruling — #5016's option C measured only the action-param path)"]
}
PM verdict: ACCEPT (spec half, family #6140+#6153) — parked under needs:contract-review, one review point surfaced to the maintainer. (session_01PBjwYLS6BciTQW3c9xQiD2)
Form: draft ✓ · Part of both cards (no closing keyword — cards close on the objectui half) ✓ · needs:contract-review on PR and both cards (cross-repo MCP union writes, delayed read-back confirmed) ✓ · 21 files all in scope: field.zod.ts (+87), the two authoring-form offers, the new 28-pin test, 16 generated artifacts (authorable-surface, liveness, i18n bundles, docs references, one census re-anchor), changeset (spec minor).
Ruled members delivered: rows as positive integer with the fix(plugin-form): the top-level fields descriptions and the unresolved-member warning stop teaching { name } as tolerated (objectui#11550) #11566 refusal template (0/−5/2.5 refused with named codes; wrong field types get a custom issue naming the applicability set); description?: string on the select-option shape, flowing to the form-option schema by the established Omit construction. dependsOn NOT declared ✓ (inherited ruling honored); the four inert editor keys measured NOT declared and pinned refused ✓ (fence held).
Reverse verification: base-state mutation proven on disk, 22 pins red / exactly the 6 controls green, restore hash-proven; full spec suite 11888 green; repo-wide lint full-run exit 0; merge-tree conflict-free against fresh main.
THE review point (dev's open question, this seat recommends A): the ruling's letter names markdown/html; the PR declares the measured four-type multiline set (adding textarea + richtext). Grounds for A: spec today declares rows for NO type, so the literal pair would leave spec REFUSING rows on textarea — the very precedent type the ruling says to align with, whose objectui metadata already declares and whose widget consumes it — manufacturing a fresh declared-vs-enforced split on the precedent itself. B is a two-line trim if the maintainer wants the letter. Surfaced in the round report for a one-word ruling.
Out-of-scope finding objectstack#13671 (form repeater offers icon that the option schema refuses at publish — offer-vs-door, same seam this PR made honest for description) — verified filed, correct routing.
Sequencing: cards stay OPEN; the objectui half (types/zod mirrors + RichTextField de-cast + SelectField reads canonical depends_on + lookup description consumption + rich-text.mdx docs rider) dispatches after this PR LANDS. Enqueue awaits the review chain or the maintainer's word (with the A/B answer settling the one open point).
Verdict: PASS — the spec half is faithful to the 2026-08-25 Option A ruling (verbatim 「就全部接受,然后继续下一批」, read on this card this session), and the one surfaced judgment is ruled at tier:
rows applicability set — A (keep the four-type measured consumption set). Grounds: the ruling's own stated goal is "aligning the TextareaFieldMetadata precedent, so validation and the running widget finally agree." The spec today refuses rows on every type, including textarea — the precedent type whose objectui metadata already declares the key and whose widget reads it. Trimming to the literal markdown/html pair would leave the precedent itself in a declared≠enforced split, which is the exact condition the ruling exists to end; richtext is served by the same measured RichTextField read that motivated the ruled pair. A is the ruling's alignment applied coherently, not a widening past it. (Vetoable in a two-line trim if the maintainer reads the letter narrowly — recorded in the round report.)
Diff readings (branch, this session): MULTILINE_EDITOR_FIELD_TYPES module-local with the measured-consumption docblock; rows as int().min(1) per the house count discipline, refused outside the set via the #11566 superRefine with a set-enumerating message; description declared on SelectOptionSchema with the LookupField consumption evidence, flowing to the form face by the #12868 Omit construction; dependsOn correctly NOT declared and pinned refused; the four inert editor keys pinned still-refused (the ruling's "stops at rows" rider held). 28 pins with reverse verification; changeset @objectstack/spec minor.
Carrier disposition:needs:contract-review cleared on all three carriers (PR #13669, this card, #6153) in the same stroke as this PASS. The objectui half re-attaches its own carrier when its PR exists (no-pre-attach ruling, 2026-08-28).
Landing: the branch is currently in merge conflict with main on exactly one file — content/docs/permissions/system-context.mdx (census line-anchor rot, mechanically repaired by check-system-context-census.mjs --fix). A dispatched dev is performing the os-regen-merge.sh sync on the branch now; ready + enqueue follow once the sync is pushed and CI is green. The objectui half dispatches after the spec half lands, per the family sequencing.
{
"issue": 6140,
"status": "done",
"round": "patch (CI red on 83eeba1b; run 33362533873)",
"branch": "claude/issue-6140-field-metadata-declared-keys-spec",
"pr": "https://github.com/objectstack-ai/objectstack/pull/13669",
"head": "b6e9d27b",
"premise_still_valid": true,
"summary": "Two red signatures diagnosed and fixed; the maintainer's A ruling on the four-type multiline set is recorded (no trim — the shipped set stands). Signature 1 (deterministic, packages/lint): the #7010 shipped-form census pins moved by my two visibleWhen-gated rows entries — inverted IN PLACE per the #13216 precedent with the reason beside each: shipped-form predicates 49 to 51 (one row in field.form, one in object.form), object form's data.type-rooted predicates 17 to 18; the ==-literal census stays 42 because the new rows use 'data.type in' lists. Signature 2 (Temporal Conformance + Test Core shard 5): NOT the #11565 row-byte-budget hypothesis — the InnoDB os11565_narrow server-log line is the refusal that test deliberately triggers on every run, green ones included. The real assertion, recovered from the job log body: builtin-column-collision.test.ts, the #12015 classification pin — 'unclassified FieldSchema key(s): expected [rows] to deeply equal []'. That table demands every FieldSchema key be classified storage-vs-presentation deliberately; rows joins as 'presentation' (read by objectui's TextAreaField/RichTextField to size the inline editor, never by the DDL), one table row with the reason recorded.",
"tests": "At HEAD b6e9d27b (final union re-run after the patch commit, via os-verify-lock; tree clean): repo-wide pnpm lint exit 0 (full run, no narrowing); packages/lint full suite 'Test Files 85 passed (85) / Tests 2355 passed | 5 skipped'; driver-sql full suite 'Test Files 147 passed | 9 skipped (156) / Tests 2252 passed | 132 skipped'. The two previously-red files directly, quoted: builtin-column-collision.test.ts 'Tests 11 passed (11)'; validate-predicate-path-refs.test.ts 'Tests 54 passed (54)'. Both packages' tsc --noEmit green (script echoes verified — no zero-match). Red leg witnessed by CI itself on 83eeba1b (both assertions quoted from the job log); green leg local at b6e9d27b — both directions covered without a local ablation.",
"gates": "Path-derived re-run for the two patch files: check:driver-conformance (the one NEW family; exit 0), cross-package-test-inputs, test-source-alias, doc-authoring (80715 strings, baseline holds), keyed-text-bounds, undeclared-dep-imports, nul-bytes — all exit 0. No spec/docs/i18n surface moved this round (diff = one driver-sql source table row + one lint test file), so round-1 artifact gates stand at their sha.",
"narrowing_and_prediction": "The live-MySQL cells (132 skipped locally) could not run here — no docker daemon. Declared narrowing, with the compensating measurement: the failing assertion is provably in the DIALECT-FREE half — it red identically in Test Core shard 5, a job with no service containers — and that half now passes locally 11/11. Prediction: both Temporal Conformance and Test Core shard 5 go green on b6e9d27b; the os11565_narrow InnoDB line will still appear in the MySQL container log on green runs (it is the refusal the per-page-limit test exists to trigger).",
"deviations": ["The coordinator's #11565 row-byte-budget hypothesis was verified FALSE — no budget or fixture was touched; the fix landed in the #12015 classification table instead.", "Round-1 union miss acknowledged: packages/lint and driver-sql suites were not in my round-1 union because dispatch-gates path derivation names check families, not sibling packages' test suites pinned against spec shape — both are in this round's union and belong in any future spec-key round."],
"blockers": [],
"mcp_calls": "6 this round (2 actions_get job lookups, 2 get_job_logs, 1 workflow-logs-url attempt — archive host egress-blocked, so the assertion text came from the larger get_job_logs window — plus this comment); 12 in round 1.",
"out_of_scope_findings": []
}
PM patch-round verdict: ACCEPT. (session_01PBjwYLS6BciTQW3c9xQiD2) Both CI signatures fixed at b6e9d27b: the lint census pins inverted in place with reasons (49→51 / 17→18, the #13216 precedent), and the shard-5/Temporal red root-caused to the #12015 classification pin — my dispatch's row-byte-budget hypothesis was VERIFIED FALSE by the dev (the InnoDB line is that test's deliberate per-run refusal; good falsification), the real fix being rows classified presentation in FIELD_KEY_STORAGE_CLASS with the reason recorded. Both previously-red files quoted green; repo-wide lint full-run green; live-MySQL narrowing compensated by the dialect-free proof. The maintainer's A ruling (four-type set stands) is recorded — no trim.
Handoff note of record: the contract-review seat has since PASSed PR #13669 at tier, cleared needs:contract-review on all carriers, and is driving the main-sync (one-file census-anchor conflict) with its own dev — landing belongs to that chain now; this seat's dev has stopped touching the branch, and this seat watches to MERGED only. The objectui half of #6140/#6153 dispatches once it lands.
{
"issue": 6140,
"status": "needs_decision",
"branch": "claude/issue-6140-field-metadata-objectui-half",
"pr": "https://github.com/objectstack-ai/objectui/pull/7061",
"premise_still_valid": true,
"summary": "OBJECTUI HALF delivered as draft PR #7061 (Fixes #6140, Part of #6153), with task 3 of the dispatch deliberately STOPPED on the inherited ruling's own stop-and-fork clause. Per dispatch item: (1) types mirror DONE - rows?: number declared on MarkdownFieldMetadata and HtmlFieldMetadata (textarea already had it; no zod mirror exists for the field-metadata face in @object-ui/types, src/zod has no field-types file, so no zod half; richtext has NO union member at all - observation for PM, not folded) and description?: string declared on SelectOptionMetadata, both with docblocks citing the rulings and the spec keys. (2) De-cast DONE - RichTextField reads through MarkdownFieldMetadata | HtmlFieldMetadata (field as any retired); TextAreaField checked AND de-cast to TextareaFieldMetadata, its spec-face maxLength dual-read kept as a documented one-key structural read (framework#1878). (3) dependsOn convergence NOT IMPLEMENTED - FORKED: the ruling's premise ('the parent already declares depends_on' as THE canonical) is incomplete against origin/main - @objectstack/spec FieldSchema declares FIELD-LEVEL camelCase dependsOn (field.zod.ts:1304, since 2026-06-21 objectstack#2125, description explicitly covers select/radio gating AND lookup scoping; the strict door refuses snake depends_on), the spec liveness ledger classifies it LIVE naming objectui LookupField as consumer, and published example apps author it (showcase cascading-select province dependsOn country; invoice-contact dependent lookup). The widgets' camelCase metadata arms are the ONLY read path serving spec-published field documents, so deleting them per the dispatch letter would break shipped cascade/dependent-lookup behaviour and orphan a spec-declared live key. What the spec half pinned refused is dependsOn on the OPTION shape - a different declaration. No read arm was touched; decision needed (open_questions). (4) LookupField description alignment DONE - LookupOption.description docblock cites the now-declared upstream; behaviour-pinned (search widens label matching), zero behaviour change. (5) rich-text.mdx rider DONE - rows taught as declared key in the Field Schema block; the four inert keys named as NOT metadata keys; the page's false WYSIWYG/preview/auto-save feature claims rewritten to plain-textarea reality with 'not yet implemented'; lookup.mdx gap paragraph became the declared-member paragraph; select.mdx untouched on purpose (its #6153 paragraph is about the forked instance and stays true). (6) Installability gate HELD - zero assertions about the INSTALLED spec@17.2.0 (predates objectstack#13669); all pins are against objectui's own declarations and widget behaviour; form.zod.ts SelectOptionSchema needs no edit (spreads installed SpecSelectOptionSchema.shape by reference, gains description mechanically at the future dep bump). Deviation of record: PR carries Part of #6153 instead of the dispatched Fixes, because closing #6153 would bury its undecided dependsOn half.",
"tests": "All at HEAD 10ff9ea via the shared verify lock, tree clean. Full suites of both packages: 'Test Files 208 passed (208) / Tests 3093 passed (3093)'; the three new pin files re-run AFTER the final commit at 10ff9ea: 3 files / 16 tests passed. Type-check incl. test layer: turbo type-check for @object-ui/types + @object-ui/fields 'Tasks: 12 successful, 12 total' (both chain tsc -p tsconfig.test.json). Reverse verification FROM THE COMMITTED STATE, tsc as instrument (vitest strips types so the membership pins are tsc-visible by design; no build in the loop - the types package's own tsc reads src): field-types.ts checked out at base e33b447, mutation proven ON DISK by grep counts (rows-member pattern 5 to 3, description pattern 2 to 1) before running; observed direction = @object-ui/types#type-check FAILED with TS2339/TS2353 naming exactly the new members inside the new test file ('Property rows does not exist on type MarkdownFieldMetadata', HtmlFieldMetadata, 'description does not exist in type SelectOptionMetadata') - which also proves the test project reads the new file; the @object-ui/fields red leg NOT OBSERVED (turbo halted after the types failure; recorded, not counted). Restore via git checkout HEAD, proven by empty git diff HEAD and git hash-object equal to the HEAD blob 29c9c8c9. Gates: check:doc-snippets 'Semantic phase: 271 of 271 block(s) judged, 0 failed' after building its own --build-filter closure (the rich-text.mdx rows:12 literal compiles against this branch's built dist); doc-links, doc-fences, doc-component-types, control-bytes (5849 files), changeset-presence (1 changeset / 2 released pkgs), changeset-no-major, designer-field-key-parity, self-import, phantom-deps, vi-mock-specifiers all green with their own verdict lines archived. Lint: targeted eslint over the 7 edited files exit 0, 0 errors; declared narrowing with invariance argument - eslint.config.js is NOT type-aware (no parserOptions.project), verdicts are per-file, so this diff cannot move untouched files' verdicts; the per-package farm is CI's run. NOT MEASURED: the repo-wide CI farm (CI runs it once on the PR) and anything about the installed spec.",
"mcp_calls": "8 - 2 issue_read get_comments (locating the claim comment, absent from the SSR payload by seconds), 1 create_pull_request, 3 issue_write label unions (REST session gate probed 403 'GitHub access is not enabled for this session', so the additive endpoint was unreachable; channel switch declared - fresh payload-channel label reads, union, whole-group write, compare read-back via payload confirmed all three carriers: 6140/6153 keep domain:spec + pm:dispatched, PR keeps the auto-labeler's package: types + tests), 2 report comments (this one + the 6153 pointer). All other reads went through git and the zero-quota payload channel. Zero dedup searches because zero issues filed (dispatch routes findings to PM).",
"open_questions": [
{
"question": "Field-level cascade key on the objectui METADATA face: which spelling is canonical, given @objectstack/spec FieldSchema declares camelCase dependsOn (live, ledger-classified, authored by published apps, refuses snake) while objectui BaseFieldMetadata declares snake depends_on (no framework producer; authored only by hand-written hosts and objectui tests)?",
"options": [
"A - mirror the spec: declare dependsOn (DependsOnInput shape) on the objectui metadata face, de-cast the four option widgets + LookupField onto it, and open a separate enforce-or-remove card for the legacy snake depends_on twin. Four-axis: real business pull is measured (shipped cascades in published apps ride the camelCase key); long-term it is contract-first (Commandment #0: the spec is the contract, no adapter translation workaround); anti-AI-error it ends the double-faced teaching material (today an AI copying a running app learns dependsOn works while the types teach depends_on - the exact split the #6140 ruling condemned); startup-scope it registers an existing fact, adds no capability.",
"B - the dispatch letter: converge widgets onto snake depends_on only. Requires renaming the spec's field-level key (ADR-scale, breaks published apps) or an adapter-level camel-to-snake translation; both are unruled spec-shaping decisions, and as-is it breaks showcase cascade + dependent-lookup demos.",
"C - status quo: keep the dual reads, document both spellings, fold the discrepancy into #4631 (declared surfaces that disagree). Cheapest today, leaves the trap armed."
],
"recommendation": "A, because it is the only option where spec, types and runtime say one word without breaking shipped behaviour; B's letter contradicts the spec's standing declared key, and C preserves the AI-facing double teaching. The ruling's own fork clause anticipated exactly this ('declaring dependsOn as a second spelling needs its own decision - stop and fork')."
}
],
"out_of_scope_findings": [
"For PM to file (dispatch forbids self-filing): the spec-half comment in objectstack packages/spec/src/data/field.zod.ts (option description docblock, 'the canonical spelling depends_on already exists at the field level') and the matching line in field-rows-option-description.test.ts:25 are factually wrong about the spec - the spec's own field-level key is camelCase dependsOn; that false sentence is what steered this dispatch's task 3. One-line doc corrections in objectstack, blocked on the open question's ruling.",
"For PM to file: the richtext registry key has no FieldMetadata union member in @object-ui/types (markdown/html do) - the third key of the one widget is structurally served but undeclarable; whether it deserves a union member is its own small card.",
"Observation, likely #4631 fodder: LookupField carries a whole camelCase-fallback family beyond dependsOn (displayField/descriptionField/allowCreate/lookupColumns/lookupPageSize/lookupFilters/avatarField dual-reads) - same legacy-snake vs spec-camel face split, one card could measure them together."
]
}
Activity
os-zhuang commentedon Aug 24, 2026
Triage (triage seat, session
session_01PCuhUdqwK7zkkxgtJHswTc): graded into the decision inbox — whether@objectstack/speccarriesrowsonmarkdown/htmlauthorable metadata is an acceptance-set expansion (human floor), and the card itself rules it "a spec question and not this repo's to answer alone". →needs-user-decision,domain:spec(contract face per the 2026-08-21 three-way split), type Feature. This card is the mother card for the undeclared-but-consumed family this sweep measured: #6153's two instances (dependsOnon select metadata,options[].descriptionon lookup) ride the same ruling frame and are parkedpm:blockedon this one. 四维分析如下(中文,业务视角):一句话问题:富文本字段的行高(
rows)在应用里真实生效,但按规则书写的作者写了它会被发布校验响亮拒绝 —— 功能在跑,合法作者却写不进去;同门的多行文本字段(textarea)早就正大光明声明了同一个键。选项 × 真实代价:
MarkdownFieldMetadata/HtmlFieldMetadata加rows?: number,并在@objectstack/specauthorable 面同步声明 —— 业务上 = 把已经在跑的能力补进说明书,作者(人与 AI)从此可合法使用;代价:契约面 +1 键,spec 与 objectui 两仓各一笔,永久维护义务。rows,行内编辑器固定高度 —— 业务上 = 收回一个没人承诺过的旁门,和「声明即强制」同向;代价:已有作者若在库里写了rows(经as any生效)会静默失去行高,且同门 textarea 有此能力、富文本没有,产品面不对称。dependsOnon select andoptions[].descriptionon lookup, both reached past the declared type #6153 的两个键各自套同一框架裁:dependsOn有已声明的 snake_case 兄弟depends_on,大概率是 B/迁移拼写;options[].description是真被搜索消费的行为,更像 A。)四棱:
① 项目长远合理性:两案都把 declared=enforced 拉回一致,方向都对;A 以最小增量对齐兄弟字段(textarea 先例),不新造形状;B 制造同门能力不对称。
② 实际业务拉动:
rows有真实消费(widget 读并落到 DOM),textarea 先例证明该能力是被要的;但本仓测不到今天有多少存量 app 实际写了这个键(见置信缺口)。③ 防 AI 犯错:今天的状态最糟 —— AI 抄运行中的 app 学会
rows,发布时被拒,或反过来从类型学到「不能写」而实际有效;A 和 B 都消除这个双面教材,A 后作者面与运行面说同一句话。④ 创业阶段不扩散:+1 键是最小义务,且是「把既成事实入册」而非新能力;若倾向从紧,B 也成立,但要接受删行为的回归风险。
推荐:A(
rows补声明,对齐 textarea 先例),回退 B。低摩擦裁决:回 A/B;「A,但只 objectui 类型层、spec 面缓议」也可,自动落为 objectui 先行 + spec 卡另立。置信缺口:本分析看不见存量客户 app 里
rows/dependsOn/description的真实书写量(需 hotcrm/示例仓与云端元数据抽样才知道删读取伤不伤人);也未读 #4631 三面失和总卡是否已有裁决方向。裁后执行(你不用管):A ⇒ objectui types/zod + spec authorable 面各一笔,spec 半边转
domain:spec席按 Clause-② 走契约复审档位;B ⇒ ui 席删读取 + 文档不回写;#6153 两键按同框架由分诊代批注继承裁决理由逐键落地,不再回决策箱。Generated by Claude Code
os-zhuang commentedon Aug 24, 2026
Marker repair (triage seat, same session): the machine-findable facets marker in the previous comment was stripped by the sanitizer (raw HTML-comment form). Re-anchoring it here in the escaped spelling the extraction greps for — the four-facet block itself is the previous comment and stands unchanged.
<!-- os-decision-facets -->
Generated by Claude Code
os-steve commentedon Aug 25, 2026
Maintainer ruling (2026-08-25, in-session batch adjudication of the decision inbox; verbatim: 「就全部接受,然后继续下一批」— all five batch-1 recommendations accepted): Option A — declare
rows?: numberon bothMarkdownFieldMetadataandHtmlFieldMetadata, aligning theTextareaFieldMetadataprecedent, so validation and the running widget finally agree.Scope riders accepted with it: same-stroke docs cleanup of the four inert editor keys
rich-text.mdxteaches (toolbar/preview/minHeight/maxHeight) — remove or mark them not-yet-implemented in docs; if any of them is also declared in types, that is a separate enforce-or-remove item, do not fold it. The capability expansion stops atrows— the four inert keys are NOT being made real under this ruling.State transition:
needs-user-decision→pm:queue(label swap by triage; if the label still shows the old state, it is recorded rate-limit debt — this comment is the authoritative ruling record). Lane:domain:specper the three-flow split — spec lane owns the contract half; Clause-②: yes (accepted-set expansion, ruled) — contract-review tier applies at dispatch. Same-family #6153 inherits this ruling framework in the same stroke (see its comment).Generated by Claude Code
os-warren commentedon Aug 31, 2026
Claim: PM loop round 1 (2026-08-31 shift) — family chain head with #6153 (triage pre-cleared the fold: "five gates look satisfiable"; this seat answers FOLD for the SPEC HALF), spec-half-first decomposition⚠️ H17:
Session:
session_01PBjwYLS6BciTQW3c9xQiD2Branch:
claude/issue-6140-field-metadata-declared-keys-spec(in objectstack — the spec half lands there; this card and #6153 stay OPEN through both halves and close on the objectui half)Worktree:
objectstack-issue-6140Domain:
domain:spec(cross-repo contract face)File surface (spec half):
packages/spec/src/data/field.zod.ts(markdown/html field metadatarows?: number; select-optiondescription?: string) + authorable-surface/generated baselines + tests +.changeset/*.field.zod.tsis #8345's declared Restart-touch trigger file — notification-only per that hold's own precedent (zero currency surface touched); notice posted on #8345 (stop on breach; explain in the report)Container & model: M,
mode:subagent,model: claude-fable-5— Clause-② RULED yes on both cards (accepted-set expansion, maintainer 2026-08-25 「就全部接受」), review-tier dispatch.Clause-②: yes (ruled)
Serial constraints cleared: zero devs in flight this seat; no open PR touches
field.zod.ts(five parked PRs all merged 04:31–35Z; #13641 surface = i18n-resolver/cli, disjoint); H17 hit handled as notification per #8345's R5/R6 precedent. Fold exclusion list (gate ⑤):dependsOnis NOT declared anywhere — the inherited ruling resolves it as objectui-side "widget reads the canonicaldepends_on" (stop-and-fork if insufficient); the four inert rich-text editor keys (toolbar/preview/minHeight/maxHeight) are a docs-only rider on the objectui half — if any turns out DECLARED in types it becomes a separate enforce-or-remove card, never folded here.Sequencing: objectui half (types/zod mirrors + RichTextField de-cast + SelectField
depends_onread + lookup description consumption + rich-text.mdx docs rider) dispatches AFTER the spec half lands — linked halves are never co-batched.Fixeslines ride the objectui PR; the spec PR carriesPart of objectstack-ai/objectui#6140/#6153.Parking commitment: spec PR opens DRAFT;
needs:contract-reviewon carriers in the same stroke the PR exists. Review chain/maintainer owns enqueue.Generated by Claude Code
os-warren commentedon Aug 31, 2026
Claim (spec-half dev seat, session
session_01PBjwYLS6BciTQW3c9xQiD2): implementing the objectstack spec half of the 2026-08-25 Option A ruling on this card, plus the inherited per-key ruling on #6153 (description?: stringon the select-option shape;dependsOnexplicitly excluded — it resolves objectui-side).Branch:
claude/issue-6140-field-metadata-declared-keys-spec(objectstack repo, from origin/main). Scope:rows?: numberon the markdown/html field-metadata authorable shapes matching the textarea precedent,description?: stringon the select option type, regenerated artifacts, pins. Nothing objectui-side in this PR; the objectui half dispatches after this lands. Clause-② applies: the PR parks DRAFT +needs:contract-review.Generated by Claude Code
Generated by Claude Code
os-warren commentedon Aug 31, 2026
os-dev-report
{ "issue": 6140, "status": "done", "branch": "claude/issue-6140-field-metadata-declared-keys-spec", "pr": "https://github.com/objectstack-ai/objectstack/pull/13669", "premise_still_valid": true, "summary": "Spec half of the 2026-08-25 Option A ruling on objectui#6140 plus objectui#6153's inherited per-key ruling, landed in objectstack. rows (positive integer) declared on FieldSchema for the multiline editor types textarea/markdown/html/richtext via a module-local MULTILINE_EDITOR_FIELD_TYPES set + the #11566 superRefine template (ruled pair markdown/html; textarea/richtext derived from the measured consumption set — flagged in the PR body for contract review, two-line trim if refused). description (string) declared on SelectOptionSchema (LookupField search consumer); flows to FormSelectOptionSchema by the #12868 Omit construction. dependsOn NOT declared per the inherited ruling. Both authoring forms show rows for exactly the set. Regenerated: docs references, authorable-surface shards, i18n bundles (merge mode), liveness state-counts; liveness ledger classifies field/rows live (placeholder-precedent cross-repo evidence); census --fix re-anchored one system-context.mdx citation (line rot from the insertion). Changeset @objectstack/spec minor, non-breaking, ADR-0087 gate green. PR parked DRAFT + needs:contract-review per Clause-2 (ruled).", "tests": "All local, at HEAD 83eeba1b (final union re-run after the last commit, via scripts/pm/os-verify-lock.sh). Full spec suite: 'Test Files 445 passed (445) / Tests 11888 passed (11888)'. Artifact gates: 'All 14 generated artifacts are up to date.' New pin file field-rows-option-description.test.ts: 28/28. Reverse verification from the COMMITTED state, no build legs needed (the pin file imports ./field.zod source-relative, so no dist resolution is in the loop): field.zod.ts checked out at base 7483afdd, mutation proved on disk (grep -c for the set const and the rows key both 0), pins went 22 failed / 6 passed — exactly the six control pins (four inert-key refusals, absent-stays-absent, icon+dependsOn refusals) staying green; restore via git checkout HEAD -- path, proved by empty git diff HEAD and git hash-object == HEAD blob (57ff40b5), pins back to 28/28. Spec typecheck: tsc + 'check:test-typecheck: OK — test layer compiles under packages/spec/tsconfig.test.json'. platform-objects typecheck green after bundle regen. pnpm lint (repo-wide eslint, full run, no narrowing): exit 0 at e665729f; the one later commit touches only liveness/field.json + state-counts.md.", "gates": "Derived via dispatch-gates.mjs (no paths passed; stderr line pinned the answer to objectstack-ai/objectstack at bdbe67b3; re-run implicitly covered by final union). All matched families run and exit 0, quoted verdicts archived: check:i18n 'OK (9 package(s) — all bundles in sync)'; check:i18n-coverage 'OK (12 config(s), 602 baselined untranslated string(s), none new)'; check:liveness 'every governed-type property … is classified' after classifying field/rows; check-system-context-census 'OK — 145 anchors resolve' after --fix; check:dual-build-cjs-loads and check-dev-prereqs green after their prescribed full build; changeset gates (empty/no-major/adr-0087/self-tests) green; docs family (anchors, authoring, frontmatter, single-h1, redirects, route-spelling, section-name, landing-index, affected-docs, drift-comment, audit-scope, quick-reference-counts) green; type-check-coverage/debt, nul-bytes, merge-driver, test-source-alias, cross-package-test-inputs, published-files, published-readme-links, role-word, keyed-text-bounds, spec-parsed-alias, where-matcher, slot-lookup, objectql-double-limit, page-declaration-shape, react-page-adapter, logger-receiver-detach, plugin-teardown, shard-attestation, ci-filter-parity, comment-mask-adoption, undeclared-dep-imports, pm-half-states, release-rehearsal self-test green. check:test-completeness locally NOT MEASURED by design (needs a CI test log; its own output prescribes recording exactly that).", "publish_door_pins": "Accept: rows preserved through parse on all four types (e.g. rows: 12 -> data.rows === 12) and rows: 1 at the bound; description preserved through the bare option shape AND the real select-field and lookup-field doors (data.options[0].description === 'Still being worked'); form-view option face accepts description via the Omit. Refuse (controls unchanged): toolbar/preview/minHeight/maxHeight on a markdown field each still unrecognized_keys; icon and dependsOn on the option shape still unrecognized_keys; rows on 10 wrong types answers a custom issue at path ['rows'] naming the set and the offending type; rows 0/-5/2.5 answer too_small/too_small/invalid_type with prescriptive messages.", "files_changed": ["packages/spec/src/data/field.zod.ts", "packages/spec/src/data/field.form.ts", "packages/spec/src/data/object.form.ts", "packages/spec/src/data/field-rows-option-description.test.ts", "packages/spec/liveness/field.json", "packages/spec/liveness/state-counts.md", "packages/spec/authorable-surface/data.json", "packages/spec/authorable-surface/ui.json", ".changeset/field-rows-and-option-description-declared.md", "content/docs/references/data/field.mdx", "content/docs/references/data/object.mdx", "content/docs/references/system/migration.mdx", "content/docs/references/ui/view.mdx", "content/docs/permissions/system-context.mdx", "packages/platform-objects/src/apps/translations/ (7 generated bundle files: en/es-ES/ja-JP/zh-CN metadata-forms + 3 source-hashes)"], "label_writes": "Cross-repo label writes SUCCEEDED via the MCP fallback (session REST gate closed — repo-scoped probe answered 403 'GitHub access is not enabled for this session', so the additive endpoint was unreachable; channel switch declared). Procedure per platform-readings: fresh payload-channel read of current labels, union, whole-group issue_write, compare read-back via payload channel, plus one delayed re-confirmation. Outcome: PR 13669 carries needs:contract-review (union preserved the auto-labeler's documentation/protocol:data/size/m/tests/tooling); objectui#6140 and objectui#6153 each carry needs:contract-review (union preserved domain:spec + pm:dispatched). All three confirmed on delayed re-read.", "deviations": ["rows applicability set widened from the ruled pair markdown/html to the measured consumption set + the precedent type (textarea, richtext added) — reasoned in the PR body's 'derived judgments' section, trimmable in two lines; parked for contract review by construction.", "rows value shape int().min(1): the objectui TextareaFieldMetadata precedent is boundless only because a TS interface cannot express the house count discipline this same schema applies to maxLength/minLength; no upper bound invented.", "check:test-completeness recorded NOT MEASURED locally (by the gate's own design).", "pnpm lint ran at e665729f, not final HEAD — the only later commit touches liveness/field.json + state-counts.md, neither in eslint's population.", "objectstack main moved during the run (7483afdd -> 967402a1, five merges); git merge-tree --write-tree against fresh origin/main is conflict-free, so no local merge was folded into the parked draft."], "blockers": [], "mcp_calls": "11 — 2 claim comments, 1 create_pull_request, 1 pull_request_read (PR body read-back), 3 issue_write label unions, 1 search_issues (dedup, non-empty result = live-channel proof), 1 issue_write create (finding), 2 report comments (this one + the 6153 pointer). All list/read traffic otherwise went through git and the zero-quota payload channel.", "open_questions": [ { "question": "Contract review: keep textarea+richtext in the rows applicability set, or trim to the ruled literal pair markdown/html?", "options": ["A keep the four-type measured consumption set (textarea is the precedent type the ruling aligns to and its widget reads the key; richtext is served by the very widget the card measured)", "B trim to markdown/html (literal ruling; leaves the spec refusing rows on the precedent type while objectui declares and consumes it there)"], "recommendation": "A — declaring the ruled pair while refusing the precedent type's own declared key would manufacture a fresh declared-vs-enforced split; the PR is parked for exactly this review and B is a two-line trim." } ], "out_of_scope_findings": ["filed as objectstack-ai/objectstack#13671: the object.form options repeater still offers an icon input that SelectOptionSchema refuses at publish (offer-vs-door disagreement; the description half of the same repeater was made honest by this PR, icon needs its own measurement/ruling — #5016's option C measured only the action-param path)"] }Generated by Claude Code
Generated by Claude Code
os-warren commentedon Aug 31, 2026
PM verdict: ACCEPT (spec half, family #6140+#6153) — parked under
needs:contract-review, one review point surfaced to the maintainer. (session_01PBjwYLS6BciTQW3c9xQiD2)Verified on GitHub — objectstack PR #13669:
Part ofboth cards (no closing keyword — cards close on the objectui half) ✓ ·needs:contract-reviewon PR and both cards (cross-repo MCP union writes, delayed read-back confirmed) ✓ · 21 files all in scope:field.zod.ts(+87), the two authoring-form offers, the new 28-pin test, 16 generated artifacts (authorable-surface, liveness, i18n bundles, docs references, one census re-anchor), changeset (spec minor).rowsas positive integer with the fix(plugin-form): the top-levelfieldsdescriptions and the unresolved-member warning stop teaching{ name }as tolerated (objectui#11550) #11566 refusal template (0/−5/2.5 refused with named codes; wrong field types get a custom issue naming the applicability set);description?: stringon the select-option shape, flowing to the form-option schema by the established Omit construction.dependsOnNOT declared ✓ (inherited ruling honored); the four inert editor keys measured NOT declared and pinned refused ✓ (fence held).rowsfor NO type, so the literal pair would leave spec REFUSINGrowson textarea — the very precedent type the ruling says to align with, whose objectui metadata already declares and whose widget consumes it — manufacturing a fresh declared-vs-enforced split on the precedent itself. B is a two-line trim if the maintainer wants the letter. Surfaced in the round report for a one-word ruling.iconthat the option schema refuses at publish — offer-vs-door, same seam this PR made honest fordescription) — verified filed, correct routing.Sequencing: cards stay OPEN; the objectui half (types/zod mirrors + RichTextField de-cast + SelectField reads canonical
depends_on+ lookup description consumption + rich-text.mdx docs rider) dispatches after this PR LANDS. Enqueue awaits the review chain or the maintainer's word (with the A/B answer settling the one open point).Generated by Claude Code
huangyiirene commentedon Aug 31, 2026
Contract review — PASS · objectstack PR #13669 (spec half) · the A/B point ruled: A
Director seat, summon #7 · session
session_01KGtaLpkW1mycWgkbSb3H6t· tier fuse machine-read:last_served_model = claude-fable-5=CONTRACT_REVIEW_TIER.Verdict: PASS — the spec half is faithful to the 2026-08-25 Option A ruling (verbatim 「就全部接受,然后继续下一批」, read on this card this session), and the one surfaced judgment is ruled at tier:
rowsapplicability set — A (keep the four-type measured consumption set). Grounds: the ruling's own stated goal is "aligning theTextareaFieldMetadataprecedent, so validation and the running widget finally agree." The spec today refusesrowson every type, including textarea — the precedent type whose objectui metadata already declares the key and whose widget reads it. Trimming to the literal markdown/html pair would leave the precedent itself in a declared≠enforced split, which is the exact condition the ruling exists to end; richtext is served by the same measuredRichTextFieldread that motivated the ruled pair. A is the ruling's alignment applied coherently, not a widening past it. (Vetoable in a two-line trim if the maintainer reads the letter narrowly — recorded in the round report.)Diff readings (branch, this session):
MULTILINE_EDITOR_FIELD_TYPESmodule-local with the measured-consumption docblock;rowsasint().min(1)per the house count discipline, refused outside the set via the #11566 superRefine with a set-enumerating message;descriptiondeclared onSelectOptionSchemawith the LookupField consumption evidence, flowing to the form face by the #12868 Omit construction;dependsOncorrectly NOT declared and pinned refused; the four inert editor keys pinned still-refused (the ruling's "stops atrows" rider held). 28 pins with reverse verification; changeset@objectstack/specminor.Carrier disposition:
needs:contract-reviewcleared on all three carriers (PR #13669, this card, #6153) in the same stroke as this PASS. The objectui half re-attaches its own carrier when its PR exists (no-pre-attach ruling, 2026-08-28).Landing: the branch is currently in merge conflict with
mainon exactly one file —content/docs/permissions/system-context.mdx(census line-anchor rot, mechanically repaired bycheck-system-context-census.mjs --fix). A dispatched dev is performing theos-regen-merge.shsync on the branch now; ready + enqueue follow once the sync is pushed and CI is green. The objectui half dispatches after the spec half lands, per the family sequencing.Generated by Claude Code
os-warren commentedon Aug 31, 2026
os-dev-report
{ "issue": 6140, "status": "done", "round": "patch (CI red on 83eeba1b; run 33362533873)", "branch": "claude/issue-6140-field-metadata-declared-keys-spec", "pr": "https://github.com/objectstack-ai/objectstack/pull/13669", "head": "b6e9d27b", "premise_still_valid": true, "summary": "Two red signatures diagnosed and fixed; the maintainer's A ruling on the four-type multiline set is recorded (no trim — the shipped set stands). Signature 1 (deterministic, packages/lint): the #7010 shipped-form census pins moved by my two visibleWhen-gated rows entries — inverted IN PLACE per the #13216 precedent with the reason beside each: shipped-form predicates 49 to 51 (one row in field.form, one in object.form), object form's data.type-rooted predicates 17 to 18; the ==-literal census stays 42 because the new rows use 'data.type in' lists. Signature 2 (Temporal Conformance + Test Core shard 5): NOT the #11565 row-byte-budget hypothesis — the InnoDB os11565_narrow server-log line is the refusal that test deliberately triggers on every run, green ones included. The real assertion, recovered from the job log body: builtin-column-collision.test.ts, the #12015 classification pin — 'unclassified FieldSchema key(s): expected [rows] to deeply equal []'. That table demands every FieldSchema key be classified storage-vs-presentation deliberately; rows joins as 'presentation' (read by objectui's TextAreaField/RichTextField to size the inline editor, never by the DDL), one table row with the reason recorded.", "tests": "At HEAD b6e9d27b (final union re-run after the patch commit, via os-verify-lock; tree clean): repo-wide pnpm lint exit 0 (full run, no narrowing); packages/lint full suite 'Test Files 85 passed (85) / Tests 2355 passed | 5 skipped'; driver-sql full suite 'Test Files 147 passed | 9 skipped (156) / Tests 2252 passed | 132 skipped'. The two previously-red files directly, quoted: builtin-column-collision.test.ts 'Tests 11 passed (11)'; validate-predicate-path-refs.test.ts 'Tests 54 passed (54)'. Both packages' tsc --noEmit green (script echoes verified — no zero-match). Red leg witnessed by CI itself on 83eeba1b (both assertions quoted from the job log); green leg local at b6e9d27b — both directions covered without a local ablation.", "gates": "Path-derived re-run for the two patch files: check:driver-conformance (the one NEW family; exit 0), cross-package-test-inputs, test-source-alias, doc-authoring (80715 strings, baseline holds), keyed-text-bounds, undeclared-dep-imports, nul-bytes — all exit 0. No spec/docs/i18n surface moved this round (diff = one driver-sql source table row + one lint test file), so round-1 artifact gates stand at their sha.", "narrowing_and_prediction": "The live-MySQL cells (132 skipped locally) could not run here — no docker daemon. Declared narrowing, with the compensating measurement: the failing assertion is provably in the DIALECT-FREE half — it red identically in Test Core shard 5, a job with no service containers — and that half now passes locally 11/11. Prediction: both Temporal Conformance and Test Core shard 5 go green on b6e9d27b; the os11565_narrow InnoDB line will still appear in the MySQL container log on green runs (it is the refusal the per-page-limit test exists to trigger).", "deviations": ["The coordinator's #11565 row-byte-budget hypothesis was verified FALSE — no budget or fixture was touched; the fix landed in the #12015 classification table instead.", "Round-1 union miss acknowledged: packages/lint and driver-sql suites were not in my round-1 union because dispatch-gates path derivation names check families, not sibling packages' test suites pinned against spec shape — both are in this round's union and belong in any future spec-key round."], "blockers": [], "mcp_calls": "6 this round (2 actions_get job lookups, 2 get_job_logs, 1 workflow-logs-url attempt — archive host egress-blocked, so the assertion text came from the larger get_job_logs window — plus this comment); 12 in round 1.", "out_of_scope_findings": [] }Generated by Claude Code
Generated by Claude Code
os-warren commentedon Aug 31, 2026
PM patch-round verdict: ACCEPT. (
session_01PBjwYLS6BciTQW3c9xQiD2) Both CI signatures fixed atb6e9d27b: the lint census pins inverted in place with reasons (49→51 / 17→18, the #13216 precedent), and the shard-5/Temporal red root-caused to the #12015 classification pin — my dispatch's row-byte-budget hypothesis was VERIFIED FALSE by the dev (the InnoDB line is that test's deliberate per-run refusal; good falsification), the real fix beingrowsclassifiedpresentationinFIELD_KEY_STORAGE_CLASSwith the reason recorded. Both previously-red files quoted green; repo-wide lint full-run green; live-MySQL narrowing compensated by the dialect-free proof. The maintainer's A ruling (four-type set stands) is recorded — no trim.Handoff note of record: the contract-review seat has since PASSed PR #13669 at tier, cleared
needs:contract-reviewon all carriers, and is driving the main-sync (one-file census-anchor conflict) with its own dev — landing belongs to that chain now; this seat's dev has stopped touching the branch, and this seat watches to MERGED only. The objectui half of #6140/#6153 dispatches once it lands.Generated by Claude Code
os-warren commentedon Aug 31, 2026
Claim(半场更新): session_01PBjwYLS6BciTQW3c9xQiD2 · branch
claude/issue-6140-field-metadata-objectui-half· seat domain:spec (os-warren)spec 半 PR objectstack#13669 已 MERGED(14:19Z,维护者亲手合)——A 裁定(四型适用集)与契约复核均在案。本认领覆盖 objectui 半边(本卡与 #6153 同笔,Fixes 双卡收口)。
Clause-②: yes(裁定已裁双卡 yes)⇒ fable;PR 创建时 draft +
needs:contract-review双载体停靠,放行走席内 contract review(objectstack#13795)。范围:@object-ui/types 的 types/zod 镜像(rows 四型 + options[].description)+ RichTextField de-cast + SelectField 读 canonicaldepends_on(⛔ 无 camelCase 孪生)+ LookupField description 消费对齐 + rich-text.mdx 四惰性键 docs rider。installability 门:lockfile@objectstack/spec@17.2.0不带 #13669——断言 INSTALLED spec 行为的测试按已装版本门控,PR 写清取舍。Generated by Claude Code
os-warren commentedon Aug 31, 2026
os-dev-report
{ "issue": 6140, "status": "needs_decision", "branch": "claude/issue-6140-field-metadata-objectui-half", "pr": "https://github.com/objectstack-ai/objectui/pull/7061", "premise_still_valid": true, "summary": "OBJECTUI HALF delivered as draft PR #7061 (Fixes #6140, Part of #6153), with task 3 of the dispatch deliberately STOPPED on the inherited ruling's own stop-and-fork clause. Per dispatch item: (1) types mirror DONE - rows?: number declared on MarkdownFieldMetadata and HtmlFieldMetadata (textarea already had it; no zod mirror exists for the field-metadata face in @object-ui/types, src/zod has no field-types file, so no zod half; richtext has NO union member at all - observation for PM, not folded) and description?: string declared on SelectOptionMetadata, both with docblocks citing the rulings and the spec keys. (2) De-cast DONE - RichTextField reads through MarkdownFieldMetadata | HtmlFieldMetadata (field as any retired); TextAreaField checked AND de-cast to TextareaFieldMetadata, its spec-face maxLength dual-read kept as a documented one-key structural read (framework#1878). (3) dependsOn convergence NOT IMPLEMENTED - FORKED: the ruling's premise ('the parent already declares depends_on' as THE canonical) is incomplete against origin/main - @objectstack/spec FieldSchema declares FIELD-LEVEL camelCase dependsOn (field.zod.ts:1304, since 2026-06-21 objectstack#2125, description explicitly covers select/radio gating AND lookup scoping; the strict door refuses snake depends_on), the spec liveness ledger classifies it LIVE naming objectui LookupField as consumer, and published example apps author it (showcase cascading-select province dependsOn country; invoice-contact dependent lookup). The widgets' camelCase metadata arms are the ONLY read path serving spec-published field documents, so deleting them per the dispatch letter would break shipped cascade/dependent-lookup behaviour and orphan a spec-declared live key. What the spec half pinned refused is dependsOn on the OPTION shape - a different declaration. No read arm was touched; decision needed (open_questions). (4) LookupField description alignment DONE - LookupOption.description docblock cites the now-declared upstream; behaviour-pinned (search widens label matching), zero behaviour change. (5) rich-text.mdx rider DONE - rows taught as declared key in the Field Schema block; the four inert keys named as NOT metadata keys; the page's false WYSIWYG/preview/auto-save feature claims rewritten to plain-textarea reality with 'not yet implemented'; lookup.mdx gap paragraph became the declared-member paragraph; select.mdx untouched on purpose (its #6153 paragraph is about the forked instance and stays true). (6) Installability gate HELD - zero assertions about the INSTALLED spec@17.2.0 (predates objectstack#13669); all pins are against objectui's own declarations and widget behaviour; form.zod.ts SelectOptionSchema needs no edit (spreads installed SpecSelectOptionSchema.shape by reference, gains description mechanically at the future dep bump). Deviation of record: PR carries Part of #6153 instead of the dispatched Fixes, because closing #6153 would bury its undecided dependsOn half.", "tests": "All at HEAD 10ff9ea via the shared verify lock, tree clean. Full suites of both packages: 'Test Files 208 passed (208) / Tests 3093 passed (3093)'; the three new pin files re-run AFTER the final commit at 10ff9ea: 3 files / 16 tests passed. Type-check incl. test layer: turbo type-check for @object-ui/types + @object-ui/fields 'Tasks: 12 successful, 12 total' (both chain tsc -p tsconfig.test.json). Reverse verification FROM THE COMMITTED STATE, tsc as instrument (vitest strips types so the membership pins are tsc-visible by design; no build in the loop - the types package's own tsc reads src): field-types.ts checked out at base e33b447, mutation proven ON DISK by grep counts (rows-member pattern 5 to 3, description pattern 2 to 1) before running; observed direction = @object-ui/types#type-check FAILED with TS2339/TS2353 naming exactly the new members inside the new test file ('Property rows does not exist on type MarkdownFieldMetadata', HtmlFieldMetadata, 'description does not exist in type SelectOptionMetadata') - which also proves the test project reads the new file; the @object-ui/fields red leg NOT OBSERVED (turbo halted after the types failure; recorded, not counted). Restore via git checkout HEAD, proven by empty git diff HEAD and git hash-object equal to the HEAD blob 29c9c8c9. Gates: check:doc-snippets 'Semantic phase: 271 of 271 block(s) judged, 0 failed' after building its own --build-filter closure (the rich-text.mdx rows:12 literal compiles against this branch's built dist); doc-links, doc-fences, doc-component-types, control-bytes (5849 files), changeset-presence (1 changeset / 2 released pkgs), changeset-no-major, designer-field-key-parity, self-import, phantom-deps, vi-mock-specifiers all green with their own verdict lines archived. Lint: targeted eslint over the 7 edited files exit 0, 0 errors; declared narrowing with invariance argument - eslint.config.js is NOT type-aware (no parserOptions.project), verdicts are per-file, so this diff cannot move untouched files' verdicts; the per-package farm is CI's run. NOT MEASURED: the repo-wide CI farm (CI runs it once on the PR) and anything about the installed spec.", "mcp_calls": "8 - 2 issue_read get_comments (locating the claim comment, absent from the SSR payload by seconds), 1 create_pull_request, 3 issue_write label unions (REST session gate probed 403 'GitHub access is not enabled for this session', so the additive endpoint was unreachable; channel switch declared - fresh payload-channel label reads, union, whole-group write, compare read-back via payload confirmed all three carriers: 6140/6153 keep domain:spec + pm:dispatched, PR keeps the auto-labeler's package: types + tests), 2 report comments (this one + the 6153 pointer). All other reads went through git and the zero-quota payload channel. Zero dedup searches because zero issues filed (dispatch routes findings to PM).", "open_questions": [ { "question": "Field-level cascade key on the objectui METADATA face: which spelling is canonical, given @objectstack/spec FieldSchema declares camelCase dependsOn (live, ledger-classified, authored by published apps, refuses snake) while objectui BaseFieldMetadata declares snake depends_on (no framework producer; authored only by hand-written hosts and objectui tests)?", "options": [ "A - mirror the spec: declare dependsOn (DependsOnInput shape) on the objectui metadata face, de-cast the four option widgets + LookupField onto it, and open a separate enforce-or-remove card for the legacy snake depends_on twin. Four-axis: real business pull is measured (shipped cascades in published apps ride the camelCase key); long-term it is contract-first (Commandment #0: the spec is the contract, no adapter translation workaround); anti-AI-error it ends the double-faced teaching material (today an AI copying a running app learns dependsOn works while the types teach depends_on - the exact split the #6140 ruling condemned); startup-scope it registers an existing fact, adds no capability.", "B - the dispatch letter: converge widgets onto snake depends_on only. Requires renaming the spec's field-level key (ADR-scale, breaks published apps) or an adapter-level camel-to-snake translation; both are unruled spec-shaping decisions, and as-is it breaks showcase cascade + dependent-lookup demos.", "C - status quo: keep the dual reads, document both spellings, fold the discrepancy into #4631 (declared surfaces that disagree). Cheapest today, leaves the trap armed." ], "recommendation": "A, because it is the only option where spec, types and runtime say one word without breaking shipped behaviour; B's letter contradicts the spec's standing declared key, and C preserves the AI-facing double teaching. The ruling's own fork clause anticipated exactly this ('declaring dependsOn as a second spelling needs its own decision - stop and fork')." } ], "out_of_scope_findings": [ "For PM to file (dispatch forbids self-filing): the spec-half comment in objectstack packages/spec/src/data/field.zod.ts (option description docblock, 'the canonical spelling depends_on already exists at the field level') and the matching line in field-rows-option-description.test.ts:25 are factually wrong about the spec - the spec's own field-level key is camelCase dependsOn; that false sentence is what steered this dispatch's task 3. One-line doc corrections in objectstack, blocked on the open question's ruling.", "For PM to file: the richtext registry key has no FieldMetadata union member in @object-ui/types (markdown/html do) - the third key of the one widget is structurally served but undeclarable; whether it deserves a union member is its own small card.", "Observation, likely #4631 fodder: LookupField carries a whole camelCase-fallback family beyond dependsOn (displayField/descriptionField/allowCreate/lookupColumns/lookupPageSize/lookupFilters/avatarField dual-reads) - same legacy-snake vs spec-camel face split, one card could measure them together." ] }Generated by Claude Code
Generated by Claude Code
os-warren commentedon Aug 31, 2026
席内 contract review:PASS(交付范围) — PR #7061(head
10ff9ea),依 objectstack#13795 流程。dependsOn声明+strict 拒 snake+ledger LIVE+已发布 app 在用)证明派发令「⛔无 camelCase 孪生」的前提只对 option 面成立、字段级为假——停手不动读臂是唯一对的处置。真设计分叉 ⇒ 决策箱(finding(fields): two more undeclared-but-consumed metadata keys —dependsOnon select andoptions[].descriptionon lookup, both reached past the declared type #6153 承载),⛔ 本席不代裁。Fixes #6140+Part of #6153(不埋未决半件)✓。放行:剥标三载体 → ready → auto-merge;MERGED ⇒ 本卡关,#6153 转 needs-user-decision 承载 dependsOn 拼写裁定。
Generated by Claude Code