Skip to content

feat(tooling): 让 #4251 的清扫棘轮真的有执行力 —— 计数基线 + 单调性校验 - #4342

Merged
os-zhuang merged 2 commits into
mainfrom
claude/service-lookup-any-rule-gap-nd9vox
Jul 31, 2026
Merged

feat(tooling): 让 #4251 的清扫棘轮真的有执行力 —— 计数基线 + 单调性校验#4342
os-zhuang merged 2 commits into
mainfrom
claude/service-lookup-any-rule-gap-nd9vox

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Refs #4251#4321(第一批)的直接续作:那一批把清扫名单落进了 eslint.config.mjs,配了一句注释「NEVER add an entry」—— 而没有任何东西检查这句话。这正是本工作线反复抓到的形态(#4320 的选项配置了一段永不执行的代码;#4127any 看起来和有检查的代码一模一样),所以先把护栏做实,再继续清扫。

问题:ESLint 表达不了棘轮

ignores 里的文件是被完全忽略的,于是三个动作在纯文件名单下不可见:

动作 纯文件名单 pnpm lint
A. 已列文件里新增擦除 看不见 静默通过(文件被 ignore)
B. 文件清干净了但条目留着 看不见(名单开始说谎) 通过
C/D. 把文件加进名单来消音 看不见 通过(加进去就 ignore 了)

A 是最要命的:一处新的 getService<any>('auth') 写进 auth-plugin.ts,搭上既有条目的便车,整条流水线一片绿。

做法:基线即单一事实来源

scripts/slot-lookup-baseline.json(文件 → 站点数)同时是:

  • eslint config 的 ignores(读它的 keys)——两份列表不可能漂移;
  • pnpm check:slot-lookup 强制的计数

脚本不是另写一套匹配逻辑,而是把 grandfathering 摘掉后重跑 ESLint 本身,再按规则的精确 message(从 config 具名导出)筛报告。所以计数器与规则不可能各说各话:改了选择器,它就照新选择器重新测量。

每一条都做了注入验证

A. 已列文件新增擦除     → count grew 1 → 2        (同一改动下 pnpm lint 退出码 0)
B. 已列文件清干净       → ratchet DOWN,提示 --update
C. 干净文件加进名单     → 同上被拒
D. 真带违规的新文件、按真实计数加进名单
                        → 仅单调性一条报错

D 是决定性的:计数 1 = 实测 1,任何纯计数逻辑都会放行,lint 也已被 ignores 消音——只有「基线 key 集合对比 merge-base」这一层能抓住它。所以名单只能减,不能增。

merge-base 读不到时(浅克隆 / 基线在本分支才新建)脚本**明说「NOT verified」**而不是默默当作通过——check:generated 的同款做法;为此 lint.yml 的 checkout 加了 fetch-depth: 0

现状数字

171 个未清扫站点 / 40 个文件(#4321 里写的 180 是清扫前的读数,这里是清扫掉 runtime 四个文件之后的精确值)。往后每一批:清扫文件 → pnpm check:slot-lookup --update 把基线往下棘 → 提交。

验证

结果
pnpm lint(--no-inline-config) clean
pnpm check:slot-lookup ✓ 171 sites / 40 files,none new
A–D 四种绕过 全部被拒(注入实测,见上表)

无 changeset:本 PR 不改任何已发布包的代码,只动根 package.json 脚本、scripts/、eslint 配置与 CI。

下一步:auth / session 集群第二批 —— plugin-auth(20)+ plugin-hono-server/current-user-endpoints(10)+ plugin-security(1),共 31 处,按 IAuthService / ISecurityService 切片。#4214 唯一的线上 fail-open 就出在这一族的 session 读取上,是产出概率最高的一批。

🤖 Generated with Claude Code

https://claude.ai/code/session_01AWW5xEALZNU5VBXfGyWPGz


Generated by Claude Code

…aseline

The sweep list shipped as a bare array of paths in eslint.config.mjs under a
comment reading "NEVER add an entry" — a promise nothing checked, which is
the exact shape this work line keeps finding (#4320's options configured a
block that never ran). ESLint cannot express a ratchet: an ignored file is
ignored COMPLETELY, so three moves were invisible.

The list becomes scripts/slot-lookup-baseline.json — file -> site count — and
is the single source: its keys ARE the config's ignores, its values are what
check:slot-lookup enforces. The script re-runs ESLint with the grandfathering
lifted and matches reports by the rule's exact message (exported from the
config), so the counter cannot drift from the rule; change a selector and it
re-measures against it. Each caught move was verified by injection:

  A. new erasure in a listed file  -> count grew 1 -> 2  (pnpm lint: silent)
  B. listed file cleared, entry kept -> ratchet DOWN, run --update
  C/D. a file ADDED to the list to mute it -> rejected against the merge base
       with main. D is the decisive one: a violating new file baselined at its
       TRUE count passes every count check, and only the key-set comparison
       catches it.

When the merge base is unreadable (shallow clone, baseline new on the branch)
the run says so rather than reading as verified — so lint.yml checks out with
fetch-depth: 0.

171 unswept sites in 40 files today. Batches sweep files, then --update
ratchets the baseline down; it never grows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWW5xEALZNU5VBXfGyWPGz
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 31, 2026 7:03am

Request Review

@github-actions github-actions Bot added ci/cd dependencies Pull requests that update a dependency file size/m and removed ci/cd dependencies Pull requests that update a dependency file labels Jul 31, 2026
The PR changes tooling and CI only — no publishable package diff — which is
exactly the case the Check Changeset gate names as an empty changeset.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AWW5xEALZNU5VBXfGyWPGz
@github-actions github-actions Bot added documentation Improvements or additions to documentation ci/cd dependencies Pull requests that update a dependency file tooling labels Jul 31, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review July 31, 2026 07:17
@os-zhuang
os-zhuang merged commit c5dd9c6 into main Jul 31, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/service-lookup-any-rule-gap-nd9vox branch July 31, 2026 07:17
os-zhuang added a commit that referenced this pull request Jul 31, 2026
* fix(lint,runtime,core): slot-lookup guard sees the split-declaration form (#4251)

The three selectors from #4321 all key off the erasure and the lookup being
in ONE expression. Split them and every selector misses:

    let ql: any;
    try { ql = ctx.getService('objectql'); } catch { /* optional */ }

Selector 1 needs the call inside the declarator (this one has no init),
selector 2 needs `as`, selector 3 needs a type argument. The contract is
erased exactly as in `const ql: any = ctx.getService(...)`.

Why this could not wait for the batches: the baseline's monotonicity check
means a file that leaves the grandfather list can never be re-added, so every
batch converted more of this shape from "grandfathered" into "lint covers
this file and says nothing" -- B2 alone moved plugin-security/security-plugin.ts
into that state. A ratchet that reports a cleaner number the more you sweep
is the #4342 failure wearing different clothes.

It is a RULE, not a fourth selector, and that is the finding. esquery can
match AssignmentExpression:has(CallExpression[...]), but cannot tell which
declaration the assigned identifier resolves to -- so it would equally flag
the correctly-typed form this work line exists to produce (8 such sites
today). That needs scope analysis: cheap, no type information, but a rule.
Verified: flags all 16 real sites, none of the 8 lookalikes.

Baseline 140 -> 169 sites, file count unchanged at 37 -- 29 were already
inside grandfathered files and simply invisible. 16 more could not be
grandfathered and are typed here. No baseline key added.

Verified: ratchet holds (169/37, none new, no files added vs c54c822);
eslint clean on every changed file; tsc on the three DEBT packages
(runtime/core/cloud-connection) shows 0 errors in touched files and
identical whole-package counts vs clean HEAD (18/91/13); runtime 974/68,
core 437/27, platform-objects 254/8, plugin-security 677/32.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(platform-objects): FreshDatastoreEngine extends MigrationFlagEngine (#4251)

CI caught what my local run could not: `platform-objects` has no `typecheck`
script (type-check coverage DEBT), so its dts build is the only gate, and I
had run only its tests. The named surface introduced for the `objectql`
lookup declared the two attestation members but not the engine shape
`attestFreshDatastore` takes, so the call no longer typechecked.

Extending `MigrationFlagEngine` is the right fix rather than widening the
call: that interface IS the contract the helper states, and the surface's
job is to add ObjectQL's attestation members on top of it.

Verified: platform-objects build (CJS/ESM/DTS) succeeds, 254 tests / 8 files
pass, eslint clean; core / runtime / cloud-connection / plugin-security dts
builds re-run and all succeed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants