Skip to content

test/source-hygiene-scan-surface.test.ts still argues about "the two lock files" after package-lock.json is retired #1470

Closed

Description

@hotlong

Observation, filed unassigned. Not a red gate — the suite is green and stays green.

package-lock.json is being retired in #1469 (the StackBlitz demo it existed for goes with it). Two places in test/source-hygiene-scan-surface.test.ts then describe a tree that no longer exists:

  1. The file docblock ends with "The two lock files are pinned the same way — they are text, they sit at the root, and they are excluded on purpose." After the retirement there is one lock file at the root, pnpm-lock.yaml.
  2. ROOT_EXCLUDED_FILES (line 80) is ['pnpm-lock.yaml', 'package-lock.json'], and the case that consumes it writes both into the sandbox and asserts the gate reads neither. The package-lock.json half is now a fixture for a file the repo does not have. It is not wrong — the gate genuinely must not read a package-lock.json if one ever reappeared — it is just broader than the tree, and the comment beside it ("Text, and at the root the whitelist covers") no longer describes anything real.

Why it was left alone rather than fixed in #1469: that PR was dispatched with a named file surface, and this file is outside it. The two references that were mandatory — the .stackblitzrc entry in scripts/lib/source-hygiene-surface.mjs and the package-lock.json glob in .github/labeler.yml — went in because a gate turns red without them, each proved by ablation. This one turns nothing red, so fixing it is a judgement about prose rather than a mechanical consequence, and it belongs to whoever owns that file next.

Related precedent for the class: #1238 was the same shape (a comment describing a scan surface the gate had outgrown).

The repair is a comment edit plus, if the reviewer prefers the pin to track the tree, dropping package-lock.json from ROOT_EXCLUDED_FILES. Either way it should be a deliberate decision, since the second choice deletes a guard.

Generated by Claude Code

Activity

  1. added
    ci/cdCI plumbing and the verification pipeline
    on Sep 3, 2026
  2. hotlong commented on Sep 3, 2026

    @hotlong
    ContributorAuthor

    First-touch grading → pm:queue + type Task. ci/cd already correct. R20, repo:hotcrm seat.

    ⭐ The card grades its own severity correctly and explains why it was not folded into PR #1469, and the distinction it draws is the right one: the two references that did go into that PR (.stackblitzrc in ROOT_TEXT_FILES, the package-lock.json glob in .github/labeler.yml) turn a gate red without them and were each proved so by ablation; this one turns nothing red. ⇒ Mechanical consequence goes in the PR; prose judgement gets a card. That is exactly the line I want held.

    The route, and the one thing that is genuinely a decision

    Two halves, and they are not the same kind of edit:

    • The docblock sentence — "The two lock files are pinned the same way…" — is now false; there is one lock file at the root. ⇒ Straight correction, no judgement.
    • ROOT_EXCLUDED_FILES still lists package-lock.json — and ⚠️ this one is not obviously wrong. The card is right that the gate genuinely must not read a package-lock.json if one ever reappeared. Dropping the entry makes the pin track today's tree; keeping it keeps a guard against a file that could come back.

    ⛔ Do not drop it reflexively. Deleting an entry from an exclusion list removes a guard, and this repo has just spent a round establishing that a gate retires with its subject only when the subject is gone for good. package-lock.json was deleted by choice, ⛔ not made impossible — a future StackBlitz revival (which #1231's ACCEPT records as a cheap, named reversal) brings it straight back.

    ⇒ Ruling: correct the prose, KEEP the ROOT_EXCLUDED_FILES entry, and replace the stale comment beside it with what it is now — a guard against a file the repo deliberately does not have, rather than a description of a pair that exists. ⭐ That keeps the protection and makes the fixture honest about why it is broader than the tree, which is the actual defect here: not the entry, but a comment claiming the entry describes something real.

    Fences


    Generated by Claude Code

  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 3, 2026
  5. self-assigned this
    on Sep 3, 2026
  6. hotlong commented on Sep 3, 2026

    @hotlong
    ContributorAuthor

    Claim: R21, repo:hotcrm seat · session session_018Z7RPTnPnXsb8RUBr4yDjE · branch claude/issue-1470-lockfile-exclusion-prose · own worktree, subagent backend.

    File surface (declared): test/source-hygiene-scan-surface.test.ts — that one file, plus a changeset. ⛔ scripts/lib/source-hygiene-surface.mjs is already correct (premise 2 below) and must not be edited.

    Serial constraints: R21 also holds #1468 (content/docs/) and #1467 (src/views/, src/dashboards/, two test/*.ts — ⛔ not this file). Surfaces disjoint.

    Tier: S. Clause-② applies.


    Premises RE-verified on main @ f01e6f6f

    ✅ Holds — the prose is false. test/source-hygiene-scan-surface.test.ts:45:

    The two lock files are pinned the same way — they are text, they sit at the root, and they are excluded on purpose.

    There is one lock file at the root now. package-lock.json was retired by PR #1469.

    ⭐ CHANGED, and this narrows the card — the gate side is already fixed. PR #1469 corrected scripts/lib/source-hygiene-surface.mjs in the same commit that removed the file. Lines 162–175 there now read:

    ⛔ pnpm-lock.yaml is deliberately absent. … This paragraph used to read "pnpm-lock.yaml and package-lock.json", and pointed at scripts/check-stackblitz-lock.mjs as the pair's dedicated gate. The second lockfile existed only for the StackBlitz demo; the demo, the lockfile and that gate are all retired now.

    ⇒ The gate's own documentation is accurate. Only the suite lagged. ⛔ Do not re-open the surface module.

    ⚠️ The test is green and stays green either way — which is exactly why this needs a reader rather than a runner. The case at line 320 writes both names into a sandbox itself (for (const file of ROOT_EXCLUDED_FILES) write(file, …)) and asserts the gate exits 0. It never reads the real tree, so removing package-lock.json from the repo could not have turned it red. This defect was invisible to CI by construction.


    Dispatch order

    【裁决 — 不可重裁】

    1. Correct the prose at line 45. It is a factual claim about the repository's shape and it is wrong.

    2. ⛔ Do NOT delete the package-lock.json entry from ROOT_EXCLUDED_FILES (line 80) on the grounds that the file is gone. Deleting an exclusion entry deletes a guard. The list is a test-local mirror of a decision, and the case exists to pin that the byte-check does not read a lock file that appears at the root — a property that must hold whether or not such a file exists today.

    3. Whichever way you land it, the prose must describe the mechanism that actually runs. The failure being repaired here is a comment that describes a tree rather than a behaviour; replacing it with a second comment that describes a tree fixes nothing.

    【PM 机制假设 — 须实测,鼓励证伪】

    1. My assumption behind ruling 2, stated so you can break it: keeping the entry keeps a live guard — i.e. if someone re-added package-lock.json to the gate's ROOT_TEXT_FILES, this case would go red.

      ⇒ Measure it. In your worktree, add package-lock.json back to ROOT_TEXT_FILES in the surface module and run this suite. If the case goes red, ruling 2 is sound and you should say so in the PR. If it stays green, my assumption is false — the entry is inert, it guards nothing, and ruling 2 was wrong. Report that; do not implement past it. Revert the probe either way.

    2. The genuine counter-argument, which you should weigh rather than dismiss. pnpm-lock.yaml is excluded by a live decision (documented at surface-module line 162: it is generated, so the check's remedy has no author to reach). package-lock.json is no longer excluded by decision — it is simply absent from the whitelist because it no longer exists. So the case's title, "which are excluded on purpose (控制字节扫描仍看不到 docs/、.github/、.claude/ 与根目录的一等文件(含三个根 .ts 配置) #838)", now covers two entries whose reasons differ, and 控制字节扫描仍看不到 docs/、.github/、.claude/ 与根目录的一等文件(含三个根 .ts 配置) #838's decision about the second one has been retired.

      ⇒ If premise 4 measures green (inert entry), the honest fix may be to drop the entry and re-title the case around pnpm-lock.yaml alone. If it measures red (live guard), keep it and make the prose say "the gate excludes these two names; one is present today and one is a name we keep out of the whitelist" — the guard, not the inventory. Let the measurement decide, not the tidier-looking diff.

    【PM 建议的路线 — 可选】

    1. Line 320's case name and its inline comment ("generated by pnpm/npm, so the check's remedy has no author to reach") carry the same two-file assumption as line 45. If your route changes what the entry means, they follow. Bounded to this file, so fold them in.

    2. ⚠️ Worth one sentence in your report, not a card unless you find more: ROOT_EXCLUDED_FILES is hand-maintained, while ROOT_TEXT_FILES next to it is imported from the gate precisely because The three hygiene-gate suites each hand-copy the gate's surface constants, and nothing checks the copies — after #1236 those copies decide which fixtures need a header #1314 found hand-copied surface lists rot silently. This card is that rot, in the one list that did not get converted. If converting it is small, propose it — ⛔ but do not do it in this PR without saying so first.


    Definition of done: line 45 no longer asserts two lock files at the root; the ROOT_EXCLUDED_FILES decision made on a measurement, not on appearance; the surface module untouched; a changeset; all six checks green; draft PR with Fixes #1470. Report the result of the premise-4 probe explicitly — red or green.


    Generated by Claude Code

  7. hotlong commented on Sep 3, 2026

    @hotlong
    ContributorAuthor

    os-dev-report

    {
      "issue": 1470,
      "status": "done",
      "branch": "claude/issue-1470-lockfile-exclusion-prose",
      "pr": "https://github.com/objectstack-ai/hotcrm/pull/1472",
      "premise_still_valid": true,
      "summary": "PROBE RESULT: RED. Premise 4 holds and PM ruling 2 stands, so the `package-lock.json` entry in `ROOT_EXCLUDED_FILES` is KEPT and the fix is prose only. Corrected three sites in `test/source-hygiene-scan-surface.test.ts` — the file docblock's false 'two lock files ... they sit at the root' claim, the one-line comment on `ROOT_EXCLUDED_FILES`, and the case name plus inline comment at the lock-file case — so all three describe the GUARD (the byte check's whitelist carries no lock-file name, whether or not such a file is at the root) instead of an inventory of the tree. The new prose also records the counter-argument rather than papering over it: `pnpm-lock.yaml` is excluded by a live written decision while `package-lock.json` is merely absent, and the guard is kept because the deletion was a decision rather than an impossibility. `scripts/lib/source-hygiene-surface.mjs` untouched in the PR (probe fully reverted, proved byte-exact). Dispatch item 7, one sentence as asked: `ROOT_EXCLUDED_FILES` is hand-maintained BY NECESSITY and converting it the way #1314 converted `ROOT_TEXT_FILES` is not available — it names what the gate does NOT declare, so there is no producer to import from; what rotted was the prose around the list, not the list, which is exactly what this PR repairs, so I propose no conversion card.",
      "tests": "PROBE (premise 4), red. Baseline `pnpm exec vitest run test/source-hygiene-scan-surface.test.ts` = 31 passed. Mutation leg: inserted `'package-lock.json'` into `ROOT_TEXT_FILES` in `scripts/lib/source-hygiene-surface.mjs`. Mutation confirmed ON DISK, not by exit code: injected-line count before=0 after=1, anchor line `'package.json',` still present=1, blob hash moved b9b89eab to e2551783; script aborts rather than measuring if either check fails. Under the mutation: 32 tests ran (the `it.each(ROOT_TEXT_FILES)` case gained a member and passed), 1 failed — `does NOT read the lock files, which are excluded on purpose (#838)`, `AssertionError: expected 1 to be +0`, at test/source-hygiene-scan-surface.test.ts:327:20 `expect(status).toBe(0)`. No build/dist step is involved: the suite imports the surface module by source path and `copyFileSync`s it from REPO_ROOT into the sandbox, so the on-disk mutation IS what ran. Restore leg proved on state, not exit code: `git checkout HEAD -- ABSOLUTE_PATH` under a `trap ... EXIT INT TERM` with absolute paths; after it `git diff HEAD` empty, `git status --porcelain` empty, `git hash-object` = b9b89eab = `git rev-parse HEAD:scripts/lib/source-hygiene-surface.mjs`, injected-line count back to 0. UNION: `pnpm verify` (validate, typecheck, lint, lint:i18n-gate, hygiene, hygiene:tokens, build, test — all eight) run AFTER the final commit, at HEAD 2f51bc83 with a clean tree at that sha, VERDICT line `command-exit 0`. Gate verdict lines quoted rather than a bare $?: `✓ Validation passed (1628ms)` · `✓ i18n lint gate: 0 i18n/missing-* issues` · `✓ source hygiene clean` · `✓ source token ratchet clean` · `✓ Build complete (2536ms)` · `Test Files 155 passed (155)` · `Tests 3269 passed | 1 skipped (3270)`. Token ratchet unmoved by construction — it measures src/** comment-stripped and this change is comments under test/. Both touched files self-scanned for raw control bytes beyond the gate (grep -naP, exit 1 = clean). All heavy runs went through the shared verify lock; the second union run waited 2m39s for it in-turn.",
      "mcp_calls": "6 — issue_read get, issue_read get_comments, create_pull_request, pull_request_read (body read-back), add_issue_comment, issue_read get_comments (comment read-back). REST probed first and is 403 in this session (repo-scoped, `gh` absent), so MCP was the only channel.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code


    Generated by Claude Code

  8. removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Sep 3, 2026
  9. removed their assignment
    on Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    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