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
[finding] The flat-input Proxy's ownKeys lists only data's ENUMERABLE keys — an own non-enumerable key is invisible to getOwnPropertyNames while hasOwnProperty and the descriptor trap both report it #12578
Found while implementing #12397 (descriptor mirror). Unassigned, observation class — no measured consumer, and the repair is a behaviour decision rather than a mechanical one, so it is recorded rather than ridden along.
What it does
installFlatInput (packages/objectql/src/hook-wrappers.ts) answers ownKeys from Object.keys(data):
Object.keys is own enumerable string keys only. So a key data holds as own-but-non-enumerable — or under a symbol — is absent from Object.getOwnPropertyNames(ctx.input) and Reflect.ownKeys(ctx.input) entirely, even though it is genuinely an own property of the payload the engine persists.
Why it is worth writing down now
#12277 routed defineProperty into data, so a hook can put such a key there for the first time; #12397 then made the descriptor trap tell the truth about it. After that fix the trap set answers one payload three ways:
Object.keys / spread / Object.entries are all correct here (they filter by enumerable and the key is not enumerable), which is why this is narrow: the inconsistency is confined to the own-key enumeration surfaces. It is legal for a proxy — the target is extensible and carries no non-configurable own key, so no [[OwnPropertyKeys]] invariant is violated — just untrue.
That card's charter was the descriptor trap, and the ownKeys filtering is deliberate and documented at the call site: it exists to keep the wrapper keys (id/options/ast/data) out of Object.keys/for-in. Its use of Object.keys(data) rather than Reflect.ownKeys(data) looks incidental to that intent, but replacing it is a decision with reach, not a mechanical fix:
Reflect.ownKeys(data) would newly expose symbol keys through the proxy, which nothing has considered.
packages/runtime/src/sandbox/body-runner.ts documents its snapshot as "materialises only what installFlatInput's ownKeys enumerates" (unwrapProxyToPlain, via Object.entries). Object.entries filters by enumerable, so the marshalled set would not in fact change — but that is a claim about today's spelling of the consumer, and the sandbox test double at body-runner.test.ts:141 models the trap as Reflect.ownKeys(t), i.e. the two already disagree about what the contract is.
// beforeInsert hook, caller payload { subject: 'help' }Object.defineProperty(ctx.input,'k',{value: 1,enumerable: false,configurable: true});Object.getOwnPropertyNames(ctx.input);// ['subject'] — 'k' is on `data`, own, and absent here
Dedup
Scanned the 100 most recently created open issues plus a local grep for ownKeys / getOwnPropertyNames across packages/** — no existing card. ⚠️ The contract's prescribed REST dedup channel returned 403 (GitHub access is not enabled for this session) for this session too, which is #12293's shape, so the issue scan went through the MCP list endpoint — declared rather than reported as a clean search.
Found while implementing #12397 (descriptor mirror). Unassigned, observation class — no measured consumer, and the repair is a behaviour decision rather than a mechanical one, so it is recorded rather than ridden along.
What it does
installFlatInput(packages/objectql/src/hook-wrappers.ts) answersownKeysfromObject.keys(data):Object.keysis own enumerable string keys only. So a keydataholds as own-but-non-enumerable — or under a symbol — is absent fromObject.getOwnPropertyNames(ctx.input)andReflect.ownKeys(ctx.input)entirely, even though it is genuinely an own property of the payload the engine persists.Why it is worth writing down now
#12277 routed
definePropertyintodata, so a hook can put such a key there for the first time; #12397 then made the descriptor trap tell the truth about it. After that fix the trap set answers one payload three ways:Object.keys/ spread /Object.entriesare all correct here (they filter byenumerableand the key is not enumerable), which is why this is narrow: the inconsistency is confined to the own-key enumeration surfaces. It is legal for a proxy — the target is extensible and carries no non-configurable own key, so no[[OwnPropertyKeys]]invariant is violated — just untrue.Why it was not repaired inside #12397
That card's charter was the descriptor trap, and the
ownKeysfiltering is deliberate and documented at the call site: it exists to keep the wrapper keys (id/options/ast/data) out ofObject.keys/for-in. Its use ofObject.keys(data)rather thanReflect.ownKeys(data)looks incidental to that intent, but replacing it is a decision with reach, not a mechanical fix:Reflect.ownKeys(data)would newly expose symbol keys through the proxy, which nothing has considered.packages/runtime/src/sandbox/body-runner.tsdocuments its snapshot as "materialises only whatinstallFlatInput'sownKeysenumerates" (unwrapProxyToPlain, viaObject.entries).Object.entriesfilters byenumerable, so the marshalled set would not in fact change — but that is a claim about today's spelling of the consumer, and the sandbox test double atbody-runner.test.ts:141models the trap asReflect.ownKeys(t), i.e. the two already disagree about what the contract is.ownKeys, or the descriptor trap — is exactly the question, and it is answerable only against what a payload is allowed to hold (open in [finding] The flat-input Proxy'sgetOwnPropertyDescriptorsynthesises a descriptor instead of mirroringdata's — now reachable, since #12277 routeddefinePropertyintodata#12397's terms).Repro
Dedup
Scanned the 100 most recently created open issues plus a local grep for⚠️ The contract's prescribed REST dedup channel returned 403 (
ownKeys/getOwnPropertyNamesacrosspackages/**— no existing card.GitHub access is not enabled for this session) for this session too, which is #12293's shape, so the issue scan went through the MCP list endpoint — declared rather than reported as a clean search.