Uh oh!
There was an error while loading. Please reload this page.
retire(metadata-admin): remove the standalone validation resource, move its preview to the embedded path - #4248
Merged
yinlianghui merged 2 commits intoAug 11, 2026
Conversation
…ts preview to the embedded path (#4132) `anchors.ts` registered a standalone `validation` resource anchored by `anchorByField('object')` with `createFields` / `createSchema`, so every object's Related tab carried a "Validations" group whose `+` routed to `validation/_new`. ADR-0088 / objectstack#4509 retired the kind; the framework ledger records that a standalone rule never reached any object's write path — it "saved cleanly and gated nothing". Re-measured by parsing the installed `@objectstack/spec` 17.0.0-rc.6 registries (not grep): `validation` is in neither `DEFAULT_METADATA_TYPE_REGISTRY` nor the unregistered-kind list. The embedded anchor `__object_validation` stays — that is the path the framework evaluates. `ValidationPreview` moved rather than retiring with the door: the drawer's `EmbeddedItemEditor` now mounts the preview registered for the anchor's `editAs`, on the live draft, above the SchemaForm. The lookup is generic, so a sub-type with no registered preview (`index`) is unchanged. This makes objectstack#7427's `validation.label` / `.description` / `.tags` read points reachable on the governed path. The `object: <name>` pill goes with the door: its stated justification was that registration, and `ValidationRuleSchema` rejects `object` by name (`unrecognized_keys`), so it could only confirm an unsaveable key. Its test case is replaced with the schema's own verdict plus a negative render assertion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
…nt guard actually red on revert Found by the reverse verification, not by review: with `anchors.ts` restored to `origin/main`, five of the seven retirement assertions went red as predicted and two stayed GREEN on a file that still registers the door. • "does not carry the stale justification" searched for `standalone variants do exist`, but the comment it guards WRAPS (`… but standalone variants` / `// do exist. Match anything …`), so the flat spelling matched nothing and passed for free. Now tolerates the newline + comment prefix between the halves. • "records the retirement" matched a bare `/ADR-0088/`, which the neighbouring `trigger` retirement note already satisfies. Now asserted on wording unique to the validation note plus the `objectui#4132` provenance marker. Re-measured after the fix: 7 red on the reverted file, controls (embedded anchor, populated registry, drawer `editAs`, preview barrel) still green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
marked this pull request as ready for review
August 11, 2026 07:47
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#4132
Verdict: shape B landed — the drawer hosts the preview with no structural surgery
The PM ruling was shape B with A as the measured fallback: retire the standalone door (non-negotiable), and wire
ValidationPreviewinto the embedded path if the drawer can host it; fall back to A (retire the preview with the door) only if hosting it is measurably structural surgery.Measurement of the drawer's structure, taken before writing any wiring:
MetadataDetailDrawer.tsx:122-132renders exactly one thing forkind: 'embedded'—EmbeddedItemEditor, inside a plainflex-1 min-h-0 overflow-autocolumn. No tab strip to extend, no layout to redesign.EmbeddedItemEditoralready owns the livedraftstate (:64) and already resolves the sub-type byeditAs(:54).MetadataPreviewPropsneeds{ type, name, draft }— all three were in hand.getMetadataPreview(editAs)lookup and one conditional mount above theSchemaForm. Diff for the wiring:EmbeddedItemEditor.tsx, +28 lines including the header paragraph explaining why.Not structural surgery, so the fallback was not taken and no follow-up card is needed for the wiring.
What was retired
anchors.tsregistered a standalonevalidationresource anchored byanchorByField('object'), withcreateFields/createSchema/createDefaults. That gave every object's Related tab a "Validations" group whose+navigated tovalidation/_new(RelatedPanel.tsx:257→ the genericmetadata/:type/newroute atAppContent.tsx:642). Gone, together with the comment that justified it — "usually embedded in the object, but standalone variants do exist" — which the framework contradicts.Kept: the embedded anchor
__object_validation(editAs: 'validation',embeddedPath: 'validations'). That is the path the framework evaluates, and it is the control in the new pin.Premise check (requirement 1) — all three items hold
anchors.ts:347standalone resource +previews/index.ts:45preview registration, both present at2776b110bvalidationkindmetadata/validation/newreachable todaylistAnchorsFor('object')returningobject → validation, a non-embedded group, which is whatRelatedPanelrenders the+forThe spec probe imported
@objectstack/spec/dist/kernel/index.mjsdirectly. Note the shipped rc.6 names differ from the issue body's: the registry isDEFAULT_METADATA_TYPE_REGISTRY(an array of 27 entries), notMETADATA_TYPE_SCHEMAS, and the unregistered-kind list is behindlistUnregisteredKindSchemaTypes().What the embedded path renders now
EmbeddedItemEditorlooks upgetMetadataPreview(editAs)and mounts it on the live draft above theSchemaForm, read-only (noonPatch,editing={false}). The lookup is generic, not avalidationspecial case — todayvalidationis the only embedded sub-type with both aneditAsanchor and a registered preview, and the control in the pin asserts a sub-type without one (index) is unchanged and grows no empty preview chrome.This makes objectstack#7427's
validation.label/.description/.tagsread points reachable on the evaluated path; those rows were gradeddeadprecisely because the render only existed behind the retired door.One read retired with the door
ValidationPreviewpainted anobject: < name >pill, explicitly exempted from the #3275 cleanup because "anchors.tsregisters a standalonevalidationresource … so a standalone rule really does carry it". With that registration gone the read has no producer — and it never had one on the governed path either. Measured against the installed spec:So the pill could only ever confirm a key that makes the rule unsaveable — the AGENTS.md #0.1 shape this file already deleted
expressionandpatternfor. Its test case was replaced, not re-spelled (re-spelling would have kept a green test for a key nothing can produce): the schema's own verdict is the instrument, plus a negative render assertion.Pins, and their reverse verification
Three files reverted one at a time against
HEAD, direction predicted first:1.
anchors.tsreverted →anchors.validation-retired.test.ts: 7 red, controls green (predicted red)not.toMatch(/standalone variants do exist/)never matched, because the comment it guards wraps across two lines in the source.toMatch(/ADR-0088/)was already satisfied by the neighbouringtriggerretirement note, so it passed on a file that still registered the door.Both now assert wording unique to this retirement. This is the whole reason the fix is taken out and the pins re-run rather than reasoned about.
2.
EmbeddedItemEditor.tsxreverted →EmbeddedItemEditor.preview.test.tsx: 4 red, 4 controls green (predicted red)3.
ValidationPreview.tsxreverted →ValidationPreview.test.tsx: 1 red, 2 partners green — a deliberately partial directionOnly the render assertion flips. The two schema-probe assertions stay green on the reverted file by design: they measure
ValidationRuleSchema, not this repo's file, so restoring the pill cannot move them. Reporting them as "should have gone red" would misdescribe what they pin.Two test-harness notes worth the next reader's time
Both cost a lap here and neither is a product defect:
MetadataDetailDrawer. The drawer wraps the editor in a RadixSheet, which never settles under the light DOM setup — the identical assertions through< MetadataDetailDrawer >ran past 540s with no output, whileEmbeddedItemEditoralone renders in ~6s.EmbeddedItemEditoris the entirety of the drawer's embedded branch, so the pin mounts it directly and the drawer's half (that it forwardseditAs) is asserted off source text in the sibling pin. Both halves of the path are covered, neither by a hanging render.conditionproperty.SchemaForm'sCONDITION_FIELD_NAMESroutes that name to the CELConditionWidget, whose formula loader is a dynamic import — the unbounded-module-load trap AGENTS.md §测试纪律 describes. Bisected: schemas withname/labelrender in ~44ms, the same schema plusconditionnever settles. The rule'sconditionis asserted on the preview instead, where it is read straight off the draft.Gates
pnpm exec vitest run packages/app-shell/— 337 files / 3195 passed, 1 skippedpnpm --filter @object-ui/app-shell type-check— clean (tsc --noEmit+ typetests)pnpm --filter @object-ui/app-shell lint— 0 errors (2262 pre-existing warnings repo-wide; none on the changed lines)node scripts/check-control-bytes.mjs— OK, 3948 filesnode scripts/check-changeset-no-major.mjs— OK.changeset/retire-standalone-validation-resource-4132.md,@object-ui/app-shell: patch, matching DatasourcePreview renders three key groupsDatasourceSchema(strict) now rejects —retryPolicy,healthCheck,capabilities#4131's precedent for a preview-surface removal. Notskip-changeset(.github/WORKFLOWS.mddocuments 5 workflows that do not exist and omits 9 that do — including a changeset gate and askip-changesetlabel neither of which is real #3724).Orphaned artifacts — swept, nothing stranded
ValidationPreview.tsx/.test.tsx: not orphaned under shape B — the renderer moved to the embedded path, so both stay (with theobjectcase replaced).TYPE_LABELS_EN/ZH.validation('Validation Rule' / 校验规则) stays live — the kind still exists as an embedded sub-type viaeditAs: 'validation'. No i18n change, so no i18n gate applies.content/docs/**for the standalone validation resource (metadata/validation,Validations,registerMetadataResource). No page documents it; the hits for "validation" are all form-field validation. Nothing to correct, andcontent/docs/releases/untouched.createConformance.test.tsreads the registry live, so it simply stops seeing the type).Generated by Claude Code