From 17b16000d7a0ee261dce5f96980c57066509547a Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 30 Oct 2026 01:54:25 +0000 Subject: [PATCH] test(dogfood): flip the three hook-abort pins to the ruled HOOK_ABORTED wire An undeclared hook abort now answers the /data door with code: 'HOOK_ABORTED' (ruling 6431311681 letter A; declared codes are kept). The error-sentences acceptance[1] case (both POST /data/OBJECT and /createMany) and the object-hook-lifecycle acceptance[6] deep-equal still pinned the code-less body and reddened Dogfood Regression Gate (1/3) on every main push. The sentence-verbatim and nothing-written assertions stay. Claude-Session: https://claude.ai/code/session_01QSeKYmTYfaPbLQD4BJfomW Co-authored-by: Claude --- .../qa/dogfood/test/error-sentences-live.dogfood.test.ts | 9 +++++---- .../test/object-hook-lifecycle-scratch.dogfood.test.ts | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/qa/dogfood/test/error-sentences-live.dogfood.test.ts b/packages/qa/dogfood/test/error-sentences-live.dogfood.test.ts index e7764989550..2ce8eb15cf3 100644 --- a/packages/qa/dogfood/test/error-sentences-live.dogfood.test.ts +++ b/packages/qa/dogfood/test/error-sentences-live.dogfood.test.ts @@ -11,8 +11,9 @@ // // - acceptance[1]: a sandboxed `beforeInsert` hook that throws a business // sentence and declares no code. `POST /data/OBJECT` and its `/createMany` -// door answer 400 carrying the sentence verbatim and NO `code` (by pinned -// design on the /data door), and no row is written. +// door answer 400 carrying the sentence verbatim and the ruled +// `code: 'HOOK_ABORTED'` (#27597: an undeclared hook abort answers the +// ledgered code, a declared one is kept), and no row is written. // - acceptance[2]: a declarative `operation: 'update'` action whose patch // repeats a `unique: true` email: `POST /actions/OBJECT/ACTION` answers // 409 UNIQUE_VIOLATION, `error.details` carries exactly `developerMessage` @@ -71,12 +72,12 @@ describe('[#26572] error answers made honest, over HTTP on a booted scratch app' ]; for (const [door, send] of doors) { - it(`${door}: 400 with the hook's own sentence, no code, nothing written`, async () => { + it(`${door}: 400 with the hook's own sentence, HOOK_ABORTED, nothing written`, async () => { const res = await send(); const body: any = await res.json(); expect(res.status, JSON.stringify(body)).toBe(400); expect(body.error).toBe(HOOK_SENTENCE); - expect('code' in body, `the hook declared no code, so the /data door carries none: ${JSON.stringify(body)}`).toBe(false); + expect(body.code, `the hook declared no code, so the /data door answers the ruled HOOK_ABORTED: ${JSON.stringify(body)}`).toBe('HOOK_ABORTED'); expect(await ql.find('errsent_budget', { where: {}, ...SYS })).toHaveLength(0); }); } diff --git a/packages/qa/dogfood/test/object-hook-lifecycle-scratch.dogfood.test.ts b/packages/qa/dogfood/test/object-hook-lifecycle-scratch.dogfood.test.ts index f221c7c33a3..952ce961136 100644 --- a/packages/qa/dogfood/test/object-hook-lifecycle-scratch.dogfood.test.ts +++ b/packages/qa/dogfood/test/object-hook-lifecycle-scratch.dogfood.test.ts @@ -128,10 +128,11 @@ describe('records-forms.object-hook-lifecycle — the scratch hook variants over } expect(res.status, res.text).toBe(400); expect(res.text).toContain(ABORT_MSG); - // [#27508] The abort is a refusal the route answered: the wire answer is - // the hook's own sentence, code-less, exactly as before, and the server - // does not log it as an unhandled error. - expect(res.json).toEqual({ error: ABORT_MSG, object: ABORT }); + // [#27508] The abort is a refusal the route answered, and the server does + // not log it as an unhandled error. [#27597] The wire answer is the hook's + // own sentence plus the ruled `HOOK_ABORTED`, since the hook declared no + // code (a declared code is kept). + expect(res.json).toEqual({ error: ABORT_MSG, code: 'HOOK_ABORTED', object: ABORT }); expect(log.matching('[REST] Unhandled error')).toHaveLength(0); expect(await stack.rows(ABORT, { name: 'ohl-abort' })).toHaveLength(0); });