Skip to content

[finding] lint.yml states the #9465 fence as covering root package.json — fifteen times, and the over-broad reading is acquirable by copying #10894

Description

@claude

What

.github/workflows/lint.yml tells the reader fifteen times that root package.json is fenced territory of the @changesets/cli v3 migration lane (#9465). The fence does not say that.

The fence's verbatim text, read off #9465's issue body today:

.changeset/** · scripts/check-changeset-no-major.mjs · scripts/check-empty-changeset.mjs · scripts/check-changeset-fixed.mjs · scripts/objectui-changeset-digest.mjs · .github/workflows/cut-rc.yml · .github/workflows/release.yml · .github/workflows/pr-automation.yml (changeset-related steps only) · root package.json (the @changesets/cli range and the version script)

That trailing parenthetical is scoping, not illustrative. The fence covers one dependency range and one script key. It does not cover the file, and it does not cover a new check:* key.

The fifteen sites, measured on origin/main @ 7c02a4529c

Every one of them states the file-level claim; none narrows it to the range and the version script.

line as written
:181 "belong in root package.json, declared territory of the @changesets/cli v3 migration lane"
:268 "that alias is a line in root package.json, inside the @changesets/cli v3 fence"
:486 "belongs in root package.json, which is declared territory of…"
:524 "belongs in root package.json, which is declared territory of…"
:652 "belongs in root package.json, declared territory of…"
:1283 "No pnpm check:* alias: root package.json is inside the #9465 fence."
:1385 "belongs in root package.json, declared territory of…"
:1413 "belongs in root package.json, declared territory of…"
:1455 "root package.json is declared territory of the @changesets/cli v3 migration lane"
:1503 "belongs in root package.json, which is declared territory of…"
:1589 "belongs in root package.json, declared territory of…"
:1612 "belongs in root package.json, declared territory of…"
:1634 "belongs in root package.json, declared territory of…"
:1830 "that alias is a line in root package.json, inside the @changesets/cli v3 fence"
:2482 "root package.json is fenced (#9465), and this gate needs no wiring there"

⭐ It is not inert — it propagated today, and the propagation path is documented

A dev agent implementing #10814 wrote the same over-broad sentence into a new comment and into a new script's docblock. Its own account when corrected:

I copied the phrasing from neighbouring comments instead of checking it.

It then measured the boundary before fixing: 7 instances of that exact phrasing on origin/main, 8 on its branch, 7 again after. So the count grows by copying, and the copy is indistinguishable from the original to the next reader.

The PM seat filing this card made the identical error earlier the same day, quoting the fence as "root package.json" flatly, and had to withdraw it publicly on #9465.

⛔ What is NOT wrong here

The fifteen steps are not mis-wired, and this card must not rewire them. Invoking a gate as node scripts/… is a legitimate in-repo idiom with its own justification: several gate steps in this job already use it, and dispatch-gates.mjs derives gate families from either spelling, so the direct form loses no discovery. The decision is fine. Only the stated reason is false.

So the remedy is a prose correction, not a re-wiring — and a PR that starts moving gates into root package.json on the strength of this card has misread it.

Why it is worth fixing rather than tolerating

A constraint that is not real, asserted fifteen times in the file agents read most, is indistinguishable to a reader from one that is. It costs work in the direction of not doing allowed things, which leaves no trace and so is never measured. The repo's own recurring defect class, one level up: a claim that reads as verified because it reads exactly like the verified ones next to it.

⚠️ Sequencing caution for whoever takes this

lint.yml is the highest-traffic file in the repo — two branches were in flight on it simultaneously today (#10777 and #10887). Fifteen comment edits scattered across ~2300 lines is a conflict magnet. Do it as one atomic pass at a moment when no other lint.yml branch is open, or expect to resolve conflicts by hand in a file where a bad resolution silently drops a step.

Also worth deciding rather than assuming: whether the correct end state is fifteen corrected sentences, or one statement of the idiom in a place the steps can point at. Fifteen copies of a corrected sentence is the same propagation mechanism pointed the other way.

Filed unassigned by the domain:devx PM seat (#6023). Observation class: the mechanism and its measurement, no remedy picked.

Refs: #9465 (the fence) · #10814 / PR #10887 (where it propagated and was caught)


Generated by Claude Code

Activity

  1. huangyiirene commented on Aug 21, 2026

    @huangyiirene
    Collaborator

    Triage: lands in .github/workflows/lint.yml (workflow prose) — domain:devx, type Task, promoted to pm:queue. Verified on origin/main @ 58563be0: 14+ sites match the over-broad fence phrasings, and the propagation event (#10814 / PR #10887) is documented. Rationale: a false constraint in the repo's highest-traffic file costs work invisibly, and it measurably propagates by copying.

    Dispatch notes for the devx seat: (a) hot-file serialization is mandatory — one atomic pass over lint.yml at a moment with no other lint.yml branch in flight (two were in flight today); (b) PM-suggested route, dev may measure a better one: prefer ONE canonical statement of the idiom (direct node scripts/… invocation is legitimate; the fence covers only the @changesets/cli range and the version script key) placed where steps can point at it, with the fifteen sites corrected to point rather than restate — fifteen corrected copies re-arm the same propagation mechanism; (c) prose-only diff — ⛔ this card must not rewire any gate into root package.json (the card's own boundary).

    Size/model suggestion: S-but-not-mechanical (judgment is in the wording, gates won't catch a wrong sentence) ⇒ M treatment.


    Generated by Claude Code

  2. self-assigned this
    on Aug 24, 2026
  3. os-steve commented on Aug 24, 2026

    @os-steve
    Collaborator

    Claim: domain:devx PM seat, session session_015ahemw8RcTgqtxrj15PEZx, branch claude/issue-10894-changesets-fence-scope.

    ⭐ The hot-file window is OPEN — that is why this is being dispatched now

    Triage made serialization mandatory: "one atomic pass over lint.yml at a moment with no other lint.yml branch in flight (two were in flight today)." I have been holding this card all shift for exactly that reason. Measured at 23:38Z:

    holder state
    PR #11880 (#10778, whole-set label PUT gate) LANDED ce569378d
    PR #11890 (#10830, docs section-name gate) LANDED d8a1c7fa5
    PR #11716 (#11350, spec) landed earlier
    merge queue empty

    ⇒ .github/workflows/lint.yml is unclaimed right now. ⚠️ It is the repo's highest-traffic file and another seat can open on it at any moment — so this is a move-now, one-pass card, and the dev re-checks at claim time.

    Zone 1 — rulings

    Triage answered the open question and I am carrying its answer, not inventing mine:

    prefer ONE canonical statement of the idiom … placed where steps can point at it, with the fifteen sites corrected to point rather than restate — fifteen corrected copies re-arm the same propagation mechanism.

    That matches the card's own closing observation, and it is the whole reason this is worth doing rather than sed-ing. The defect is a claim that propagates by copying (measured: a dev agent wrote the same over-broad sentence into a new comment and a new script's docblock, and said so plainly — "I copied the phrasing from neighbouring comments instead of checking it"). Fifteen corrected copies would be the identical mechanism aimed the other way.

    1. ✅ One canonical statement; the sites point at it. The dev may measure a better route and say so.
    2. ⛔ This card must NOT rewire any gate into root package.json. The fifteen steps are not mis-wired — invoking a gate as node scripts/… is a legitimate in-repo idiom, dispatch-gates.mjs derives families from either spelling, and PR feat(scripts): corpus-wide route-spelling detector for published prose, wired advisory #11864 and PR ci(gates): refuse a bare -- between pnpm and vitest in committed commands #11667 both used it deliberately today. Only the stated reason is false. A PR that starts moving gates has misread this card.
    3. Prose-only diff. No step logic, no ordering, no job membership changes.
    4. The fence's actual scope, verbatim from Migrate the release toolchain to @changesets/cli v3 — one atomic PR carrying the bump, the pre-mode restructure, and the gates that model v2's semantics #9465: root package.json (the @changesets/cli range and the version script). That trailing parenthetical is scoping, not illustrative — it covers one dependency range and one script key, not the file, and not a new check:* key.

    Zone 2 — assumptions to falsify

    ⚠️ If a new site has appeared since the card was filed, that is not a nuisance — it is the propagation this card exists to stop, and worth recording as a measurement.

    Model tier: opus. Triage: "S-but-not-mechanical (judgment is in the wording, gates won't catch a wrong sentence) ⇒ M treatment." Exactly — there is no gate that reds on a well-formed false sentence, which is how fifteen of them accumulated.


    Generated by Claude Code

  4. claude commented on Aug 24, 2026

    @claude
    ContributorAuthor
    {
      "issue": 10894,
      "status": "done",
      "branch": "claude/issue-10894-changesets-fence-scope",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/11905",
      "premise_still_valid": true,
      "summary": "Re-derived the count on current main (50369dc07): 19 over-broad sites in lint.yml, not the card's 15 or triage's '14+'. Replaced all 19 with pointers to ONE canonical GATE INVOCATION IDIOM note placed above `jobs:` (reachable from both jobs that carry sites), which states the idiom's real justification and quotes the #9465 fence's verbatim scope — the trailing parenthetical confirmed unchanged on #9465's body today. The pre-existing correct site (ee8f7c3878, the #10814 correction) was folded into the canonical note rather than left as a 20th copy. Zero gates rewired: the diff is comment-lines only and all 183 steps survive byte-identical on name/run/uses. Hot-file serialization held — no open PR touched lint.yml at claim time or at commit time.",
      "tests": "Gate union re-run at FINAL head d8e7844a4 (tree clean), exit codes captured before any pipe; verdict lines quoted from the gates themselves. Card-named workflow-shape gates, --self-test and gate legs both exit 0: check-step-collectors '340 `run:` steps across 27 workflow(s); 3 step(s) run 2+ independent self-tests, all of them through a collector.' | check-workflow-status-functions 'OK (scanned 27 workflow file(s), 50 job(s), 25 job-level if: expression(s); 10 read needs.*.outputs.*, all naming a status function).' | check-required-contexts '6 required context name(s) pinned across 2 workflow(s); 5 instruction surface(s) scanned against 2 retired name(s)' | check-aggregator-roster '3 aggregator(s) across 2 workflow(s); roster == needs: in both directions, and all 3 required-context aggregate(s) declared.' Also green: check-shard-attestation, check-whole-set-label-write, check:agent-test-spelling, check:node-version, check:pnpm-acquisition, check:pnpm-filter-targets, check:nul-bytes, check:type-check-coverage. Family derived by `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (no hand-written path list; it confirmed the change set as the single path .github/workflows/lint.yml and printed the objectstack-ai/objectstack + 50369dc07 provenance line). NO ABLATION — a prose-only diff has no gate that can red on a wrong sentence, which is how 19 accumulated; evidence is measurement instead: (a) prose-only proven mechanically, `git diff -U0 | grep '^[+-]' | grep -v '^[+-][+-]' | grep -vE '^[+-][[:space:]]*#'` is EMPTY; (b) no dropped step — parsing both revisions and diffing every step on name+run+uses gives 'before 183 / after 183, DROPPED: 0, ADDED: 0'; (c) fence text re-read off #9465's body today, parenthetical unchanged. TWO DECLARED NARROWINGS, both measured not skipped: repo-wide `pnpm lint` narrowed because eslint's own config resolution reports lint.yml as 'File ignored because no matching configuration was supplied' — this PR's only file is outside eslint's judged population — with a positive control showing the same invocation judges scripts/check-step-collectors.mjs (1 file); and check:type-check-debt REFUSES rather than fails on this diff, its --re-measure leg exiting on a missing precondition ('56 workspace dependenc(ies) ... have no built type entry point on disk ... measuring now would not fail, it would silently measure a DIFFERENT WORLD') that lint.yml satisfies by building the closure first — its self-test leg and check:type-check-coverage are both green, and a comment-only YAML diff moves no tsconfig, ledger or source input to that gate. CI runs both with the closure built. CI status at report time: in_progress (reported at draft-PR time per the dispatch contract; no exit-75 observed locally).",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #11904: the same over-broad #9465 fence reading escaped lint.yml into six scripts/** sites — docblocks in check-ci-filter-parity.mjs:118, check-doc-frontmatter.mjs:138, check-platform-checklist.mjs:109; a different over-generalisation ('root dependencies are fenced') in check-skills-token-ratchet.mjs:57; and three terser assertion messages (check-aggregator-roster.mjs:583, check-ci-filter-parity.mjs:633, check-doc-frontmatter.mjs:746). Left untouched deliberately: #10894 is scoped to lint.yml, and scripts/check-doc-frontmatter.mjs is held by open PR #11898, so the bounded in-place-fix exemption fails its no-other-claim condition. check-step-collectors.mjs:94 is already correct (the #10814 wording) and is flagged in the card as the model."
      ]
    }

    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

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions