What happens
A read against an object that exists only as an unpublished draft is refused by the
authorization middleware with:
[Security] Access denied: the security posture of object 'shyx_customer' could not be resolved for operation 'find'
Producer: packages/plugins/plugin-security/src/security-plugin.ts:1420 (the #3545
fail-closed branch, secMeta.unresolved). The sibling prose lives in
packages/plugins/plugin-security/src/explain-engine.ts:1121.
Why the message is wrong for this case
The deny is correct — nothing here argues with #3545's fail-closed stance. The
explanation is what misleads:
- It describes an internal resolution step, so every reader — human and model — reads it
as a permissions problem and goes looking for a sharing rule to change. The object's
declaration is fine; it simply has not been published. - It is indistinguishable from the genuine
#3545 case (an authenticated principal asking
for an object whose declaration really is missing/corrupt). Two very different remedies,
one string.
Measured downstream cost (cloud#1481): an end-user AI turn asked "how many customers do I
have?" against a draft-only object and spent seven tool calls / 31 seconds oscillating
between a metadata plane that said the object existed and this refusal, then told the user
the object "is missing its sharing/visibility setting" — confident, professional, and wrong.
On a free plan that single turn also exhausted the daily allowance.
Suggested shape
Where the posture is unresolvable because the object has a pending draft and no active
row (the runtime already distinguishes these — that is what previewDrafts overlays),
deny with its own code and message, e.g. unpublished_object: "object 'X' is not published
yet". Keep the existing message for the true unresolvable case.
The consumer side was deliberately not patched: cloud's ask agent now answers the
draft-only case from the metadata plane before a query is attempted
(objectstack-ai/cloud#1481), but the data plane's own message is the producer's to fix and
is reachable from every other client too (REST, GraphQL, objectui).
Filed from objectstack-ai/cloud#1481 (cloud PR objectstack-ai/cloud#1485).
What happens
A read against an object that exists only as an unpublished draft is refused by the
authorization middleware with:
Producer:
packages/plugins/plugin-security/src/security-plugin.ts:1420(the#3545fail-closed branch,
secMeta.unresolved). The sibling prose lives inpackages/plugins/plugin-security/src/explain-engine.ts:1121.Why the message is wrong for this case
The deny is correct — nothing here argues with
#3545's fail-closed stance. Theexplanation is what misleads:
as a permissions problem and goes looking for a sharing rule to change. The object's
declaration is fine; it simply has not been published.
#3545case (an authenticated principal askingfor an object whose declaration really is missing/corrupt). Two very different remedies,
one string.
Measured downstream cost (cloud#1481): an end-user AI turn asked "how many customers do I
have?" against a draft-only object and spent seven tool calls / 31 seconds oscillating
between a metadata plane that said the object existed and this refusal, then told the user
the object "is missing its sharing/visibility setting" — confident, professional, and wrong.
On a free plan that single turn also exhausted the daily allowance.
Suggested shape
Where the posture is unresolvable because the object has a pending draft and no active
row (the runtime already distinguishes these — that is what
previewDraftsoverlays),deny with its own code and message, e.g.
unpublished_object: "object 'X' is not publishedyet". Keep the existing message for the true unresolvable case.
The consumer side was deliberately not patched: cloud's
askagent now answers thedraft-only case from the metadata plane before a query is attempted
(objectstack-ai/cloud#1481), but the data plane's own message is the producer's to fix and
is reachable from every other client too (REST, GraphQL, objectui).
Filed from objectstack-ai/cloud#1481 (cloud PR objectstack-ai/cloud#1485).