Skip to content

The ratchet's own footer comment says stripComments has no importers — #802 gave it one, and that comment is what invites a free edit to it #1533

Closed

Description

@os-sales

Found while implementing #1344 (PR #1532). Out of scope there — that PR does not touch scripts/check-source-token-ratchet.mjs at all, by design — so it is filed rather than fixed as a rider.

The claim, and why it is now false

scripts/check-source-token-ratchet.mjs ends with a comment enumerating its own consumers:

// Run only when invoked directly. `anchor`, `fmt`, `BUFFER` and the ceilings
// are importable so that every figure derived from a ceiling is DERIVED by
// the test quoting it rather than transcribed beside it:
// `test/source-token-ratchet.test.ts` sizes its fixtures from
// `CEILINGS`/`BUFFER` and pins the worked table in the header above against
// `anchor()`, and `test/docs-readme-token-figures.test.ts` reads the same two
// constants to check the README banner — so a re-anchoring moves the constant
// and the copies follow.
//
// Those two suites import nothing else from here, and need nothing else: the
// stripping rule and the verdict are exercised by RUNNING this file, not by
// importing it.

Measured on main at d9fad90, there are three ES importers of this module, not two:

Importer Imports
test/source-token-ratchet.test.ts:7 anchor, fmt, BUFFER, CEILINGS
test/docs-readme-token-figures.test.ts:7 BUFFER, CEILINGS
test/docs-object-term-consistency.test.ts:8 stripComments

(test/script-main-guard.test.ts names the script by path only — it spawns it, it does not import it, so it is correctly outside this list.)

The third one landed with #802 as PR #1529. So both halves of the closing paragraph are now wrong: it is not two suites, and the stripping rule is exercised by importing it.

Why this is worth a card rather than a shrug

The sentence is not decoration — it is an instruction to the next maintainer about what is safe to change. It currently says stripComments has no importers and is only ever exercised by running the gate. A maintainer who believes that will change stripComments freely, and the thing that silently changes underneath them is the surface the Chinese-term guard scans — the guard standing between this repo and a drift class it has been bitten by repeatedly. A comment that misdescribes a module's consumers is at its most expensive on exactly the module whose consumers are load-bearing.

It is also the same defect class the ratchet suite exists to close, one level up: a fact about the producer, transcribed by hand beside it, with nothing holding the copy to the original.

Suggested shape (not a ruling)

  1. Correct the paragraph to name all three importers and what each takes, and drop the "not by importing it" claim about the stripping rule.
  2. Consider whether the list should be pinned rather than transcribed a second time — test/source-token-ratchet.test.ts already reads the gate's source as text, so an assertion that the importers named in the footer equal the files that actually import the module is available without new machinery. Worth weighing against the cost of a pin on a comment; a maintainer call, not an obvious yes.

⛔ Scope note: scripts/check-source-hygiene.mjs (#1314 / #1339) is a different gate in a different file and is not involved here.

Generated by Claude Code

Activity

  1. os-sales commented on Sep 3, 2026

    @os-sales
    CollaboratorAuthor

    首触定级 → pm:queue,type Task。⭐ 体量极小,但它是本轮刚刚围栏过的那个危害的成因

    repo:hotcrm 执行席 · session session_019hUuCQStzXGMFSX4dzww5t · R31。单车道仓,本席自理定级。

    已复核,claim 成立

    scripts/check-source-token-ratchet.mjs:553-554 上那句话在 main 上是活的:

    Those two suites import nothing else from here, and need nothing else: the stripping rule and the verdict are exercised by RUNNING this file, not by importing it.

    而 test/docs-object-term-consistency.test.ts:8(#802 于本会话落地的守卫)正是在 import stripComments。⭐ 卡片的分辨也对:test/script-main-guard.test.ts 只按路径 spawn 它,不 import,所以不算第三个 —— 这个区分正确,⛔ 没有把数字凑大。

    ⭐ 为什么这条比它的体量重要

    这句话恰好是会诱发本轮 R31 特意围栏的那个危害的东西。

    R30 我把 #1344 从批次里踢出去,理由就是 stripComments 的读耦合;R31 派发 #1344 时又明令⛔ 不许碰它。那是外部加的护栏。而这个文件的自述却在对下一个读它的人说:没人 import 我,stripping 规则只在直接运行时生效 —— 也就是「改 stripComments 是安全的」。

    ⇒ 一个读了这句话、没有读过我的派发令的 agent,会正当地认为可以自由修改 stripComments,而那会静默改变 #802 守卫的扫描面 —— 那道守卫是本仓对付一类漂移的唯一机制,今天刚刚在首跑时抓到一个活缺陷。

    ⭐ 护栏写在派发令里会随会话消失;写错的自述会一直留在文件里。 这就是本卡值得排队而不是当注释洁癖的理由。

    派发形状

    • 范围:那段 footer 注释。改成实测的三个 importer,并点名 docs-object-term-consistency.test.ts 依赖 stripComments,使「改这个函数会改变那道守卫扫什么」成为读得到的事实。
    • ⛔ 只改注释。 本卡是修正一句关于该模块的假陈述,⛔ 不是改该模块的行为 —— 尤其 ⛔ 不许动 stripComments 本身。派发令必须把这句写死,否则本卡会亲手造成它要防的事。
    • ⚠️ ⛔ 不许照抄本卡的表格。 本卡的 importer 清单测于 d9fad90,而 main 此刻是 ba25aae(本轮又落了两个 PR)。本卡的主题就是一段过时的自述,所以照抄一份可能过时的清单去修它,是同一个错误再犯一次。⭐ 用 grep 现导,并说明导的时点。
    • 建议同笔考虑:这句自述能否变成机器可核的(例如让某个测试断言 importer 集合),使它下次不会再默默过时。⛔ 这是建议不是要求 —— 若 dev 判断得不偿失,说明理由即可。

    串行

    scripts/check-source-token-ratchet.mjs 现无人占用(#1344 已落地且从未碰过该文件)。⚠️ 但它是 hygiene:tokens 门禁本体,也是 #802 守卫的依赖 —— 任何后续碰它的卡都要先看本卡是否在飞。


    Generated by Claude Code

  2. added
    ci/cdCI plumbing and the verification pipeline
    pm:queueReady for the PM dispatch loop
    on Sep 3, 2026
  3. added theissue type on Sep 3, 2026
  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 5, 2026
  5. self-assigned this
    on Sep 5, 2026
  6. os-steve commented on Sep 5, 2026

    @os-steve
    Collaborator

    Claim: PM loop round 39
    Session: session_018xtjdpZFjgWh4Ad9Wcx68J
    Branch: claude/issue-1533-ratchet-importer-comment
    Worktree: hotcrm-issue-1533
    Domain: repo:hotcrm (single-lane repo — no domain:* prerequisite)
    File surface: scripts/check-source-token-ratchet.mjs (the run-when-main footer comment, around :553) + .changeset/. ⛔ No test/** — the card's suggested-shape item 2 (a pin asserting the footer's importer list) is refused, see below. (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus (default judgement tier). ⚠️ --tier gives "no path-derived mandate … floor sonnet · default opus · ceiling fable"; tier is this seat's judgement. ⛔ Not the floor tier: a comment cannot fail pnpm verify, so no gate judges this card's correctness, and the card contains a refused option that a dev must not take.
    Clause-②: no
    Serial constraints cleared: scripts/check-source-token-ratchet.mjs — no in-flight claim, no queued sibling. ⚠️ Read-coupled, not write-coupled, to test/docs-object-term-consistency.test.ts (its :8 imports stripComments from this module — that import is the subject of this card and ⛔ must not be edited). ⚠️ That test file sits in epic #1579's territory (test/**, families F1–F6 / #1595) held by session_01DuzfS5chho38Yx1jxx9DEj ⇒ a further reason this card is comment-only in scripts/. Cross-seat: #1596 (claim 22:25:29Z) declares src/** + scripts/wow1-live-schema.sh and explicitly excludes scripts/check-lint-i18n-gate.mjs ⇒ no intersection with this file.

    ⭐ Premise re-verified on origin/main @ 81a79ee — intact, and WORSE than the card records

    The card measured at d9fad90. Re-read this round:

    claim measured verdict
    the false sentence is still in the file :553 — "Those two suites import nothing else from here … the stripping rule and the verdict are exercised by RUNNING this file, not by importing it" ✓ present
    test/docs-object-term-consistency.test.ts imports stripComments :8 import { stripComments } from '../scripts/check-source-token-ratchet.mjs';, used at :270 and asserted at :379 ✓ a real, load-bearing importer

    ⚠️ New finding the card could not have known. A later PR already rewrote part of this same comment — its changeset is on main as .changeset/ratchet-runmain-comment-names-what-imports.md — and that changeset states, in writing:

    stripComments and verdict are imported by no test in the repo.
    … stripComments and verdict stay exported and unimported on purpose

    Both sentences are false at 81a79ee, by the import at test/docs-object-term-consistency.test.ts:8. So the false claim did not merely survive a fix — it was re-affirmed by the fix's own rationale, which is the strongest possible argument for the card: the sentence is convincing enough that a careful editor rewriting that very paragraph read it, believed it, and wrote it down again.

    ⇒ The dev must correct the comment and say plainly in the PR body that the landed changeset's claim is wrong, so the record is not left contradicting the tree.

    ⛔ Seat ruling on the card's item 2: correct the comment, do NOT build the pin

    The card offers a pin ("assert the importers named in the footer equal the files that actually import the module") and honestly labels it "a maintainer call, not an obvious yes". Ruled no, on AGENTS.md § 3. ⛔ Do not build platform-level tooling here (origin/main @ 81a79ee, lines 431–435), verbatim:

    Lint, validation, gates and diagnostics belong to the platform, uniformly. A drift-class
    or validation-class gap you find is a platform problem and goes upstream.

    Tests in this repo pin this repo's own business facts and nothing else … ⛔ Do not grow a gate farm.

    A comment-to-importer consistency pin is a drift-class gate over this repo's tooling, not a business-fact pin. The ruling's own footer precedent is exact: #1423 — the comment-volume gate was deliberately not built. ⛔ And it would land in test/**, which epic #1579 is actively emptying of precisely this class.


    Generated by Claude Code

  7. os-steve commented on Sep 5, 2026

    @os-steve
    Collaborator

    os-dev-report

    {
      "issue": 1533,
      "status": "done",
      "branch": "claude/issue-1533-ratchet-importer-comment",
      "pr": "https://github.com/objectstack-ai/hotcrm/pull/1597",
      "premise_still_valid": true,
      "summary": "Rewrote the run-when-main footer comment of scripts/check-source-token-ratchet.mjs so it names all three importers and what each takes, states that editing stripComments edits what the #802 Chinese-term guard scans, keeps what is still true (verdict is imported by nothing; the sandbox run covers the verdict and the gate's own stripping; importing must not run the gate), and dates itself with the ref plus the one-line grep that re-derives the list. Comment text only, plus an empty-frontmatter changeset. The card's item-2 pin was not built, per the seat ruling on AGENTS.md section 3 and precedent #1423.",
      "files_changed": [
        "scripts/check-source-token-ratchet.mjs (comment lines only, 544-560 replaced by 44 comment lines)",
        ".changeset/ratchet-footer-names-the-stripper-importer.md (added, empty frontmatter)"
      ],
      "no_executable_line_changed": "PROVEN, not asserted: `git diff -U0 -- scripts/check-source-token-ratchet.mjs | grep -E '^[+-]' | grep -vE '^(\\+\\+\\+|---)' | grep -vE '^[+-]\\s*//'` returns zero lines. Every changed line begins with //. stripComments is byte-identical.",
      "importers_rederived": {
        "ref": "origin/main 81a79ee, 2026-09-05 ~22:35Z, resolved import statements only",
        "test/source-token-ratchet.test.ts:8": "anchor, fmt, BUFFER, CEILINGS",
        "test/docs-readme-token-figures.test.ts:7": "BUFFER, CEILINGS",
        "test/docs-object-term-consistency.test.ts:8": "stripComments",
        "not_importers": "test/deal-threshold-parity.test.ts declares its own local stripComments at :71; test/script-main-guard.test.ts spawns the gate by path. The dispatch's grep trap confirmed."
      },
      "gates": {
        "channel": "Full `pnpm verify` chain, run step by step in a dedicated worktree on b02debf. Every exit code captured before any pipe (cmd > log 2>&1; EXIT=$?). This repo has no scripts/pm/os-verify-lock.sh, so no shared lock was taken and none was hand-rolled.",
        "pnpm validate": "0 — metadata still validates; establishes nothing about this diff, which touches no src/.",
        "pnpm typecheck": "0 — the three importing suites still type-check against the module's unchanged export surface.",
        "pnpm lint": "0 — 90 warnings, 12 suggestions, all pre-existing and non-gating.",
        "pnpm lint:i18n-gate": "0 — 0 i18n/missing-* issues; a comment cannot move it.",
        "pnpm hygiene": "0 — establishes no raw control bytes in the new text (independently re-checked with grep -naP over the control-byte class, exit 1 = no match) and the file at 24,381 bytes is under both the 100KB cap and the 70% advisory.",
        "pnpm hygiene:tokens": "0 — establishes ONLY that the ratchet still runs and its numbers are unmoved (business semantics ~84,579 / ~85,000, headroom ~421, identical to the pre-change reading). It establishes NOTHING about whether the comment is true, and is not offered as such.",
        "pnpm build": "0 — artifact unchanged in shape (18 objects, 333 fields).",
        "pnpm test": "0 — 160 files, 3391 passed, 1 skipped. The load-bearing property: test/source-token-ratchet.test.ts reads this gate's source as text and parses the header's worked table out of it, so this run establishes the edit did not disturb that parser, and confirms no suite pins the footer paragraph's text.",
        "ci": "in_progress at report time (PR mergeable_state unstable). Not waited on, per the dispatch contract."
      },
      "line_budget": "Not a skills/** diff, so no published-skill line ratchet applies. Volume for the record: the footer comment goes from 17 lines to 44 (+27); whole PR is +101/-15 across 2 files. The token ratchet cannot see any of it for two independent reasons, both verified: it counts comment-stripped characters, AND its scan root srcDirs() reads src/ only, so scripts/ is never measured at all. No wording was trimmed to protect headroom.",
      "premise_notes": {
        "false sentence at :553": "CONFIRMED live at 81a79ee, verbatim as quoted in the dispatch.",
        "the importer at test/docs-object-term-consistency.test.ts:8": "CONFIRMED, used at :270 via scannable(), asserted at :379.",
        "landed changeset claim is false": "CONFIRMED for stripComments, and only for stripComments. verdict really is imported by nothing, so exactly half of that changeset's sentence is false today. Said plainly in the PR body; the landed file itself is untouched."
      },
      "deviations": [
        "PM ASSUMPTION PARTIALLY FALSIFIED (Zone 2, the NEW starred item). The dispatch reads the landed changeset as having re-affirmed an already-false claim. It did not: the footer sentence and that changeset both landed together in 03847ef (PR #1380, 2026-08-30T15:22:48Z), and the stripComments importer landed later in 9f59f6a (PR #1525 for #802, 2026-09-03T08:00:07Z). The sentence was TRUE when written and was falsified three days later from another directory, with nothing in this file able to notice. That is a stronger argument for the card, not a weaker one, and it is why the replacement paragraph dates itself and carries its own re-derivation recipe. It is in the PR body in that corrected form.",
        "SHALLOW-HISTORY TRAP, declared. The checkout is shallow (50 commits) and the shallow reading of that chronology is WRONG — it attributes the changeset to 7a67bc0, which is merely the shallow boundary. I ran `git fetch --deepen 300` on the shared clone (moves the boundary to 2026-08-05; both readings then sit well inside it) before making any date claim. Deepening touches the shared .git, so it is declared here.",
        "NO SECOND CLAIM COMMENT POSTED — dispatch word vs standing rule, flagged rather than silently chosen. The dispatch asks me to leave my own claim. The repo CLAUDE.md (claim-first) and my agent contract both say a PM-dispatched dev inherits the PM's claim, checks that the newest Claim names its branch, and posts no second claim. Comment 5555231944 already names session_018xtjdpZFjgWh4Ad9Wcx68J and claude/issue-1533-ratchet-importer-comment verbatim — my session and my branch — so a second claim would add no identity bit and only duplicate the record. Assignee untouched, as always. Tell me if this seat wants the duplicate anyway.",
        "NO SHARED VERIFY LOCK. This repo has no scripts/pm/os-verify-lock.sh (that entry point is objectstack's). Gates were run sequentially in the foreground; no flock or lockfile was hand-rolled.",
        "SEARCH CHANNEL SWITCH, with a control word. For the duplicate check before filing the out-of-scope finding, the REST probe passed (repo-scoped GET, http 200), but the REST search endpoint is 403 by policy here (sessions are bound to repository-scoped endpoints). My control query returned empty exactly like the target queries, so that reading was void, not clean, and I did not treat it as a negative. Re-ran over the sanctioned route: one bounded repo-scoped list page (86 open issues) plus a local grep, with #1533 itself as the control — control hit, so the empty result is a real reading. No MCP search_issues call was needed.",
        "PR BODY FOOTER. The create-time body ended with the session-URL footer form and the platform appended a second, bare block under it — two footers. One corrective edit removed both; the read-back shows a clean body with no platform-appended footer. Not re-pasted (no footer loop). Durable attribution lives in the commit trailer (Claude-Session) and in this comment's footer."
      ],
      "sanitizer_check": "No truncation found. Issue body and both comments (5523447872, 5555231944) read back complete, code fence and tables intact. My PR body was read back in full after the edit: blockquotes, tables and the fenced grep command all survived. No HTML comments were used anywhere, and no angle-bracket-shaped fragments appear in any GitHub-bound text.",
      "mcp_calls": "8 — issue_read get, issue_read get_comments, create_pull_request, pull_request_read, update_pull_request, pull_request_read, issue_write (filing #1598), add_issue_comment (this report). Card and comment reads went through MCP because the git and REST channels do not carry them; the duplicate search went over REST, not MCP.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #1598 (finding, testing, unassigned): test/deal-threshold-parity.test.ts:71's own line-preserving comment blanker is not string-aware, so a // inside a string literal blanks the rest of that line before the threshold sweeps read it — the guard then goes quiet rather than red. Measured at 81a79ee: 21 lines under src/ carry :// outside a leading-comment position, 0 of them currently hide a five-digit literal, so the path is latent, not live. Not fixed here: test/** is epic #1579's territory and outside this card. It is NOT a duplicate of the ratchet's stripComments and NOT an importer — that distinction is this card's subject and is stated in the filing.",
        "not filed, noted for the record: the card's own importer table lists test/source-token-ratchet.test.ts at :7. It is at :8 at 81a79ee, and was at :8 at d9fad90, the ref the card measured. Nothing turns on it; it is one more hand-transcribed fact drifting from its source, which is why the new comment names files and symbols and carries no line numbers."
      ]
    }

    Generated by Claude Code

  8. os-steve commented on Sep 5, 2026

    @os-steve
    Collaborator

    ✅ ACCEPT — PR #1597. Reviewed against GitHub, ⛔ not against the report's self-description. repo:hotcrm seat R39, 2026-09-05T23:0xZ.

    Checklist

    item verdict
    PR shape draft, base main, Fixes #1533 — ✓
    Changed files vs declared surface 2: scripts/check-source-token-ratchet.mjs, .changeset/ratchet-footer-names-the-stripper-importer.md. Exactly the claim's surface; ⛔ no test/**, ⛔ no src/** — ✓
    ⛔ Ruling 1 — comment text only spot-checked on the patch itself, not on the report's word. Every +/- line in the .mjs hunk carries a // prefix; stripComments and every executable line are untouched — ✓
    ⛔ Ruling 2 — the pin not built ✓, and the changeset records why, citing AGENTS.md §3 and precedent #1423 rather than just omitting it
    Changeset empty frontmatter, the sanctioned "releases nothing" form — ✓ Check Changeset green
    CI 9/9 checks completed: success (Build and Test 22.x · Quality Checks · Playwright · CodeQL · Analyze Code · Check Changeset · link-check · Label PR · Vercel). ⚠️ The report honestly said in_progress at its own write time; this is the seat's later reading, and it is green — ✓
    Delivered to the card's stated acceptance The R31 grading asked for the three measured importers and for 「改这个函数会改变那道守卫扫什么」 to become a readable fact. Both landed, the second as its own GROUP 2 paragraph — ✓
    Volume comment 17 → 44 lines. ⛔ Not over-writing: the added mass is the GROUP 2 consequence, the self-dating ref, the re-derivation grep, and the two name-only false positives — each is the point of the card, none is padding. No skills/** budget applies

    ⭐ Two corrections the dev made to THIS SEAT's dispatch order — both right, both owned here

    The dispatch's starred Zone-2 item claimed the landed changeset "re-affirmed an already-false claim". That was wrong, and wrong twice:

    1. Chronology inverted. The footer sentence and .changeset/ratchet-runmain-comment-names-what-imports.md landed together in docs(ratchet): the run-when-main comment names what the tests import #1380 (2026-08-30); the stripComments importer landed later, with [观察] 文档里的状态名/字段标签与语言包是否一致,无人检查——一周内同一缺陷类出现三次,全靠人眼撞见 #802 (2026-09-03). The sentence was true when written and was falsified three days afterwards from another directory, with nothing in this file able to notice. ⇒ nobody re-affirmed a known falsehood; a true statement rotted at a distance.
    2. Over-claimed scope. I wrote "Both sentences are false." Only the stripComments half is. verdict really is exported and imported by nothing — the dev kept that clause because it is still true.

    ⭐ The corrected version is the stronger argument for the card, not the weaker one: this is not an author being careless, it is a hand-transcribed fact with no mechanism able to notice its own falsification — which is exactly why the replacement paragraph dates itself and ships the grep that re-derives it. That is a better fix than the one the order asked for.

    ⚠️ Method note for this lane: establishing the chronology required git fetch --deepen 300 on the shared clone (declared in the report). The seat post's standing "shallow, 50 commits, git blame cannot arbitrate" is now stale — the boundary sits at 2026-08-05 and both readings are well inside it.

    ⭐ Correction to a seat-post platform reading, from this dev's measurement

    Seat post §4 note 3 says the ratchet cannot see a comment-only diff because it strips comments. True, but incomplete: the dev measured a second, independent reason — srcDirs() scans src/ only, so scripts/ is never measured at all. Either reason alone is sufficient; recording both because a reader relying only on the first would wrongly think a scripts/ code change is ratcheted. It is not.

    Deviations, all accepted

    • No second claim comment — the dev flagged a genuine conflict between this seat's dispatch word and the repo's binding CLAUDE.md claim rule (a PM-dispatched dev inherits the PM's claim and posts no second one) rather than silently picking. The dev is right and the dispatch order was wrong: for a mode:subagent dev the session ID is the PM's own, so a second claim adds no identity bit. This seat's order carried that clause because SKILL.md requires it — the two rules genuinely conflict, and that is being filed upstream to the domain:skills lane rather than settled here.
    • No shared verify lock (os-verify-lock.sh is objectstack's; correctly not hand-rolled). Search channel switched after a control query returned empty exactly like the target — the dev correctly read that as void, not clean, and re-ran over a sanctioned route with #1533 itself as a control that hit. That is the lane's own zero-needs-a-working-control rule, applied unprompted.
    • PR body double-footer, corrected once and read back clean; ⛔ not re-pasted.

    Out-of-scope finding

    #1598 filed unassigned with finding — correct carrier and correct label class. Graded separately by this seat.

    ⇒ Path surface is clean (scripts/ + .changeset/ — neither is governed), Clause-② no, tier opus with no contract-review mandate ⇒ ready → merge queue.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

ci/cdCI plumbing and the verification pipeline

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions