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 }}
Repository navigation
spec: declare placeholder on FieldSchema — four objectui render surfaces already honour the key the producer refuses (ruled Option C on objectui#4676) #9019
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:
app-shell: resolveActionParams.ts:512 — field-backed action params inherit placeholder, documented in the module header as intended behaviour
apps/console: FormPage.tsx:343 (override.placeholder ?? def.placeholder), rendered at :830
underneath, @object-ui/fields widgets read field.placeholder off 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 placeholder as a declared optional string key on FieldSchema (packages/spec/src/data/field.zod.ts), with the semantics the renderers already implement (in-input placeholder text; distinct from inlineHelpText).
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).
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).
Triage (triage seat, session session_01VUKiXCWoLo8Vnd26tHqXzj): lands in packages/spec/src/data/field.zod.ts (accept-set expansion) ⇒ domain:spec semantic face, type Feature. pm:blocking stamped: objectui#4676 is open and carries Blocked-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.
Claim: PM loop round 5
Session: session_01225pUjnCKWqxcc1PeqKFUq
Branch: claude/issue-9019-fieldschema-placeholder
Worktree: objectstack-issue-9019
Domain: domain:spec
File surface: packages/spec/src/data/field.zod.ts (declare placeholder; flip the now-false "never a FieldSchema key" guidance prose in the same PR) + field.test.ts, one minor changeset, regenerated content/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. ⚠️ Same-file churn: #9088 (field.zod.ts currency guidance) is IN THE MERGE QUEUE right now — dev branches off current origin/main AND 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.
{
"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": []
}
ACCEPT (content) — PR #9113 (claude/issue-9019-fieldschema-placeholder, reviewed at head 7cb089f, the post-#9088-merge commit). Verified against GitHub, not the report's self-description. Ready-flip waits only on CI convergence.
Ruling fidelity (Option C): placeholder declared as optional string on FieldSchema, .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_GUIDANCE in authoring-key-lint.ts, folded in via fieldKeyGuidanceAsStrictOptions(), not literally in field.zod.ts) and the removal is gate-enforced by the no-rot pin, not convention.
Reverse verification in the correct direction for an accept-set expansion: pre-change probe on unmodified main dist (control SUCCESS / +placeholder FAIL with the exact prescription), post-change rebuilt dist (+placeholder SUCCESS + round-trip; non-string still rejects — proving the rebuilt artifact was read).
Ecosystem sync complete in one PR: liveness ledger live row 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.
Declared amendments accepted: both are the flip's own surface or its bounded same-class completion, named in the PR body — not scope creep.
Landing: touches regenerated references (field.mdx) — if main moves 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).
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…