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.
clientValidation.ts: three metadata types opt out of client-side Zod validation on premises that are stale or name the wrong schema (sharing_rule / translation / connector) #3561
Found while running the read-only two-repo metadata-coverage audit for objectstack#6234 / objectstack#2657. Not fixed there (audit card, no code PR). Filed unassigned.
packages/app-shell/src/views/metadata-admin/clientValidation.ts keeps a documented list of metadata types that deliberately fall through to server-only validation (:77-92). Three of those opt-outs rest on a stated reason that does not hold against framework origin/main (objectstack @ 8e2bbba24f76d56daad166b6b776c69d8c3ac0dc).
1. sharing_rule — "declared but has empty shape"
clientValidation.ts:88:
sharing_rule: SharingRuleSchema is declared but has empty shape — server-only.
Against framework main, SharingRuleSchema is CriteriaSharingRuleSchema (packages/spec/src/security/sharing.zod.ts:217), i.e. BaseSharingRuleSchema.extend({ type, condition }) where the base (:148-171) is a .strict() object declaring name / label / description / object / active / accessLevel / sharedWith{type,value}, with a curated unknown-key error map. Not an empty shape.
2. translation — judged by the wrong schema
clientValidation.ts:89:
translation: TranslationBundleSchema is z.object({}) — accepts anything; server-only.
TranslationBundleSchema is indeed unsuitable — but it is not this kind's schema. It is z.record(LocaleSchema, TranslationDataSchema) (packages/spec/src/system/translation.zod.ts:626), the bundle shape. The translationmetadata kind resolves TranslationItemSchema (packages/spec/src/kernel/metadata-type-schemas.ts:130), a strictObject with locale / name / label plus the full translation-data shape (translation.zod.ts:737-762), complete with alias and guidance maps.
This is the same class of error the file itself documents having already paid for once, forty lines below, for email_template (clientValidation.ts:130-139): the validator named a schema that was not the one BUILTIN_METADATA_TYPE_SCHEMAS binds, so drafts were checked against the wrong contract.
3. connector — the named blocker is not in the schema, and the schema named is not the authoring shape
clientValidation.ts:90-92:
connector: ConnectorSchema requires an id field that's not in the on-disk metadata shape — the spec models the runtime connector instance, not the file. Wiring it would flag every valid connector definition.
Two problems. ConnectorSchema (packages/spec/src/integration/connector.zod.ts:648) opens with name, label, type — there is no required id. And the shape a stack actually declares under connectors: is DeclarativeConnectorEntrySchema (connector.zod.ts:843, stack.zod.ts:477), which is ConnectorSchema.superRefine(...) carrying the ADR-0097 authoring rules — that is the schema a connector draft should be checked against, and it is exactly the one that would catch the authoring mistakes the ADR-0097 rules exist to catch (inlined secrets, providerConfig without a provider, authored actions on a provider-bound instance).
Why this is worth fixing rather than re-wording
The comment block is not decoration: it is the recorded justification for why a metadata type gets no live validation, and the next reader trusts it instead of re-checking. Two of the three reasons are false and the third names a schema that was never the authoring contract, so all three types silently cost the author a save round-trip to learn about a typo — on connector and sharing_rule specifically, where the authoring rules are security-shaped (ADR-0097 credential handling; the sharing criteria whose least-validating write path objectstack#3896 had to harden after it could seed a permissive match-all).
Verification boundary
Read against framework origin/main. packages/app-shell/package.json pins @objectstack/spec: ^17.0.0-rc.2, so whoever fixes this should re-verify each schema against the resolved published version before wiring a loader — some of these shapes moved during 17.x, which is plausibly how at least the sharing_rule note became stale in the first place. webhook is the worked precedent for the wiring itself (clientValidation.ts:124).
Suggested scope
Point the three loaders at the schemas BUILTIN_METADATA_TYPE_SCHEMAS / stack.zod.ts actually bind (SharingRuleSchema, TranslationItemSchema, DeclarativeConnectorEntrySchema), or — where a shape genuinely still cannot be used client-side — replace the stale reason with the real one.
Filed per AGENTS.md Prime Directive #10 while auditing for objectstack#6234. Not assigned; no PR.
Triage: finding. ⛔ No domain:* — objectui is a whole-repo seat and carries no domain routing.
Transfer check — stays in objectui
The landing site is packages/app-shell/src/views/metadata-admin/clientValidation.ts (:77-92, plus the three loaders). It reads framework schemas but changes none of them, so the "shared contract surfaces have one owner" transfer does not fire, and it is neither feature-level nor cross-repo. It stays on the objectui queue.
Why finding rather than pm:queue
Same grade as its sibling from the same sweep (objectstack#6242), and for the same reasons the body itself supplies: it is filed as an audit card with no code PR, and the harm is a missing client-side pre-check rather than a wrong behaviour. Server-side validation still runs and still rejects the bad draft; the cost is a save round-trip, and the drift is in three comment blocks that are stale rather than in code that misbehaves. That is observation-class.
The part that keeps it open rather than closable is real and worth restating: those comments are the recorded justification for why three metadata types get no live validation, the next reader trusts them instead of re-checking, and two of the three reasons are false against framework main —
sharing_rule: "declared but has empty shape" — SharingRuleSchema is CriteriaSharingRuleSchema (packages/spec/src/security/sharing.zod.ts:217) over a .strict() base declaring seven fields;
translation: judged by TranslationBundleSchema (z.record(LocaleSchema, TranslationDataSchema)), which is the bundle shape, while the kind resolves TranslationItemSchema (kernel/metadata-type-schemas.ts:130);
connector: the named blocker (a required id) is not in ConnectorSchema, and the authoring shape is DeclarativeConnectorEntrySchema (connector.zod.ts:843) carrying the ADR-0097 rules.
The body's own precedent is the strongest argument for eventual promotion: email_template (clientValidation.ts:130-139) is the same error already paid for once in this file — a validator pointed at a schema BUILTIN_METADATA_TYPE_SCHEMAS does not bind.
What has to happen before it is dispatchable
The body states the boundary itself, and it is the reason this is not queue-ready as written: packages/app-shell/package.json pins @objectstack/spec: ^17.0.0-rc.2, while every schema reading above was taken from framework origin/main. Some of these shapes moved during 17.x — plausibly how the sharing_rule note went stale in the first place. So each of the three has to be re-verified against the resolved published version before a loader is wired, and until that is done the acceptance criteria cannot be written. webhook (clientValidation.ts:124) is the worked precedent for the wiring once they are.
A findings triage round should promote this once someone confirms the three schemas against the pinned release — that check is small, and it is the whole distance between this card and pm:queue.
Dedup (three repos, open issues and PRs)
⚠️ The GitHub search API is not reachable from this seat (/search/issues answers "sessions are bound to their configured repositories", returning a structurally empty result rather than an error). Dedup was done by paginating all 442 open issues and PRs across the three repos and matching locally on clientValidation, TranslationBundleSchema, DeclarativeConnectorEntrySchema, SharingRuleSchema.
No duplicate. Two neighbours, both distinct: objectstack#5316 (metadata-admin validating a stored view against the authoring schema — the opposite direction: a validator that runs and is too strict, where this is validators that never run) and objectstack#2657 (the Studio coverage programme this audit feeds, pm:queue + pm:blocked, domain:spec). Its sibling card from the same sweep is objectstack#6242, also graded finding this round.
Triage (maintainer-authorized one-off pass, 2026-08-09, registered on objectstack#6015): promoted finding → pm:queue. Three client-validation opt-outs resting on false or wrong-schema premises, two of them on security-shaped types (sharing_rule, connector with the ADR-0097 credential rules), and the file itself records having paid for this exact error class once already (email_template). Deliverable per the filing: point the three loaders at the schemas the platform actually binds (SharingRuleSchema, TranslationItemSchema, DeclarativeConnectorEntrySchema), re-verifying each against the resolved published spec pin before wiring; webhook is the worked precedent.
Claim: PM loop round 5 (objectui whole-repo seat, non-v17 queue wave)
Session: session_017Qqyix2QcnpUC9XeYVDzx3
Branch: claude/issue-3561-clientvalidation-optouts
Worktree: objectui-issue-3561
File surface: packages/app-shell/src/views/metadata-admin/clientValidation.ts + its tests. Serial constraints: disjoint from the other four dispatches of this batch (no other dispatch touches app-shell).
Container & model: M, mode:subagent, model: opus
Scope note from the card, binding: every schema claim must be re-verified against the resolved published spec version the workspace actually installs (package.json pins ^17.0.0-rc.2; today's resolve is rc.5), not against framework origin/main — the card's own verification boundary. webhook (clientValidation.ts:124) is the worked wiring precedent. Where a shape genuinely cannot be used client-side on the resolved version, the honest outcome is a corrected reason, not a forced wiring.
Per-type outcomes, each premise re-verified on the RESOLVED published spec (rc.5) as the card's verification boundary demanded: translation and connector wired on both doors (TranslationItemSchema — the retired note was wrong twice, the bundle schema isn't the kind's schema AND isn't lenient; DeclarativeConnectorEntrySchema with its ADR-0097 superRefine rules — the claimed required id never existed). sharing_rule wired on the CREATE door only — and that split is the judgment call that earned this acceptance: it is the sole wired schema declaring none of the 7 ADR-0010 envelope keys while the metadata read path stamps _packageId type-blind, so a both-doors wiring would 422 stored bodies the platform itself wrote — the objectstack#5316 inversion, independently reached by framework's own #6245 comment. The envelope gap is filed upstream as objectstack#7404 (observation-class, honestly caveated: the echo-back round trip was not executed). If the envelope is later declared, the edit door opens with one set change.
Also right: hasClientValidator gained a mode argument because ResourceEditPage uses it to pick the diagnostics SOURCE — a mode-blind answer would have suppressed the server's own _diagnostics on the gate-less edit path. Three reverse verifications, each with the red set predicted exactly beforehand; one pin honestly downgraded to non-discriminating in the report rather than smoothed over. Binding-parity safeguards adapted to published-spec reality (structural parity + superRefine count, since UNREGISTERED_KIND_SCHEMAS isn't in the release and cross-bundle === is vacuous) — the safeguard's intent preserved, its mechanism corrected, with the reasoning on the record.
Card citation note for the archive: the card's ^17.0.0-rc.2 pin reference had already moved to ^17.0.0-rc.5 on main; resolution unaffected. Flipping ready + auto-merge.
Activity
claude commentedon Aug 7, 2026
Triage:
finding. ⛔ Nodomain:*— objectui is a whole-repo seat and carries no domain routing.Transfer check — stays in objectui
The landing site is
packages/app-shell/src/views/metadata-admin/clientValidation.ts(:77-92, plus the three loaders). It reads framework schemas but changes none of them, so the "shared contract surfaces have one owner" transfer does not fire, and it is neither feature-level nor cross-repo. It stays on the objectui queue.Why
findingrather thanpm:queueSame grade as its sibling from the same sweep (objectstack#6242), and for the same reasons the body itself supplies: it is filed as an audit card with no code PR, and the harm is a missing client-side pre-check rather than a wrong behaviour. Server-side validation still runs and still rejects the bad draft; the cost is a save round-trip, and the drift is in three comment blocks that are stale rather than in code that misbehaves. That is observation-class.
The part that keeps it open rather than closable is real and worth restating: those comments are the recorded justification for why three metadata types get no live validation, the next reader trusts them instead of re-checking, and two of the three reasons are false against framework main —
sharing_rule: "declared but has empty shape" —SharingRuleSchemaisCriteriaSharingRuleSchema(packages/spec/src/security/sharing.zod.ts:217) over a.strict()base declaring seven fields;translation: judged byTranslationBundleSchema(z.record(LocaleSchema, TranslationDataSchema)), which is the bundle shape, while the kind resolvesTranslationItemSchema(kernel/metadata-type-schemas.ts:130);connector: the named blocker (a requiredid) is not inConnectorSchema, and the authoring shape isDeclarativeConnectorEntrySchema(connector.zod.ts:843) carrying the ADR-0097 rules.The body's own precedent is the strongest argument for eventual promotion:
email_template(clientValidation.ts:130-139) is the same error already paid for once in this file — a validator pointed at a schemaBUILTIN_METADATA_TYPE_SCHEMASdoes not bind.What has to happen before it is dispatchable
The body states the boundary itself, and it is the reason this is not queue-ready as written:
packages/app-shell/package.jsonpins@objectstack/spec: ^17.0.0-rc.2, while every schema reading above was taken from frameworkorigin/main. Some of these shapes moved during 17.x — plausibly how thesharing_rulenote went stale in the first place. So each of the three has to be re-verified against the resolved published version before a loader is wired, and until that is done the acceptance criteria cannot be written.webhook(clientValidation.ts:124) is the worked precedent for the wiring once they are.A findings triage round should promote this once someone confirms the three schemas against the pinned release — that check is small, and it is the whole distance between this card and
pm:queue.Dedup (three repos, open issues and PRs)
/search/issuesanswers "sessions are bound to their configured repositories", returning a structurally empty result rather than an error). Dedup was done by paginating all 442 open issues and PRs across the three repos and matching locally onclientValidation,TranslationBundleSchema,DeclarativeConnectorEntrySchema,SharingRuleSchema.No duplicate. Two neighbours, both distinct: objectstack#5316 (metadata-admin validating a stored view against the authoring schema — the opposite direction: a validator that runs and is too strict, where this is validators that never run) and objectstack#2657 (the Studio coverage programme this audit feeds,
pm:queue+pm:blocked,domain:spec). Its sibling card from the same sweep is objectstack#6242, also gradedfindingthis round.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-project-manager commentedon Aug 9, 2026
Triage (maintainer-authorized one-off pass, 2026-08-09, registered on objectstack#6015): promoted
finding→pm:queue. Three client-validation opt-outs resting on false or wrong-schema premises, two of them on security-shaped types (sharing_rule,connectorwith the ADR-0097 credential rules), and the file itself records having paid for this exact error class once already (email_template). Deliverable per the filing: point the three loaders at the schemas the platform actually binds (SharingRuleSchema,TranslationItemSchema,DeclarativeConnectorEntrySchema), re-verifying each against the resolved published spec pin before wiring;webhookis the worked precedent.Generated by Claude Code
yinlianghui commentedon Aug 10, 2026
Claim: PM loop round 5 (objectui whole-repo seat, non-v17 queue wave)
Session:
session_017Qqyix2QcnpUC9XeYVDzx3Branch:
claude/issue-3561-clientvalidation-optoutsWorktree:
objectui-issue-3561File surface:
packages/app-shell/src/views/metadata-admin/clientValidation.ts+ its tests. Serial constraints: disjoint from the other four dispatches of this batch (no other dispatch touches app-shell).Container & model: M,
mode:subagent,model: opusScope note from the card, binding: every schema claim must be re-verified against the resolved published spec version the workspace actually installs (package.json pins
^17.0.0-rc.2; today's resolve is rc.5), not against frameworkorigin/main— the card's own verification boundary.webhook(clientValidation.ts:124) is the worked wiring precedent. Where a shape genuinely cannot be used client-side on the resolved version, the honest outcome is a corrected reason, not a forced wiring.Generated by Claude Code
yinlianghui commentedon Aug 10, 2026
ACCEPT (objectui seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4107.Per-type outcomes, each premise re-verified on the RESOLVED published spec (rc.5) as the card's verification boundary demanded: translation and connector wired on both doors (
TranslationItemSchema— the retired note was wrong twice, the bundle schema isn't the kind's schema AND isn't lenient;DeclarativeConnectorEntrySchemawith its ADR-0097 superRefine rules — the claimed requiredidnever existed). sharing_rule wired on the CREATE door only — and that split is the judgment call that earned this acceptance: it is the sole wired schema declaring none of the 7 ADR-0010 envelope keys while the metadata read path stamps_packageIdtype-blind, so a both-doors wiring would 422 stored bodies the platform itself wrote — the objectstack#5316 inversion, independently reached by framework's own #6245 comment. The envelope gap is filed upstream as objectstack#7404 (observation-class, honestly caveated: the echo-back round trip was not executed). If the envelope is later declared, the edit door opens with one set change.Also right:
hasClientValidatorgained amodeargument because ResourceEditPage uses it to pick the diagnostics SOURCE — a mode-blind answer would have suppressed the server's own_diagnosticson the gate-less edit path. Three reverse verifications, each with the red set predicted exactly beforehand; one pin honestly downgraded to non-discriminating in the report rather than smoothed over. Binding-parity safeguards adapted to published-spec reality (structural parity + superRefine count, sinceUNREGISTERED_KIND_SCHEMASisn't in the release and cross-bundle===is vacuous) — the safeguard's intent preserved, its mechanism corrected, with the reasoning on the record.Card citation note for the archive: the card's
^17.0.0-rc.2pin reference had already moved to^17.0.0-rc.5on main; resolution unaffected. Flipping ready + auto-merge.Generated by Claude Code
fix(app-shell): wire clientValidation for sharing_rule / translation …