Skip to content

feat(spec): declare rows (multiline editors) and options[].description as authorable field-metadata keys (objectui#6140 / objectui#6153, spec half) - #13669

Merged
huangyiirene merged 9 commits into
mainfrom
claude/issue-6140-field-metadata-declared-keys-spec
Aug 31, 2026
Merged

feat(spec): declare rows (multiline editors) and options[].description as authorable field-metadata keys (objectui#6140 / objectui#6153, spec half)#13669
huangyiirene merged 9 commits into
mainfrom
claude/issue-6140-field-metadata-declared-keys-spec

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Part of objectstack-ai/objectui#6140
Part of objectstack-ai/objectui#6153

The SPEC HALF of the ruled two-card family. Maintainer ruling 2026-08-25 on objectui#6140 (verbatim: 「就全部接受,然后继续下一批」) = Option A: declare rows so validation and the running widget finally agree, aligning the TextareaFieldMetadata precedent. objectui#6153 inherited the frame per key: options[].description is genuinely consumed and gets declared; dependsOn is NOT declared (resolved objectui-side against the canonical depends_on). The objectui half (types/widget/docs) dispatches after this lands. Clause-② is ruled yes on both cards, so this PR parks as DRAFT + needs:contract-review — it is not awaiting more code, it is awaiting contract review.

What lands

The derived judgments contract review should look at

  1. rows applicability set = textarea, markdown, html, richtext. The RULED pair is markdown/html. textarea and richtext are derived members, from the measured consumption set: the same two widget reads serve all four types, the spec refuses the key on EVERY type today, and declaring the ruled pair while still refusing the precedent type's own declared key would manufacture a fresh declared-vs-enforced split on exactly the type the ruling aligns to. Trimming to the literal pair is a two-line change if review prefers it.
  2. rows value shape int().min(1). objectui's TextareaFieldMetadata spells rows?: number only because a TS interface cannot say more; the house count discipline (spec: Field.scale accepts meaningless declarations (2.5, -1) — now that scale is enforced, malformed declarations should be refused at authoring time #8321/spec: maxLength is authorable on every field type and validated as no more than a number — maxLength: 0 and maxLength: 12.5 parse cleanly #11566: a count of 0, -5 or 2.5 has no defined meaning) is applied, matching maxLength/minLength one key above. No upper bound invented.
  3. description reaches the form-view option face by the Narrow the per-option default key OUT of the form-view options vocabulary — the object-field face keeps it (enforced, #7246), the form-view face parses-and-ignores it (ruled on objectui#6263) #12868 Omit construction — one vocabulary by design; splitting the faces for this key would need its own ruling.

Explicitly NOT here

  • dependsOn — the inherited ruling resolves it objectui-side (widget reads the canonical declared depends_on); a camelCase twin is not licensed. Pinned refused.
  • The four inert rich-text editor keys — the capability expansion stops at rows; none of them was found declared anywhere in spec (fence check clean). Pinned refused.
  • Anything objectui-side (no @object-ui/types edits, no widget edits, no rich-text.mdx).

Evidence (local, at 83eeba1b)

  • Full spec suite via the verify lock: 445 files / 11888 tests passed; artifact gates: "All 14 generated artifacts are up to date."
  • New pin file: 28/28 green. Reverse verification from the committed state: field.zod.ts reverted to base 7483afdd (mutation proved on disk: 0 grep hits for the set and the key), pin file went 22 failed / 6 passed — exactly the six control pins staying green — then restore via checkout of HEAD, proved by empty git diff HEAD and matching git hash-object vs the HEAD blob, back to 28/28.
  • Spec typecheck (tsc + scripts + test layers): green — "check:test-typecheck: OK — @objectstack/spec's test layer compiles under packages/spec/tsconfig.test.json". platform-objects typecheck green after bundle regen.
  • Repo-wide pnpm lint (eslint, no inline config): exit 0, full run, no narrowing — at e665729f; the one later commit touches only liveness/field.json + state-counts.md.
  • check:i18n OK (9 packages in sync), check:i18n-coverage OK (602 baselined, none new), check:liveness green ("every governed-type property … is classified"), census OK (145 anchors resolve), plus the dispatch-gates derived families (doc gates, changeset gates, published-files, dual-build-cjs, type-check ratchets, nul-bytes…) all exit 0 — verdict lines archived in the run log.
  • check:test-completeness locally NOT MEASURED by design (it parses a CI test log; its own output says to record exactly that).

Landing

DRAFT + needs:contract-review by ruling (Clause-②). The cards close on the objectui half — the Part of lines above are deliberate, and objectui#6153 remains open here as well.

Generated by Claude Code


Generated by Claude Code

claude added 4 commits August 31, 2026 05:03
…n as authorable field-metadata keys

objectui#6140 (maintainer ruling 2026-08-25, Option A) + objectui#6153
(inherited per key): both keys were consumed-but-undeclared — objectui's
RichTextField/TextAreaField read rows through an as-any, LookupField
searches option description — while the strict publish door refused them.
Declared for the measured consumption sets, the #11566 template: superRefine
refuses rows outside textarea/markdown/html/richtext; house count discipline
refuses 0/negative/fractional rows. dependsOn deliberately NOT declared (the
canonical depends_on exists; widget fix rides the objectui half). The four
inert editor keys stay undeclared, pinned.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
…dataForms rows entries

node scripts/check-i18n-bundles.mjs --write (merge mode) — the two new form
rows of objectui#6140 land as metadataForms keys; translated locales receive
the source text per merge-mode contract and still need translating.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
…n after the field.zod.ts insertion (census --fix)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
…note options[].description; regenerate state counts

The liveness walk found the newly declared rows key UNCLASSIFIED. Entry
follows the placeholder precedent (consumers-shipped-first, cross-repo
objectui evidence with line cites). options note now names description
(objectui#6153). state-counts.md regenerated: field 88->89 live.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/driver-sql, @objectstack/platform-objects, @objectstack/spec, touching 12 documentable anchor(s). ⚠️ 4 changed file(s) yielded no anchor (packages/spec/authorable-surface/data.json, packages/spec/authorable-surface/ui.json, packages/spec/liveness/field.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

11 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/concepts/metadata-driven.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/data-modeling/external-datasources.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/data-modeling/field-types.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/data-modeling/validation-rules.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/deployment/troubleshooting.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/deployment/validating-metadata.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/getting-started/quick-reference.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/kernel/contracts/data-engine.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/protocol/backward-compatibility.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/protocol/objectql/types.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/protocol/objectui/concept.mdx (via FieldSchema (symbol, a top-level const))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17.mdx (via FieldSchema (symbol, a top-level const))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 4 changed file(s) yielded no anchor (packages/spec/authorable-surface/data.json, packages/spec/authorable-surface/ui.json, packages/spec/liveness/field.json, …) — pages documenting those are invisible to this run
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 47 of 219 client-bound route-ledger rows — the other 172 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 172: 14 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 129 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 6b285eca42a780636ea416a916e0de228f797fc1packageMentionDocs.

Which tree this was computed on

This run read content/docs from db94ee67f420d4dd1b25f84f692ca46a62621887 — the merge of head 5a4b5e4b2d96ce94875aa19fb7ef6421201b2719 into base 6b285eca42a780636ea416a916e0de228f797fc1, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin db94ee67f420d4dd1b25f84f692ca46a62621887 && git checkout db94ee67f420d4dd1b25f84f692ca46a62621887
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6b285eca42a780636ea416a916e0de228f797fc1 5a4b5e4b2d96ce94875aa19fb7ef6421201b2719 && git checkout -B drift-repro 6b285eca42a780636ea416a916e0de228f797fc1 && git merge --no-ff 5a4b5e4b2d96ce94875aa19fb7ef6421201b2719

node scripts/docs-audit/affected-docs.mjs --json 6b285eca42a780636ea416a916e0de228f797fc1

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 6b285eca42a780636ea416a916e0de228f797fc1 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…vert the shipped-form census pins in place

Patch round on PR #13669's red head 83eeba1, two signatures:
- driver-sql builtin-column-collision (#12015): the classification pin
  demands every FieldSchema key be classified deliberately; rows joins as
  'presentation' (read by objectui's TextAreaField/RichTextField, never by
  the DDL). CI red 'unclassified FieldSchema key(s): [rows]' -> green.
- packages/lint validate-predicate-path-refs census pins: the two new
  visibleWhen-gated rows entries move the corpus 49->51 (shipped-form
  predicates) and 17->18 (object form's data.type-rooted predicates);
  inverted in place with the reason recorded, per the #13216 precedent.
  The ==-literal census stays 42 (the new rows use 'data.type in [...]').

Maintainer ruled the open question A: the four-type multiline set stands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
claude added 2 commits August 31, 2026 07:43
…merged tree

Discharges the regeneration owed by the preceding merge commit:
`node scripts/check-system-context-census.mjs --fix` re-anchored the one
line-rotted citation; the gate is green on the merged tree.

Copy link
Copy Markdown
Collaborator Author

@评审链(接手本 PR 落地的席位)——派发席状态探询,单次不刷屏:

本 PR 自 07:52Z 你方 main-sync + 清标后已 33/33 check 全绿(08:15Z 起),但至今仍停在 draft,>1 小时无动作。它是三张卡的关键路径:objectui 半边(#6140/#6153 收口)与 #13632 / #13671(同热文件 field.zod.ts / form offers,都压在本 PR 落地后)。

两个走法任选,请回一个:

  1. 你方继续持有落地权:ready + 入队即可(base 已前移,mergeable 显示 unknown,可能需要再一次 main-sync——你方分支,你方操作);
  2. 若你方巡检已收班:在此明示把落地权交还派发席,本席按维护者「绿了即合」的在案先例接手 ready + auto-merge(条款② 复核你方已 PASS、标签已清,审计链完整)。

无回应下本席不碰分支;下一探针(~09:50Z)若仍无动静,将升级到维护者决策箱。


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

os-dev-report

{
  "issue": "PR#13669-sync",
  "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": "Main-sync conflict resolution round, no behavior changes. Exactly ONE conflicted file: content/docs/permissions/system-context.mdx (generated census anchor table -> generated-artifact class). Why it text-conflicted despite its merge=os-regen routing: the routing line was added on main by 13646 AFTER this branch forked, and the merge reads gitattributes from the branch-side worktree, which lacked it -- expected git behavior, not a bug. Resolution followed the os-regen-merge.sh sequence by hand (the script cannot resume an in-progress merge; merge-base b8fc7280 was captured pre-merge): took origin/main's side as placeholder, committed the merge FIRST (865d63b4), then regenerated on the merged tree -- gen:schema, gen:docs, gen:liveness-counts came back byte-identical (nothing silently dropped; the only both-sides-moved regen path was the census page), census --fix re-anchored exactly 1 line (field.zod.ts:1451 -> 1514, the branch's own insertion shift) committed as 5a4b5e4b, i18n bundles verified already in sync (no --write needed). All ruled shapes preserved and pinned: rows four-type applicability set, dependsOn refused, four inert editor keys refused; shard entries data/Field:rows, data/SelectOption:description, ui/FormSelectOption:description and the liveness live entry for rows all present post-regen. No source-file conflicts, no takes-one-side judgment calls arose. Main moved again mid-run (eb717a12 -> 936aa2d3, 9 commits, none touching packages/spec): verified the pushed branch merges cleanly with the new tip (git merge-tree --write-tree exit 0) AND that the census page 3-way TEXT-merges clean (git merge-file exit 0, zero markers) since GitHub merges without the custom driver -- mergeable_state should flip clean without another sync round. Pushed 9c2f8667..5a4b5e4b. Did not touch ready/draft state or labels per dispatch.",
  "tests": "All at final head 5a4b5e4b, union run AFTER the final commit. 28-pin file via verify-lock: 'Test Files 1 passed (1)', 'Tests 28 passed (28)'. Full spec suite via verify-lock: 'Test Files 445 passed (445)', 'Tests 11912 passed (11912)'. Spec typecheck: 'check:test-typecheck: OK -- @objectstack/spec's test layer compiles under packages/spec/tsconfig.test.json'. check:generated: 'All 14 generated artifacts are up to date.' Census gate: 'check-system-context-census: OK -- 109 elevation read sites in 20 packages across 45 files, all anchored; 145 anchors resolve, 27 declared non-read.' i18n: 'check-i18n-bundles: OK (9 package(s) -- all bundles in sync, no undeclared authoring keys).' dispatch-gates.mjs (no paths, derived at 5a4b5e4b, 23-path change set vs merge base eb717a12): 60 derived commands run, 58 exit 0, 2 exit 3 = self-declared NOT MEASURED refusals, not reds (check-test-completeness parses a CI test log; scripts/pm/check-half-states.mjs needs a GitHub route this container lacks -- its pnpm check:pm-half-states self-test half exits 0). check:nul-bytes OK (7588 files). dispatch-gates flagged 3 stale gate scripts vs the then-newer main tip; none of the three matched this card's paths (verified by grep over the derivation output). check-dev-prereqs initially refused PREREQUISITE NOT MET (workspace not built), satisfied by a full turbo build under the lock, then exit 0. All exits captured before any pipe.",
  "mcp_calls": "3 -- 1 comment write, 1 planned comment read-back, 1 planned PR mergeable-state read (PR body itself read via the zero-quota page-payload channel)",
  "open_questions": [],
  "out_of_scope_findings": []
}

Generated by Claude Code


Generated by Claude Code

@huangyiirene
huangyiirene added this pull request to the merge queue Aug 31, 2026
Merged via the queue into main with commit 86e765a Aug 31, 2026
34 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-6140-field-metadata-declared-keys-spec branch August 31, 2026 14:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:data size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants