Uh oh!
There was an error while loading. Please reload this page.
feat(spec,rest): batch recordIds on security/explain (#8326) - #8452
Conversation
- spec: ExplainRequestSchema gains recordIds (1-200, mutually exclusive with recordId, cap exported as EXPLAIN_BATCH_MAX_RECORD_IDS); the decision gains an optional records[] of per-record verdicts sharing the singular record verdict schema. Ordering, cap and missing-record behaviour documented in TSDoc; singular/object-level shapes untouched. - rest: the explain handler answers recordIds by one object-level pass plus the singular record-grained evaluation per unique id, relayed verbatim into records[i] answering recordIds[i] — batch ≡ N singular answers by construction. GET wraps a lone repeated query param; POST strings stay 400. All refusals ride the existing 400 VALIDATION_FAILED envelope. - regenerated: authorable-surface, api-surface, export-origins, docs reference page. - tests: schema round-trip/cap/exclusivity pins; handler ordering, agreement, cap envelope (code+status), missing-record fail-closed, pre-C2 service fail-closed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012MNV7ZSCjNfA38eDCjsXQL
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 2 package(s): 108 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 7 release-owned page(s) also reference the affected code. These are read-only:
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#8326
Maintainer-approved batch form for
security/explain(ruling comment 5278876009, 2026-08-13):recordIds: string[]on the EXISTING request shape — not a sibling endpoint — answering the per-recorddecision.record.visiblemap for one(object, operation)pair in one round trip.Contract (spec)
ExplainRequestSchemagainsrecordIds: string[]— min 1, cap 200 (exported asEXPLAIN_BATCH_MAX_RECORD_IDS; a consumer with more records paginates under it). Over-cap and empty arrays are refused at validation, never truncated.recordIdviasuperRefine: a request carrying both spellings is a loud 400, never a silent precedence.ExplainDecisionSchemagains an optionalrecordsarray. The per-record verdict object is hoisted into one shared (non-exported) schema used by both the singularrecordand eachrecords[]entry, so the two shapes can never drift.records[i]answersrecordIds[i]— same order, same length, duplicates answered per position.{ recordId, visible: false }withdecidedByomitted — the same answer the singular form gives.recordsis absent on singular responses, and the pre-batch fixtures unchanged).Handler (rest)
Same layering semantics by construction: the handler runs one object-level explain for the shared trace, then the SINGULAR record-grained evaluation per unique id, relaying each verdict verbatim into
records[]— the service never seesrecordIds, so batch ≡ N singular answers is a property of the code, and the agreement test pins it from regressing. A service predating record-grained explain fail-closes each entry tovisible: false(a hidden button beats a shown-then-403). GET wraps a lone repeatedrecordIdsquery param (a query string cannot spell a one-element array); a bare string in a POST body stays a 400. All refusals ride the existing400 VALIDATION_FAILEDADR-0112 envelope — no new error codes, no ledger changes.Coverage
error.code === 'VALIDATION_FAILED'; service never called. Exactly 200 parses.decidedByomitted, pinned at schema and handler level.Regenerated artifacts:
authorable-surface/security.json,api-surface/security.json+ signatures,export-origins/security.json,content/docs/references/security/explain.mdx(generated reference page — not a release note).Changeset:
@objectstack/specminor +@objectstack/restminor (additive public surface).On merge, objectui#4296 (the list-row kebab consumer card, Blocked-by this one) unblocks — the list page folds the verdict map into the grid's per-row predicate channel.
Refs: ADR-0090 D6, ADR-0095 C2.
Generated by Claude Code