Skip to content

39 hook-ctx call sites across 14 test files still build ctx.input inline, so they keep the plain-object blind spot #1295 closed everywhere else #1298

Description

@hotlong

Filed by the repo:hotcrm PM seat from a measurement reported in PR #1297 (#1295). It is the residual half of that card, deliberately left outside its fence and reported rather than swept in.

The gap

#1295 routed test/helpers/hook-harness.ts's makeCtx through the engine's real wrapper (wrapDeclarativeHook), so a hook handler now receives the flat-record Proxy production hands it instead of a plain object. That closed the blind spot for every call site that goes through makeCtx.

39 call sites across 14 test files do not. They build a hook ctx inline:

const ctx = { event: 'beforeInsert', input, user: undefined, api: harness.api };

so input is a plain object and those assertions keep exactly the under-approximation #1295 removed.

file sites
test/hooks-runtime.test.ts 18
test/flow-scheduled.test.ts 2
test/forecast-period-boundary.test.ts 2
test/knowledge-deflection.test.ts 2
test/knowledge-feedback.test.ts 2
test/ownership-model.test.ts 2
action-sandbox · demo-staffing · forecast-period-end-boundary · priority-rank-parity · quote-accepted-lookups · quote-accepted-payment-terms · territory-seed-coverage 1 each

(test/case-assignment.test.ts's 4 were converted in PR #1297 — the two the card named plus their handler calls.)

Why it is worth doing rather than noting

This is the shape that produced #1133: fifteen delete statements were silent no-ops in production while the tests asserting them passed, because a plain object honours delete and the engine's Proxy does not. Those tests were not merely failing to catch the defect — they were certifying the opposite of production for as long as it existed.

⚠️ test/hooks-runtime.test.ts alone holds 18 of the 39, and it is a hooks-runtime file — the highest-density place in the repo for exactly the class of defect this shape hides.

The failure also generalises past delete: ownKeys ordering, getOwnPropertyDescriptor, has on an absent key, and the non-enumerable reserved keys all diverge between the two shapes, with the same green tests either way.

Direction

Most of these files carry a single local ctxFor-style builder, so the conversion is roughly one line per file — point the builder at makeCtx (or at the exported engineFlatInput) instead of an object literal.

Every assertion that changes verdict is a finding to READ, not noise to suppress. #1295 changed no verdict because PR #1294 had already retired the last live delete; these 39 may not be so lucky, and a red here is the instrument finally telling the truth. ⛔ Never weaken, skip or delete a test to get green — if a red is a genuine defect outside this card's scope, stop and report it.

⚠️ Note the one disclosed divergence in the new harness before relying on it: id is hoisted onto the wrapper by copy, so a record carrying id has it in both places and Object.keys lists it where a real per-row update dispatch would not. Approximate in the enumerable direction only — if a converted assertion trips on exactly that, it is the harness, not the hook.

Acceptance

  • All 39 sites build their ctx through the shared helper, or each exception is named with a measured reason.
  • ⚠️ A pin, or an existing gate extended, that makes a new inline plain-object ctx visible — otherwise the 40th call site re-opens this silently. test/hook-input-shape.test.ts is the natural home.

Refs #1295 · PR #1297 · #1133 · PR #1294 · objectstack#12277

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions