Skip to content

Commit 816889e

Browse files
test(dogfood): flip the three hook-abort pins to the ruled HOOK_ABORTED wire (#27878)
Fixes #27806 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:把两个 dogfood 测试文件里三处仍断言"hook 中止不带 code"的旧线形 pin,翻成裁决后的 `code: 'HOOK_ABORTED'`;句子原文与"未写入任何行"的断言原样保留。 - 为什么改:#27667 按裁决(#27597,6431311681 letter A)让未声明 code 的 hook 中止在 /data 门回 `HOOK_ABORTED`,这三处 pin 没跟上,main 每次跑 Dogfood 分片 1/3 都红。 - 风险与回滚:只改测试断言与注释,不动产品代码、不跳过、不放宽;回滚即 revert 本 PR(main 会重新变红)。 - 先看:`packages/qa/dogfood/test/object-hook-lifecycle-scratch.dogfood.test.ts`(acceptance[6] 的 deep-equal)、`packages/qa/dogfood/test/error-sentences-live.dogfood.test.ts`(acceptance[1] 两个写门的 code 断言)。 - 两分钟验证:`pnpm --filter @objectstack/dogfood exec vitest run test/error-sentences-live.dogfood.test.ts test/object-hook-lifecycle-scratch.dogfood.test.ts`,预期 2 个文件、8 个用例全绿。 - 需要你做的:无,席位落地。 ## What changes A stop-bleed for the main-red `Dogfood Regression Gate (1/3)` since `21c4bdd8`. #27667 made an undeclared hook abort answer the /data write door with `code: 'HOOK_ABORTED'` (ruling `6431311681` letter A on #27597; a declared code is kept). Three pins still asserted the old code-less body. Each now asserts the ruled wire: - `packages/qa/dogfood/test/error-sentences-live.dogfood.test.ts`, acceptance[1], for `POST /data/OBJECT` and `POST /data/OBJECT/createMany` (two pins, one per door). It was `'code' in body` is false. It is now `body.code` is `'HOOK_ABORTED'`. The `body.error === HOOK_SENTENCE` assert and the nothing-written assert stay. The case title and the header comment now name the ruled wire. - `packages/qa/dogfood/test/object-hook-lifecycle-scratch.dogfood.test.ts`, acceptance[6] / negative[0]. The deep-equal is now `{ error: ABORT_MSG, code: 'HOOK_ABORTED', object: ABORT }`. The no-unhandled-error log assert and the no-row assert stay. The comment cites #27597. No skip, no `.todo`, no retry, no product change, and no other test touched. The spelling follows the existing pin at `hook-error-format.dogfood.test.ts:124`. ## Tests All runs used the dependency closure built locally (`pnpm --filter '@objectstack/dogfood^...' build`, lock VERDICT command-exit 0). - **Red reproduced at origin/main `0a4b0fb223`, before any edit:** the two files gave `Tests 3 failed | 5 passed (8)`. The failures are exactly the three pins: - `error-sentences-live.dogfood.test.ts:79` failed for both doors. Received `{"error":"A budget line needs an approver before it can be recorded.","code":"HOOK_ABORTED","object":"errsent_budget"}`. - `object-hook-lifecycle-scratch.dogfood.test.ts:134` failed. The diff was `+ "code": "HOOK_ABORTED"`. - **Green after the flip:** `Test Files 2 passed (2)`, `Tests 8 passed (8)`, at `17b16000d7` and again at the final head `bd2646adfa` (after merging origin/main). - **The flips have teeth.** Each mutation went through `scripts/ablation-replace.mjs`, which verified the anchor 1 -> 0 and the blob change, then restored blob == HEAD with `git diff HEAD` empty: - The deep-equal reverted to the code-less body gives `Tests 1 failed | 3 passed (4)`. - The error-sentences assert reverted to `'code' in body` false gives `Tests 2 failed | 2 passed (4)`. - **Sibling hook-refusal files in shard 1** (`hook-after-abort-rollback`, `hook-after-condition-rollback`, `hook-capability-refusal`, `hook-error-format`, `hook-refusal-user-facing-marking`, `hook-runas-fls`, `lowered-body-door`, `organization-delete-refuse-nonempty`, `object-hook-lifecycle-showcase`) gave `Test Files 9 passed (9)`, `Tests 59 passed (59)`. A grep of `packages/qa/dogfood/test` finds no other pin asserting a code-less hook abort. - `pnpm --filter @objectstack/dogfood typecheck` exits 0. The tsconfig `include` is `test/**/*`, so it covers both files. ## Acceptance notes - The card records that the merge-queue run skipped the dogfood shards while the main push ran them, a filter-parity blind spot. This PR does not fix that, as the card says. - `skip-changeset`: `@objectstack/dogfood` is `"private": true`, and the diff touches only its `test/` files, so nothing is published. ## Verification The gates derived by `node scripts/pm/dispatch-gates.mjs --commands` were run locally on `bd2646adfa`, with each exit code captured before any pipe. `check:dual-build-cjs-loads` was NOT MEASURED: it reported PREREQUISITE NOT MET because it needs a full `pnpm build` of unrelated packages. CI runs it. Full results are in the os-dev-report on #27806. --- _Generated by [Claude Code](https://claude.ai/code/session_01QSeKYmTYfaPbLQD4BJfomW)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3e039e0 commit 816889e

2 files changed

Lines changed: 10 additions & 8 deletions

File tree

‎packages/qa/dogfood/test/error-sentences-live.dogfood.test.ts‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
//
1212
// - acceptance[1]: a sandboxed `beforeInsert` hook that throws a business
1313
// sentence and declares no code. `POST /data/OBJECT` and its `/createMany`
14-
// door answer 400 carrying the sentence verbatim and NO `code` (by pinned
15-
// design on the /data door), and no row is written.
14+
// door answer 400 carrying the sentence verbatim and the ruled
15+
// `code: 'HOOK_ABORTED'` (#27597: an undeclared hook abort answers the
16+
// ledgered code, a declared one is kept), and no row is written.
1617
// - acceptance[2]: a declarative `operation: 'update'` action whose patch
1718
// repeats a `unique: true` email: `POST /actions/OBJECT/ACTION` answers
1819
// 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'
7172
];
7273

7374
for (const [door, send] of doors) {
74-
it(`${door}: 400 with the hook's own sentence, no code, nothing written`, async () => {
75+
it(`${door}: 400 with the hook's own sentence, HOOK_ABORTED, nothing written`, async () => {
7576
const res = await send();
7677
const body: any = await res.json();
7778
expect(res.status, JSON.stringify(body)).toBe(400);
7879
expect(body.error).toBe(HOOK_SENTENCE);
79-
expect('code' in body, `the hook declared no code, so the /data door carries none: ${JSON.stringify(body)}`).toBe(false);
80+
expect(body.code, `the hook declared no code, so the /data door answers the ruled HOOK_ABORTED: ${JSON.stringify(body)}`).toBe('HOOK_ABORTED');
8081
expect(await ql.find('errsent_budget', { where: {}, ...SYS })).toHaveLength(0);
8182
});
8283
}

‎packages/qa/dogfood/test/object-hook-lifecycle-scratch.dogfood.test.ts‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,11 @@ describe('records-forms.object-hook-lifecycle — the scratch hook variants over
128128
}
129129
expect(res.status, res.text).toBe(400);
130130
expect(res.text).toContain(ABORT_MSG);
131-
// [#27508] The abort is a refusal the route answered: the wire answer is
132-
// the hook's own sentence, code-less, exactly as before, and the server
133-
// does not log it as an unhandled error.
134-
expect(res.json).toEqual({ error: ABORT_MSG, object: ABORT });
131+
// [#27508] The abort is a refusal the route answered, and the server does
132+
// not log it as an unhandled error. [#27597] The wire answer is the hook's
133+
// own sentence plus the ruled `HOOK_ABORTED`, since the hook declared no
134+
// code (a declared code is kept).
135+
expect(res.json).toEqual({ error: ABORT_MSG, code: 'HOOK_ABORTED', object: ABORT });
135136
expect(log.matching('[REST] Unhandled error')).toHaveLength(0);
136137
expect(await stack.rows(ABORT, { name: 'ohl-abort' })).toHaveLength(0);
137138
});

0 commit comments

Comments
 (0)