From e7d51240b5e01d36ba267dc2fc95bb17c684c3bc Mon Sep 17 00:00:00 2001 From: Claude Fable 5 Date: Sat, 8 Aug 2026 12:36:47 +0000 Subject: [PATCH] =?UTF-8?q?test(spec):=20=E5=88=86=E8=8A=82=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E4=B8=8E=E7=94=A8=E4=BE=8B=E6=A0=87=E9=A2=98=E7=9A=84?= =?UTF-8?q?=20pin=20=E8=AE=A1=E6=95=B0=E6=94=B9=E7=94=B1=E8=AE=A1=E6=95=B0?= =?UTF-8?q?=E6=96=AD=E8=A8=80=E6=A0=A1=E9=AA=8C=20(#6605)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR-0122 pin 文件里的计数被一条计数断言盯着,但那条断言读的是 `export type Iso...` 声明,读不到旁边散文里写的同一个数字。于是文件里 两处散文各自漂了:分节注释停在 717、用例标题停在 755,而实测 pin 数 是 823。标题此前已被手工纠正过一次(751 -> 754,#6037),之后又漂了 第二次 —— 说明手工维护不是解法。 改为把散文纳入同一条计数机制:按短语匹配,而不是按固定行号,这样以后 新写的句子在写下的那一刻就已被覆盖。收据块里的历史数字(749 -> 822、 -7、136 - 17 - 40 - 5 - 1)刻意不匹配 —— 它们描述的是文件的过去状态。 另加一条"守卫的守卫":短语一个都匹配不到时直接判红,避免改写措辞后 这条检查静默失效。 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_018ffcE95NaMJcL9XJ9VDYgk --- .../src/type-alias-convention.pin.test.ts | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/packages/spec/src/type-alias-convention.pin.test.ts b/packages/spec/src/type-alias-convention.pin.test.ts index 8dfc88732c..e1fb9948ef 100644 --- a/packages/spec/src/type-alias-convention.pin.test.ts +++ b/packages/spec/src/type-alias-convention.pin.test.ts @@ -264,7 +264,14 @@ import type * as M167 from './ui/view.zod.js'; import type * as M170 from './ui/component.zod.js'; // --------------------------------------------------------------------------- -// 717 isomorphic aliases: `z.input` === `z.infer`, so no `XParsed` is declared. +// 823 isomorphic aliases: `z.input` === `z.infer`, so no `XParsed` is declared. +// +// That number is machine-checked, not hand-kept. The runtime companion at the +// bottom of this file recomputes the pin count from the source and asserts that +// every sentence stating it — this header and that case's own title — agrees +// (#6605). Before the check existed this line had been left at 717 while the +// list grew past 800, because the counting assertion reads the `export type +// Iso...` declarations and never the prose sitting beside them. // --------------------------------------------------------------------------- // ai/agent.zod.ts @@ -1604,10 +1611,13 @@ export type AFamilyParsedIsParseState = Assert< // --------------------------------------------------------------------------- describe('ADR-0122 type-alias convention', () => { - // The title tracks the assertion below; it had been left at 751 when #6037 - // moved the count to 754, so it is corrected here rather than left two - // numbers behind. - it('still declares all 755 isomorphic pins', () => { + // The title states the count as well, and hand-tracking it did not hold: it + // was corrected once, from 751 to 754 (#6037), and had drifted again to sit + // 68 behind by the time #6605 looked. Both prose statements of the number — + // this title and the section header above the pin list — are now asserted + // against the recomputed count below, so neither can go stale without a red + // test naming it. + it('still declares all 823 isomorphic pins', () => { // The truth of each pin is proved by tsc, not here — an `Assert>` // that stops holding is a compile error with the alias named. What tsc // cannot notice is a pin that was DELETED: removing the assertion removes @@ -1746,6 +1756,36 @@ describe('ADR-0122 type-alias convention', () => { const self = readFileSync(fileURLToPath(import.meta.url), 'utf8'); const pins = self.match(/^export type Iso\d+ = Assert 822`, `-7`, + // `136 - 17 - 40 - 5 - 1` — and is deliberately NOT matched. Those numbers + // are true about a past state of the file, and rewriting them to today's + // count would destroy the receipts. The phrase caught here is the narrow + // one that can only ever mean "how many pins are in this file right now". + const stated = [...self.matchAll(/(\d+) isomorphic \w+/g)].map((m) => m[0]); + // Guard the guard: if a reword leaves nothing matching, the check below + // passes over an empty list and silently stops existing. + const phrasingMoved = 'no prose states the pin count any more — has the phrasing moved?'; + expect(stated.length, phrasingMoved).toBeGreaterThanOrEqual(2); + const wrong = stated.filter((s) => !s.startsWith(`${pins.length} `)); + expect(wrong, `prose disagreeing with the ${pins.length} pins counted above`).toEqual([]); }); it('leaves the A-family parse behaviour untouched', () => {