Repository navigation
Description
Provenance
Filed by the PM session (session_01NYgmGheCzM6NrHZN436Cxf) recording a maintainer ruling of 2026-08-16 on objectui#4676. The maintainer reviewed the decision-inbox report and approved the contract-coherence group's recommendations verbatim: 「主线1 3 4:同意」— for objectui#4676 the approved recommendation was Option C: add placeholder to FieldSchema in objectstack (the producer moves).
Why (measured, not inferred)
objectui#4676's dispatched measurement (report comment 5301288148, probes with sensitivity controls, origin/main @ 9ce096fb0) found an object-field-level placeholder is read and applied at render time in four packages plus apps/console, none of them the designer preview:
plugin-form:ObjectForm.tsx:601,sectionFields.ts:122(fromObjectSchema),ModalForm.tsx:425,DrawerForm.tsx:350plugin-detail:RecordDetailDrawer.tsx:253(inline edit)app-shell:resolveActionParams.ts:512— field-backed action params inheritplaceholder, documented in the module header as intended behaviourapps/console:FormPage.tsx:343(override.placeholder ?? def.placeholder), rendered at:830- underneath,
@object-ui/fieldswidgets readfield.placeholderoff the metadata carrier
Meanwhile FieldSchema rejects the key by name (unrecognized_keys, prescription text says "never a FieldSchema key. Author hint text through inlineHelpText or description"), so the ObjectStack path 422s on PUT /api/v1/meta/object/:name for a key the designer's own preview renders — the classic preview-renders/save-refuses trap, hitting mostly AI authors.
This is the #7176 doctrine satisfied from the consumer side: the consumers are written, shipped, and reachable; the declaration is what is missing.
The work
- Add
placeholderas a declared optional string key onFieldSchema(packages/spec/src/data/field.zod.ts), with the semantics the renderers already implement (in-input placeholder text; distinct frominlineHelpText). - Remove/replace the now-false "never a FieldSchema key" guidance prose and any alias-table entry steering authors away from it.
- Consumer-package fixture triage per standing rule: run the consumer packages' tests (rest/meta routes, objectql validation fixtures) — a spec accept-set change can flip sibling-package fixtures.
- Changeset: minor (accept-set expansion on a published surface).
Dispatch notes
model: claude-fable-5mandatory — this changes contract accept behaviour (standing 2026-08-12 tiering rule).- Routes to the
packages/specsemantic lane (accept-set change);domain:*left for the triage seat to stamp (single-producer rule). - Downstream: objectui#4676 carries
Blocked-by:this card; once this lands and the objectui pin covers it, the designer trap closes with zero objectui code change (controls stay as they are).
Refs: objectui#4676 (ruling + measurement) · #7176 (doctrine) · #4001 (silent drop → loud rejection).
Activity
os-project-manager commentedon Aug 16, 2026
Triage (triage seat, session
session_01VUKiXCWoLo8Vnd26tHqXzj): lands inpackages/spec/src/data/field.zod.ts(accept-set expansion) ⇒domain:specsemantic face, type Feature.pm:blockingstamped: objectui#4676 is open and carriesBlocked-by: objectstack-ai/objectstack#9019(verified in its body just now), so this card has an open downstream dependent and sorts ahead in lane selection.Rationale: maintainer ruling of 2026-08-16 (Option C on objectui#4676, 「主线1 3 4:同意」) — direction is ruled, this is the destination-repo implementation card. The dispatch mandates in the body stand:
model: claude-fable-5(contract accept-behaviour change, standing 2026-08-12 tiering rule); consumer-package fixture triage with real per-package readings; changeset minor. The prescription prose "never a FieldSchema key" must flip with the schema in the same PR, or the error guidance contradicts the contract.Generated by Claude Code
os-zhuang commentedon Aug 16, 2026
Claim: PM loop round 5⚠️ Same-file churn: #9088 (
Session:
session_01225pUjnCKWqxcc1PeqKFUqBranch:
claude/issue-9019-fieldschema-placeholderWorktree:
objectstack-issue-9019Domain:
domain:specFile surface:
packages/spec/src/data/field.zod.ts(declareplaceholder; flip the now-false "never a FieldSchema key" guidance prose in the same PR) +field.test.ts, one minor changeset, regeneratedcontent/docs/references/**(field pages), consumer fixture triage per the card (stop on breach; explain in the report)Container & model: S,
mode:subagent,model: fable— mandatory (contract accept-set expansion, 2026-08-12 semantic-lane clause; the card and triage both mandate it)Serial constraints cleared: in-flight devs are the #4001 terminal re-measure (docs/audits only — disjoint) — no other dev running.
field.zod.tscurrency guidance) is IN THE MERGE QUEUE right now — dev branches off currentorigin/mainAND re-merges main after #9088 lands before opening/finalizing the PR; both edits are in the guidance-table region, so declare region-level care there. #9019's own regen surface (field reference pages) overlaps no in-flight card.Generated by Claude Code
feat(spec): declare placeholder on FieldSchema — Option C ruling on o…
os-zhuang commentedon Aug 16, 2026
{ "issue": 9019, "status": "done", "branch": "claude/issue-9019-fieldschema-placeholder", "pr": "https://github.com/objectstack-ai/objectstack/pull/9113", "premise_still_valid": true, "summary": "Ruled Option C implemented. Premise verified on origin/main @9bf110fb6 before editing: FieldSchema rejected `placeholder` with unrecognized_keys and the exact prescription ('never a FieldSchema key. Author hint text through `inlineHelpText` or `description`.'), while `inlineHelpText`/`description` parsed. One PM mechanism assumption corrected: the prescription prose does NOT live in field.zod.ts — it lives in FIELD_KEY_GUIDANCE in packages/spec/src/data/authoring-key-lint.ts and is folded into FieldSchema's strictObject guidance options; and NO existing test pinned the placeholder rejection anywhere in packages/spec or the consumer packages (quoted-exact grep), so there was nothing to flip — only accept-side pins to add. Changes: `placeholder` declared as optional string on FieldSchema next to inlineHelpText with .describe() prose carrying the three-way distinction (in-input placeholder vs always-visible inlineHelpText vs description tooltip); FIELD_KEY_GUIDANCE entry removed with a NOTE comment (the authoring-key-lint.test.ts no-rot pin 'no guidance entry names a key the schema now declares' makes the removal gate-enforced); object.form.ts tombstone comment replaced with the real form entry and field.form.ts (the full per-field editor) offers the key too (bounded same-class completion, named in the PR body); liveness/field.json gains a live row with cross-repo evidence citing the measured objectui read points @9ce096fb0 (the liveness gate demands a row for every new authorable property — a surface the card did not name); state-counts.md, authorable-surface/data.json (+data/Field:placeholder) and content/docs/references/data/field.mdx regenerated wholesale; minor changeset with ADR-0087 not-required annotation. #9088 landed mid-task exactly as the PM predicted; origin/main re-merged (merge commit 7cb089f37), clean, both semantics verified coexisting (currency guidance entry + placeholder declaration). FieldTranslation.placeholder was already declared, so the translation surface now has a declared base key.", "tests": "All at final HEAD 7cb089f37 (post-merge), union re-run after the last commit. Reverse verification, direction reject-flips-to-accept: pre-change probe on unmodified origin/main dist — control SUCCESS, +placeholder FAIL unrecognized_keys with the exact prescription, +inlineHelpText/+description SUCCESS; post-change rebuilt dist — +placeholder SUCCESS ('Enter nickname' round-trips), non-string placeholder still rejects (proves the rebuilt artifact was read). Spec full suite: 406 files / 10771 tests passed (309s, maxWorkers=2, under /tmp/os-heavy-verify.lock); spec typecheck EXIT=0. Consumer sweep, direction DOWNSTREAM consumers of @objectstack/spec, per-package real readings: rest 120 files/2001 tests ✓, objectql 212/3730 ✓, lint 73/2065 ✓, metadata 31/603 ✓, metadata-core 10/162 ✓, metadata-fs 6/51 ✓, metadata-protocol 112/1565 ✓ — zero fixture flips (no consumer fixture pinned the rejection). Gate union re-derived via dispatch-gates.mjs against the actual diff, all EXIT=0: changeset-gate-self-tests, cross-package-test-inputs (pnpm + node forms), docs-audit-scope, docs-redirects, merge-driver, objectui-changeset, quick-reference-counts, role-word, spec-parsed-alias, type-source-resolution, nul-bytes, adr-0087-registration, changeset-no-major, empty-changeset, dev-prereqs, doc-formula-expressions, query-options-erasure, engine-double-contract, where-matcher, type-check-coverage, type-check-debt ('33 ledger entries re-measured, none above recorded' — workspace closure built first via turbo 70/70), spec check:api-surface/authorable-surface (green, base-anchor lag is the allowed informational line)/docs/generated/liveness/export-origins. Examples validate: app-crm, app-showcase, app-todo all EXIT=0 (pre-existing draft-flow warnings only). Derivation additions beyond the prompt's gate list: the changeset family (gate-self-tests, objectui-changeset, adr-0087, no-major, empty) and docs family (audit-scope, redirects, quick-reference-counts, role-word) — all run. One mid-run interruption: a 10-minute foreground call was killed while queued behind another agent's lock hold (issue-9068, verified via fuser — a live short vitest run, normal queueing, not a stall); split into separate bounded calls and completed. No ablation was run (no dogfood/dist-ablation surface in this change), so no rebuild statement is owed.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
Generated by Claude Code
os-zhuang commentedon Aug 16, 2026
ACCEPT (content) — PR #9113 (
claude/issue-9019-fieldschema-placeholder, reviewed at head7cb089f, the post-#9088-merge commit). Verified against GitHub, not the report's self-description. Ready-flip waits only on CI convergence.placeholderdeclared as optional string onFieldSchema,.describe()carrying the three-way distinction (placeholder /inlineHelpText/description) so the reference page renders it. The now-false prescription REMOVED at its real address — the dev corrected the PM's assumption (FIELD_KEY_GUIDANCEinauthoring-key-lint.ts, folded in viafieldKeyGuidanceAsStrictOptions(), not literally infield.zod.ts) and the removal is gate-enforced by the no-rot pin, not convention.+placeholderFAIL with the exact prescription), post-change rebuilt dist (+placeholderSUCCESS + round-trip; non-string still rejects — proving the rebuilt artifact was read).currencykey atcurrencyConfig(#8163) #9088 same-file churn handled exactly as dispatched:origin/mainre-merged after it landed (7cb089f), both semantics verified coexisting (currency guidance entry survives; placeholder declaration stands); full suite re-run green post-merge.liverow with cross-repo evidence (the measured objectui read points), authorable-surface + reference page regenerated, form entries (the tombstone comment's flip surface + the per-field editor row, validated by the reconciliation test), minor changeset with ADR-0087 not-required annotation. Consumer sweep across 7 downstream packages: zero fixture flips, searched not assumed.Landing: touches regenerated references (field.mdx) — if
mainmoves on an os-regen surface before queueing, the relay applies; otherwise flip ready + auto-merge at gate green. Downstream: objectui#4676 unblocks on release + pin bump (its own card).Generated by Claude Code
chore(i18n): regenerate platform-objects metadata-form bundles for th…
feat(spec,plugin-security): partial field masking — maskingRule prese…
feat(spec): declare `placeholder` on FieldSchema — Option C ruling on…
feat(spec,plugin-security): partial field masking — maskingRule prese…
test(spec): the second data/ file group's test titles state each cite…