Skip to content

feat(rest/protocol): extend droppedFields write-observability to bulk paths + client SDK (#3455)#3468

Open
os-zhuang wants to merge 3 commits into
mainfrom
feat/3455-dropped-fields-bulk
Open

feat(rest/protocol): extend droppedFields write-observability to bulk paths + client SDK (#3455)#3468
os-zhuang wants to merge 3 commits into
mainfrom
feat/3455-dropped-fields-bulk

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #3455. Follow-up to #3448 (#3431 D2 波及面): single-write PATCH/POST /data already surfaces LEGALLY-stripped write fields (readonly #2948 / readonlyWhen #3042 / #3043 create ingress) as droppedFields; the bulk paths dropped them silently and the typed client warning + CORS mirror were deferred. This closes those out.

What changed

Bulk passthrough — @objectstack/metadata-protocol

  • updateManyData and batchData (update/upsert rows): register a per-row onFieldsDropped collector, attach events to that row's result.
  • insertManyData: per-row droppedFields on each outcome (ingress diff, 1:1 with rows).
  • createManyData: aggregated top-level droppedFields (one event per object/reason, union of field names) — its { records, count } response has no per-row slot, and the insert-time strip is static-readonly only (schema-uniform), so aggregation is faithful, not lossy.
  • Correctness fix bundled in: updateManyData and batchData never threaded the caller's execution context — bulk writes ran context-less, so RLS/FLS and readonlyWhen evaluated without the caller's principal, and the batch create-ingress strip was hard-coded to a non-system context. All engine calls in both methods now run under the resolved context.

Contract — @objectstack/spec

  • BatchOperationResultSchema gains optional per-row droppedFields (covers updateMany + batch, which alias BatchUpdateResponseSchema).
  • CreateManyDataResponseSchema gains the optional aggregated droppedFields.
  • Both omit-when-empty → backward-compatible. No X-ObjectStack-Dropped-Fields header for batches by design: one response header cannot express per-row drops, so the per-row body field is the canonical bulk channel.

Typed client — @objectstack/client

  • CreateDataResult / UpdateDataResult gain droppedFields?: DroppedFieldsEvent[].

CORS — @objectstack/hono + @objectstack/plugin-hono-server

  • x-objectstack-dropped-fields added to the default Access-Control-Expose-Headers (lockstep across both Hono CORS sites) so a cross-origin browser can read the single-write drop header. The body droppedFields remains the primary, cross-origin-safe surface.

Design decisions

  • Envelope (issue open question): kept precise droppedFields (reused DroppedFieldsEventSchema) over a generic warnings envelope, for symmetry with the already-shipped single-write path. A generic envelope, if wanted later, should migrate single + bulk together.
  • GraphQL item — not applicable (documented). GraphQL has no runtime: kernel.graphql is unassigned everywhere, handleGraphQL returns 501, and discovery never advertises /graphql. There is no schema generator or mutation resolver to expose a typed payload field on — nothing to wire until a GraphQL engine lands, at which point the protocol-layer droppedFields is already present and only the GraphQL schema projection would remain.

Tests

  • packages/metadata-protocol/src/protocol.dropped-fields.bulk.test.ts — 10 cases: per-row (updateMany / batch / insertMany), aggregated (createMany), context-threading assertions, system-context exemption, returnRecords:false preserves droppedFields.
  • packages/adapters/hono/src/hono.test.ts — default expose-header assertion for the new header.
  • Full regression green on rebased main: metadata-protocol 70, spec batch+protocol 49, hono 68, plugin-hono-server 17, rest dropped/batch 19; spec drift gates (api-surface, spec-changes) clean; DTS type gate green across all 5 touched packages.

🤖 Generated with Claude Code

… paths + client SDK (#3455)

Follow-up to #3448 (#3431 D2). Single-write PATCH/POST /data already surfaces
LEGALLY-stripped write fields (readonly #2948 / readonlyWhen #3042 / #3043 create
ingress) as `droppedFields`; the bulk paths dropped them silently. This closes out
the deferred波及面.

- metadata-protocol: updateManyData + batchData collect per-row onFieldsDropped
  and attach to each result row; insertManyData attaches per-row via ingress diff;
  createManyData returns an aggregated top-level droppedFields (no per-row slot;
  static-readonly strip is schema-uniform). Correctness fix: updateManyData and
  batchData never threaded the caller `context` — bulk writes ran context-less
  (RLS/FLS/readonlyWhen without the principal; batch create strip forced
  non-system). All engine calls now run under the resolved context.
- spec: BatchOperationResultSchema gains optional per-row droppedFields (covers
  updateMany + batch); CreateManyDataResponseSchema gains the aggregated one. Both
  omit-when-empty. No X-ObjectStack-Dropped-Fields header for batches by design —
  one header cannot express per-row drops, so the body is the canonical channel.
- client: CreateDataResult / UpdateDataResult gain droppedFields?: DroppedFieldsEvent[].
- hono adapter + plugin-hono-server: x-objectstack-dropped-fields added to the
  default Access-Control-Expose-Headers (lockstep across both CORS sites).
- Design decision: kept precise droppedFields (reused DroppedFieldsEventSchema)
  over a generic warnings envelope, for symmetry with the shipped single-write path.
- GraphQL item is a no-op: GraphQL has no runtime (kernel.graphql unassigned,
  handleGraphQL 501s, discovery never advertises it) — nothing to wire until an
  engine lands, at which point the protocol-layer droppedFields is already present.

Tests: 10 bulk protocol cases (per-row + aggregated + context threading +
returnRecords=false) and a CORS default-expose assertion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Canceled Canceled Jul 24, 2026 4:47pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Jul 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 5 package(s): packages/adapters, @objectstack/client, @objectstack/metadata-protocol, @objectstack/plugin-hono-server, @objectstack/spec.

108 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via packages/client, @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/api/data-flow.mdx (via @objectstack/client)
  • content/docs/api/environment-routing.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/client, @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/metadata-protocol, packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/client, @objectstack/plugin-hono-server, @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via packages/client)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/client, packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/client, @objectstack/plugin-hono-server)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/plugin-hono-server, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via packages/adapters, @objectstack/client, @objectstack/plugin-hono-server, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/realtime-protocol.mdx (via @objectstack/client)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/client, @objectstack/plugin-hono-server, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/client, @objectstack/plugin-hono-server, @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…3455)

Generated from the batch/protocol Zod schemas — BatchOperationResult (per-row)
and CreateManyDataResponse (aggregated) gained droppedFields. Regenerated via
gen:schema && gen:docs; these files are generated, not hand-edited.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…elds transitive deps (#3455)

batch.zod.ts now imports DroppedFieldsEventSchema from data-engine.zod, whose
transitive imports (kernel/execution-context, security/explain) pull those into
the objectstack-api skill's referenced-schema set. Regenerated via gen:skill-refs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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 size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rest/protocol: 把 droppedFields 写路径观测扩展到 bulk / GraphQL / client SDK(#3431 D2 波及面 follow-up)

1 participant