Skip to content

test(scan-surfaces): cross the four uncrossed walk-vs-literal pairs #1834 reported - #1840

Merged
os-steve merged 3 commits into
mainfrom
claude/issue-1839-cross-uncrossed-surfaces
Sep 9, 2026
Merged

os-steve merged 3 commits into
mainfrom
claude/issue-1839-cross-uncrossed-surfaces

Conversation

@claude

@claude claude Bot commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

Part of #1839 (epic #1579). ⛔ No closing keyword: the epic PM closes the card after
triage, same as PR #1838. #1834, #1778, #1770, #1755 and #1529 are named below for
provenance only; none of them is addressed here.

What this card asked, and what the tree answered

Not a new sweep — #1834 already measured the farm and PR #1838 reported these four. The
question here is narrower: a guard walks a root and filters by suffix, while a second
mechanism in the same file names a specific carrier by literal path. Does anything tie
the two together?
For all four, no. Repaired, five assertions in five files, each one
inside an existing self-test block.

Measured against 965933b — origin/main, no sibling branch in the tree.

# surface verdict
1 TEST_FILES (docs-object-term-consistency) repaired — blind, both legs below
2 hookFiles (hook-query-predicate) repaired — blind; the PM's filename-proxy ruling holds, and the equality form is not authorable here
3 readdirSync(OBJECT_DIR) (collaboration-capabilities) repaired — asserted nothing at all; a count is the content proof for this class
4a PAGES / VERBATIM_PAGES (docs-drift) repaired — blind
4b pages / CARRIERS (docs-conversion-rate-spelling) repaired — blind

⭐ The ruling the card asked for: when does a filename count as content?

The card asks whether toContain('_line-item-price-fill.ts') is a content pin, and the
four surfaces only stop contradicting each other under one rule, which is the answer:

A path in a walk's output is a content pin exactly when the other half of the pair
already pins that file's content. Standing alone, it pins a name.

That is why the same gesture is right in three files and wrong in the fourth:

  • docs-object-term-consistency — the neighbouring blocks assert what
    test/sharing-coverage.test.ts must still say (ROW_LABEL, the Simplified label,
    the Traditional cell). Only reachability was missing, so toContain(ledger) completes
    the pair.
  • docs-drift — VERBATIM_PAGES' content is pinned by the block below (each page must
    quote the compiled CEL condition). Same.
  • docs-conversion-rate-spelling — CARRIERS' content is pinned on the very next line.
    Same.
  • hook-query-predicate — nothing anywhere pins _line-item-price-fill.ts's content.
    So there the name is load-bearing for nothing, and the honest repair is a pin on the
    class itself.

The PM's ruling stands, and it is not a matter of taste — leg A measured it. With
every hook body relocated under src/objects/hooks/ and an export * shim left at each
old path, the surface still holds 44 real .ts files and still contains a file named
_line-item-price-fill.ts, carries 0 where: predicates, and the shipped assertion
passes: 71 passed, with a planted filter: defect sitting in the relocated body.

⭐ Is the equality form authorable in hook-query-predicate? No — measured.

The card asked for this to be measured before copying hook-write-shape.test.ts:444, and
the measurement says do not copy it.

hook-write-shape can write expect(covered, …).toBe(sites.length) because it has a
second population: its CASES table is a hand-authored roster of every derived write,
and its runtime half executes each one, so the scanned call sites have something to be
equal to.

hook-query-predicate has no such population. Its runtime half stands up an in-memory
ObjectQL on a synthetic crm_account and asserts kernel behaviour for where / filter
— it never runs a hook body and enumerates no call site. Nothing else in test/ enumerates
the query sites either. Manufacturing one would mean authoring a new hand-maintained roster
of all 18 where: carriers — a new ledger to keep in step, which is a gate farm growing by
exactly the mechanism AGENTS.md scope rule 3 forbids, to buy a stronger pin on a rule whose
own class is well populated.

So the honest form is the weaker one the card names — the surface holds at least one
instance of the class
— and predicateKey() is single-sourced so the rule and the surface
check cannot come to discriminate different classes. Today: 44 files, 18 carrying a
where: predicate.

The five repairs

file line added to an existing block
docs-object-term-consistency.test.ts expect(TEST_FILES).toContain(ROW_LABEL_LEDGER) in has pages and ledgers to scan; the literal single-sourced across its three readers
docs-object-term-consistency.test.ts ⭐ and the same cross derived from the row's own LEDGERS list, in the ledgers that pin a row for this object still carry one — see below
hook-query-predicate.test.ts the surface must hold a where: carrier, in finds files to check
collaboration-capabilities.test.ts schemaFiles.length > 10, hoisted out of the for head in no *.object.ts restates feeds: true
docs-drift.test.ts VERBATIM_PAGES ⊆ PAGES, in reads the real published pages…
docs-conversion-rate-spelling.test.ts expect(pages).toContain(file) in the existing it.each(CARRIERS)

The ablations — three legs each, exit codes captured before any pipe

Every mutation proved on disk (occurrence count before/after and blob hash) before
any result was read; a no-op would have been refused and the run re-done. Every leg
restored under trap … EXIT INT TERM and proved by bytes: post-restore blob identical
to HEAD, git diff HEAD --name-only empty, git status --porcelain empty.

CONTROL = defect present, surface unmutated, shipped guard → must fail (the defect is
real). LEG A = same defect, surface mutated, old assertion → must pass (blind).
LEG B = same defect, same mutation, new assertion → must fail.

1 · TEST_FILES — the walk vs. the ROW_LABEL ledger

Defect: 案件, a retired crm_case spelling, planted in test/sharing-coverage.test.ts
(1 → 2 occurrences, blob 5bba32bc… → 8b193f05…). Mutation: the walk gains
&& !f.includes('coverage') — surface 165 → 157 real .test.ts files, ledger no
longer reached, literal read still succeeding.

CONTROL  VERDICT control-unmutated-surface-exit=1
         AssertionError: test ledger(s) pinning a retired spelling
           test/sharing-coverage.test.ts:1311: 案件
LEG A    VERDICT legA-old-assertion-exit=0     Tests 33 passed (33)   ← MISSED
LEG B    VERDICT legB-new-assertion-exit=1
         AssertionError: the test walk no longer reaches test/sharing-coverage.test.ts …
           ❯ test/docs-object-term-consistency.test.ts:416:9

⭐ One addition beyond the card's literal wording, and the measurement that asked for
it.
The card specifies "one line: the walk's output must contain that ledger file",
singular. But the LEDGERS table names three ledgers for crm_case, each read by
literal path, and leg B above shows the same mutation dropped a second one —
test/automation-docs-coverage.test.ts — out of the walk while the shipped missing
check stayed green, because it also reads by literal path. Pinning one exemplar leaves
the same defect class open two rows down in the same block, so the cross is also derived
from the row's own list, where a new TERMS row gets it for free. Re-measured on the
extended assertion, same defect and same mutation:

LEG A    VERDICT legA-old-assertion-exit=0     Tests 33 passed (33)   ← still MISSED
LEG B    VERDICT legB-new-assertion-exit=1     Tests 3 failed | 30 passed (33)
         AssertionError: ledger(s) pinned by literal path that the test walk no longer
         reaches …: test/sharing-coverage.test.ts, test/automation-docs-coverage.test.ts
           ❯ test/docs-object-term-consistency.test.ts:557:9

2 · hookFiles — the walk vs. the filename

Defect: const ablationQuery = { filter: { id: 'lead_1' } }; — the #1229 defect — planted
in the relocated lead.hook.ts (0 → 1, blob 0d92d1db… → a2a99a2a…). Mutation: the 18
where: carriers moved to src/objects/hooks/ with export * shims left behind, so the
surface stays 44 files and keeps the name the old assertion reads, while carrying
0 where: predicates.

CONTROL  VERDICT control-unmutated-surface-exit=1
         AssertionError: lead.hook.ts passes a `filter:` query key …
LEG A    VERDICT legA-old-assertion-exit=0     Tests 71 passed (71)   ← MISSED
LEG B    VERDICT legB-new-assertion-exit=1
         AssertionError: no file under src/objects/ writes a `where:` predicate at all …
           ❯ test/hook-query-predicate.test.ts:346:7

3 · readdirSync(OBJECT_DIR) — the surface that asserted nothing

Defect: an inert enable: { feeds: true } planted in src/objects/account.object.ts
(0 → 1, blob 2aa0c6fb… → 16d0e994…). Mutation: OBJECT_DIR swapped to a real,
non-empty, class-free tree — src/flows, 26 real .ts files, 0 *.object.ts.

⚠️ The first attempt at this one relocated the schemas instead and came back
legA-old-assertion-exit=1 with Tests no tests — Cannot find module './_picklists'.
That is a load failure, not an assertion, so it is not a measurement and was not read
as "the surface was not blind"; the mutation was replaced with one that leaves the module
graph intact and the legs re-run.

CONTROL  VERDICT control-unmutated-surface-exit=1
         AssertionError: inert `feeds: true` — the spec default is already true …
LEG A    VERDICT legA-old-assertion-exit=0     Tests 7 passed (7)     ← MISSED
LEG B    VERDICT legB-new-assertion-exit=1
         AssertionError: no *.object.ts under src/objects/ — this sweep has gone vacuous …
           ❯ test/collaboration-capabilities.test.ts:173:7

4a · docs-drift — the walk vs. VERBATIM_PAGES

Defect: Any deal over $100K routes through the large-deal path. appended to
content/docs/sales/opportunities.mdx (0 → 1, blob cbb75df9… → c49e15e7…) — the
exclusive phrasing #1087 exists to reject. Mutation: walkDocs skips the sales
directory — 201 → 171 pages reachable, all 30 sales pages still on disk and still
passing existsSync.

CONTROL  VERDICT control-unmutated-surface-exit=1
         × no content/docs page states the large-deal threshold exclusively
           content/docs/sales/opportunities.mdx: "over $100K"
LEG A    VERDICT legA-old-assertion-exit=0     Tests 14 passed (14)   ← MISSED
LEG B    VERDICT legB-new-assertion-exit=1
         AssertionError: these pages exist but the content/docs walk no longer reaches them …
           ❯ test/docs-drift.test.ts:419:7

4b · docs-conversion-rate-spelling — the walk vs. CARRIERS

Defect: 本页的转换率口径与报表一致。 appended to
content/docs/analytics/cubes.zh-Hans.mdx — the rejected spelling. Mutation: the walk
skips analytics — 134 → 126 Chinese pages reachable, all four carrier files still on
disk and still opened by readFileSync.

CONTROL  VERDICT control-unmutated-surface-exit=1
         content/docs/analytics/cubes.zh-Hans.mdx:121 — 转换率
LEG A    VERDICT legA-old-assertion-exit=0     Tests 4 passed (4)     ← MISSED
LEG B    VERDICT legB-new-assertion-exit=1
         AssertionError: content/docs/analytics/cubes.zh-Hans.mdx carries the metric but
         the walk no longer reaches it …
           ❯ test/docs-conversion-rate-spelling.test.ts:131:9

Gates — pnpm verify, every exit code read before any pipe

Each step run in the foreground, redirected to a file, $? read before any pipe. All
eight on final HEAD 568c25a, after the changeset commit.

step exit
validate · typecheck · lint · lint:i18n-gate 0 · 0 · 0 · 0
hygiene · hygiene:tokens · build 0 · 0 · 0
test — Test Files 165 passed (165), Tests 3454 passed | 1 skipped (3455) 0

Test count: 3455 before, 3455 after — unchanged. Test files: 165 → 165. No new it,
no new gate, no new file under test/, matching PR #1838. Nothing to justify against
scope rule 3, because nothing was added.

pnpm lint --json, before on this branch's own base 965933b and after on 568c25a:

errors warnings suggestions
before 0 13 12
after 0 13 12

Neither number moved, as the card predicted.

Scope

Five files under test/, plus a changeset. Six assertions, all inside existing blocks. No src/** change — none of the five
repairs needed one, and every ablation's src/ and content/ mutation was restored and
proved by bytes. content/docs/releases/ untouched. No governed surface. No surface
widened. hook-write-shape.test.ts untouched, and the 28 surfaces #1834 found already
pinned were not re-opened. The --strict flip (#1581), field-no-consumers (#1826 /
#1827) and the family cards #1582–#1587 are out of scope and untouched.

The changeset carries empty frontmatter — the sanctioned "releases nothing"
declaration .github/workflows/changeset-check.yml:33 documents in its own comment, and
the form #1778 used for the same kind of work, per the PM's ruling on #1834.

维护者速读(草稿)

改了什么 —— 四个守卫都是「走查一棵树 + 在同一文件里用字面路径点名某个承载
文件」,两者之间没有任何断言把它们扣在一起。走查一旦够不到那个承载文件,两个读数都
还是健康的:计数由另外一百多个文件撑着,字面读取照样打得开文件 —— 于是规则在一棵
真实、非空、却已经不含它要判的东西的树上继续报绿。#1778 给 PACK_FILES 交叉过的正
是这一对,这四个当时留着没交叉。五处修复,五个文件,每一条都放进已存在的自检块:
没有新 it、没有新门禁,测试数 3455 → 3455 一个没涨。⭐ 第一个面比派发词字面多了一条:
LEGDERS 表给 crm_case 点了三个账本,消融里同一个变异也把
test/automation-docs-coverage.test.ts 挤出了走查,而按字面路径读的 missing 照样绿 ——
只钉一个样本,同一个缺陷类在同一个块里还开着两行,所以那条交叉改成从该行自己的账本
列表推导。

本卡要的那个裁决 —— 「文件名算不算内容?」答案是一条规则,四个面在它下面才不
互相矛盾:走查输出里的一个路径,当且仅当这一对的另一半已经钉住了那个文件的内容
时,才算内容钉子;单独站着,它钉的只是一个名字。
所以同一个手势在三个文件里是对
的(旁边的块已经钉了该文件必须说什么),在 hook-query-predicate 里是错的 ——
仓库里没有任何地方钉 _line-item-price-fill.ts 的内容。

PM 的裁决成立,而且是量出来的 —— 把 17 个 hook 体搬到 src/objects/hooks/ 并在
原路径留下 export * 壳,扫描面仍是 44 个真实 .ts、仍含那个文件名、where: 谓词
归零,而出厂断言照样全绿(71 passed),同时一个 filter: 缺陷就躺在搬走的 body 里。

⚠️ 但等式形态在这个文件里不可写,这一条是我的实测结论 —— hook-write-shape 能
写等式,是因为它有第二个总体:CASES 是手写的派生写入名册,运行半边逐条执行,
扫描到的调用点才有东西可等。hook-query-predicate 没有:它的运行半边是在合成的
crm_account 上立一个内存引擎量 kernel 行为,一个 hook body 都不跑,也不枚举任何调
用点;test/ 里别处也没有。硬造一个,就是新增一份要手工维护的名册 —— 那正是
AGENTS.md 范围规则 3 说的门禁农场。所以取本卡点名的较弱形态,并把 predicateKey()
单一来源化,规则和面检查不会漂成两个类。

风险与代价(含回滚) —— 风险低:只动 test/** 五个文件,src/ 一个字节没动,
不向用户发布任何东西。回滚 = revert 这两个 commit。

⚠️ 一次不算数的读数,记在这里 —— 第三个面的消融我第一次用「把 schema 搬进子目
录」,回来是 legA-exit=1 但 Tests no tests,报 Cannot find module './_picklists'。
那是加载失败不是断言失败,不是一次测量,所以我没把它读成「这个面不瞎」,换了一
个不破坏模块图的变异重跑。

你要做的 —— 三件。① 复核那条「文件名何时算内容」的裁决,它是本 PR 的核心产出,
也是四个面处置不一致的唯一理由。② 确认 changeset 的空 frontmatter(门禁注释 + #1778
先例 + 你在 #1834 上的裁定)。③ 本 PR 停在 draft 等你复审 —— 「等总监复审」。


🤖 Generated with Claude Code

https://claude.ai/code/session_01DuzfS5chho38Yx1jxx9DEj


Generated by Claude Code

os-steve and others added 2 commits September 9, 2026 15:39
Each of these four guards walks a root and filters by suffix, while a second
mechanism in the same file names a specific carrier by literal path. Nothing
tied the two together: a walk that stopped reaching the carrier leaves the
count healthy and the literal read succeeding, so the rule reports clean over
a real, non-empty tree that no longer holds what it judges. #1778 crossed
exactly this pair for PACK_FILES.

- docs-object-term-consistency: TEST_FILES must contain the ROW_LABEL ledger
  the neighbouring blocks read by literal path (literal single-sourced).
- hook-query-predicate: the surface must still hold a `where:` predicate —
  the class the rule judges — not merely a filename. Equality against a
  second population is not authorable here: the runtime half queries a
  synthetic engine and enumerates no call sites.
- collaboration-capabilities: the *.object.ts sweep asserted nothing about
  its surface; the class is any member, so a count is the content proof.
- docs-drift / docs-conversion-rate-spelling: the carrier lists proved by
  existsSync / readFileSync must also be reached by the walk.

All five assertions go into existing self-test blocks: no new `it`, no new
gate, no new file, no widened surface.

Part of #1839

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuzfS5chho38Yx1jxx9DEj
Empty frontmatter — the sanctioned "this PR releases nothing" declaration
documented in .github/workflows/changeset-check.yml, and the form #1778 used
for the same kind of test-only work.

Part of #1839

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

vercel Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated
hotcrm Ignored Ignored Sep 9, 2026 3:57pm UTC

Request Review

@github-actions github-actions Bot added the ci/cd CI plumbing and the verification pipeline label Sep 9, 2026
…plar

The surface-block cross names one ledger. The LEDGERS table beside it names
three for crm_case, each read by literal path, and the other two were left
uncrossed by exactly the mechanism this card is about: the same mutation that
proved the surface blind also dropped automation-docs-coverage.test.ts from
the walk, and the `missing` check stayed green because it reads by literal
path too.

One assertion, inside the same existing `it`, derived from the row's own
ledger list so a new TERMS row is crossed for free.

Part of #1839

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuzfS5chho38Yx1jxx9DEj
@os-steve
os-steve marked this pull request as ready for review September 9, 2026 23:41
@os-steve
os-steve added this pull request to the merge queue Sep 9, 2026
Merged via the queue into main with commit 62e23f5 Sep 9, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd CI plumbing and the verification pipeline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant