Skip to content

chore(lint): make lint:pkg gate ANSI-invariant + declare sideEffects:false — closes queue #63 + #70 (supersedes #88)#101

Open
Goosterhof wants to merge 2 commits into
mainfrom
armorer/queue-63-70-lintpkg-honest-gate
Open

chore(lint): make lint:pkg gate ANSI-invariant + declare sideEffects:false — closes queue #63 + #70 (supersedes #88)#101
Goosterhof wants to merge 2 commits into
mainfrom
armorer/queue-63-70-lintpkg-honest-gate

Conversation

@Goosterhof
Copy link
Copy Markdown
Contributor

What & why

Two coupled fixes that make Gate 6 (lint:pkg) actually enforce in CI and clear the only standing publint Suggestion.

Verified ground truth (the polarity matters)

The lint:pkg wrapper (scripts/lint-pkg.mjs) runs publint run per package with piped stdout, captures it, matches PUBLINT_BLOCK_RE = /^(Suggestions|Warnings|Errors):$/m, and fails the gate on a match.

  • In CI publint detects a color-capable environment and emits ANSI-wrapped headers, e.g. \x1b[1m\x1b[34mSuggestions:\x1b[39m\x1b[22m. The leading SGR codes mean the line is not a bare Suggestions:, so the regex never matched → no failure pushed → the gate silently no-op'd. Verified against raw CI logs: this has been a no-op in CI since publint 0.3.21 landed 2026-05-11 — the 2026-05-18 and 2026-05-29 main runs both passed lint:pkg while publint printed the Suggestions block.
  • Locally (plain-text, non-TTY) publint emits no color → the regex matched → the gate failed correctly. That local "failure" was the gate working, not a false positive.

So the real defect is a CI false-NEGATIVE: a genuine publint Warning/Error (e.g. a broken exports map) would sail through CI undetected today. This PR does not "restore Gate 6 to green on main" — Gate 6 was never red in CI. It makes Gate 6 actually enforce in CI and clears the standing sideEffects Suggestion.

(For the record: PRs #86/#87 were red on the npm audit step, not Gate 6.)

Changes

  1. scripts/lint-pkg.mjs — ANSI invariance (closes queue fs-router 0.1.0 published peer-dep is stale (vue-router ^4.5.0 vs source ^5.0.6) — republish as 0.1.1 #63). Spawn publint with NO_COLOR=1 and strip residual ANSI SGR codes from captured stdout before the regex match (belt-and-suspenders, verdict identical in every color environment). ANSI_RE is built via String.fromCharCode(27) + RegExp() to avoid a control-char literal that oxlint's no-control-regex (Correctness) would reject. Header comment rewritten to record the verified CI-color root cause.
  2. 11 × packages/*/package.json"sideEffects": false (closes queue fs-dialog: forward host <dialog> ARIA attributes via dialog.open() options (#67) #70). Added immediately after "type": "module". Clears the publint 0.3.21 Suggestion the now-honest gate flags as fatal; lets consumers tree-shake under deep imports.
  3. CLAUDE.md — "No top-level side effects" Conventions bullet locking the convention so a future package author doesn't ship without the flag and re-fire Gate 6.

Load-bearing proof the no-op is fixed

Run Pre-fix behaviour Post-fix (this PR)
npm run lint:pkg (plain), all packages have sideEffects PASS PASS (exit 0)
FORCE_COLOR=1 npm run lint:pkg, all packages have sideEffects PASS PASS (exit 0)
npm run lint:pkg (plain), one package's sideEffects removed FAIL (regex saw plain Suggestions:) FAIL (exit 1)
FORCE_COLOR=1 npm run lint:pkg, one package's sideEffects removed false-PASS (ANSI defeated the regex) FAIL (exit 1)

The bottom-right cell is the whole point: pre-fix, the colored run let the Suggestion through; post-fix it fails. Captured directly — FORCE_COLOR=1 npx publint run emits \033[1m\033[34mSuggestions:\033[39m\033[22m (verified via od -c), exactly the ANSI wrap that defeated the bare-line regex.

Per-package side-effect audit (re-run on current main, post-#87 streamRequest removal)

Every package's src/index.ts is a pure re-export or a file whose top-level statements are imports, type declarations, and const/function factory declarations. All flagged console.* / document.* / window.* / Object.defineProperty / dialog body-scroll writes are inside function bodies (call-time, not module-evaluation). No bare side-effect imports anywhere.

Package Top-level verdict
fs-adapter-store pure re-exports + types
fs-cached-adapter-store re-exports; top-level new WeakMap() is pure construction
fs-dialog inline factory; document.body write is call-time inside open()
fs-helpers pure re-exports
fs-http imports + DEFAULT_TIMEOUT_MS literal + factory consts (streamRequest gone in 0.4.0)
fs-loading pure re-exports
fs-router pure re-exports + page-name consts
fs-storage pure re-exports; console.error is call-time inside service methods
fs-theme pure re-exports; document/window reads are call-time
fs-toast inline factory; Popover calls are call-time inside defineComponent setup
fs-translation inline factory + getCacheKey helper; all pure declarations

Verification — full 8-gate gauntlet, local

Gate Result
npm audit PASS (0 vulnerabilities)
npm run format:check PASS (145 files)
npm run lint PASS (0 warnings / 0 errors, 92 files)
npm run build PASS (11 packages, dual ESM+CJS)
npm run typecheck PASS
npm run lint:pkg PASS — and now color-invariant
npm run test:coverage PASS (520/520 tests, 100% per-package)
npm run test:mutation PASS (all 11 packages ≥ 90%: 100/94.81/97.18/100/96.23/92.50/91.20/92.73/96.67/98.36/93.33)

Supersedes #88

Folds PR #88's sideEffects: false manifest changes + CLAUDE.md doctrine bullet into this PR, and adds the queue #63 ANSI fix that #88 was missing. #88 can be closed as superseded.

Closes enforcement queue #63 + #70.

Goosterhof and others added 2 commits May 29, 2026 12:20
…I — queue #63

The lint:pkg wrapper captures publint stdout and matches PUBLINT_BLOCK_RE
(/^(Suggestions|Warnings|Errors):$/m) to fail the gate on any advisory. In CI,
publint colorizes its block headers (color-capable environment), emitting e.g.
"\x1b[1m\x1b[34mSuggestions:\x1b[39m\x1b[22m". The leading SGR codes mean the
line is not a bare "Suggestions:", so the regex never matched and the gate
silently no-op'd — a CI false-NEGATIVE in which a genuine Warning/Error block
would sail through undetected. Verified against raw CI logs: the gate had been
a no-op in CI since publint 0.3.21 landed 2026-05-11; locally (plain-text,
non-TTY) the regex matched and the gate fired correctly.

Fix (belt-and-suspenders): spawn publint with NO_COLOR=1 AND strip residual
ANSI SGR codes from captured stdout before the regex match, so the verdict is
identical in every color environment (plain, TTY, FORCE_COLOR=1). ANSI_RE is
built via String.fromCharCode(27) + RegExp() to avoid a control-char literal
that oxlint's no-control-regex (Correctness) would reject. Header comment
updated to record the verified CI-color root cause.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>


Adds "sideEffects": false immediately after "type": "module" in every
packages/*/package.json. Clears the publint 0.3.21 Suggestion ("package appears
to be consumed by bundlers but does not specify sideEffects") that the
now-honest Gate 6 (queue #63 fix) flags as fatal, and lets consumers tree-shake
under deep imports.

Per-package side-effect audit re-run on current main (post-#87 streamRequest
removal from fs-http): every package's src/index.ts is a pure re-export or a
file whose top-level statements are imports, type declarations, and
const/function factory declarations. console.*/document.*/window.*/
Object.defineProperty and the dialog body-scroll write are all inside function
bodies (call-time, not module-evaluation). cached-adapter-store's top-level
`new WeakMap()` is pure construction. No bare side-effect imports anywhere. All
11 cleared.

CLAUDE.md gains a "No top-level side effects" Conventions bullet locking the
convention so a future package author doesn't ship without the flag and re-fire
Gate 6. Closes enforcement queue #70.

Supersedes PR #88.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying fs-packages with  Cloudflare Pages  Cloudflare Pages

Latest commit: b5de5ea
Status: ✅  Deploy successful!
Preview URL: https://ab8b72de.fs-packages.pages.dev
Branch Preview URL: https://armorer-queue-63-70-lintpkg.fs-packages.pages.dev

View logs

@jasperboerhof
Copy link
Copy Markdown
Contributor

PR Reviewer · claimed

@Goosterhof Goosterhof added the Agent Review Requested Requesting review of specialized AI review agents. label May 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Agent Review Requested Requesting review of specialized AI review agents.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants