Skip to content

chore(deadcode): fix dead-code ratchet — remove unused devDeps + rebaseline knip (#607) - #673

Open
scottschreckengaust wants to merge 2 commits into
mainfrom
fix/issue-607-deadcode-ratchet
Open

chore(deadcode): fix dead-code ratchet — remove unused devDeps + rebaseline knip (#607)#673
scottschreckengaust wants to merge 2 commits into
mainfrom
fix/issue-607-deadcode-ratchet

Conversation

@scottschreckengaust

@scottschreckengaust scottschreckengaust commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

The dead-code ratchet (scripts/check-deadcode-ratchet.mjs) was failing on main, independent of any PR. This re-greens it by removing 2 genuinely-dead devDependencies, suppressing 1 knip false positive, and rebaselining the knip issue count to its honest post-cleanup value.

Closes #607

Reproduced root cause + evidence

On a fresh origin/main worktree:

$ node scripts/check-deadcode-ratchet.mjs
❌ Dead-code count increased: 91 (baseline 78, +13).
exit=1

The knip count is now 91 vs baseline 78 (the issue reported 81; main has drifted further since it was filed). Category breakdown of the 91:

category count
devDependencies 3
files 3 (jira-forge-app fixtures)
exports 44
types 41

The 3 devDependencies findings are the ones this issue targets: @astrojs/check (root package.json), markdown-link-check (docs/package.json), @types/pdf-parse (cdk/package.json).

The fix

I confirmed each candidate against knip and its actual usage before acting — the issue's premise that all three are "genuinely dead" holds for two of them; the third is a knip false positive.

  • Removed @types/pdf-parse from cdk/package.json — genuinely dead. pdf-parse@2.4.5 ships its own types (dist/pdf-parse/cjs/index.d.cts) and cdk already carries a local ambient declaration cdk/src/types/pdf-parse.d.ts. The @types stub was the stale v1 API (^1.1.5) against an installed v2. Nothing references it. mise //cdk:compile (tsc) stays green after removal.
  • Removed @astrojs/check from the root package.json — a duplicate of the docs workspace's own dependency. docs/package.json declares @astrojs/check as a dependency, and that is where astro check runs (the docs:check script and the docs-astro-check pre-commit hook, both cd docs && ./node_modules/.bin/astro check). The root devDep was unused. mise //docs:check stays green (0 errors, 0 warnings) after removal; the hoisted install is retained via the docs dependency.
  • Suppressed markdown-link-check in knip.json (docs ignoreDependencies) — a knip false positive, not dead code. It is invoked via ./node_modules/.bin/markdown-link-check inside docs/scripts/link-check.sh (wired into the //docs:link-check mise task, itself part of drift-prevention), which knip cannot parse. Per the ratchet's own documented guidance ("Per-category false positives belong in knip.json, not here") and the issue's option 2, this is suppressed rather than removed. mise //docs:link-check still resolves and runs the binary.

Rebaselined knip-baseline.json count 78 → 88 — the honest, empirically-measured post-cleanup count (91 − 2 removed − 1 suppressed = 88), not inflated to paper over the failure. The remaining 88 are pre-existing unused exports/types plus the jira-forge-app fixture files, out of scope for #607 (see Follow-ups).

Note: this ratchet is advisory / non-blocking in CI today (check:deadcode-ratchet — "Advisory in CI for now; flips to blocking once the baseline is driven to zero"), but it now exits 0 as the issue requires.

Testing

  • node scripts/check-deadcode-ratchet.mjs✅ Dead-code count holding at baseline (88). exit 0 (the acceptance criterion).
  • mise //cdk:compile → exit 0 (proves @types/pdf-parse removal doesn't break cdk typecheck).
  • mise //docs:check (astro check) → 0 errors / 0 warnings / 0 hints (proves @astrojs/check root removal is safe).
  • mise //docs:link-check → runs, links checked (proves markdown-link-check still available).
  • pre-commit run --files <changed> → all Passed / correctly Skipped.
  • gitleaks on my commit range (origin/main..HEAD) → no leaks.
  • mise run build: the only failure is a pre-existing environment limitation in this sandbox — cdk:synth:quiet errors on ec2:DescribeAvailabilityZones (the assumed IAM role lacks EC2 AZ-lookup permission). It is an AWS auth error, not a dep/type regression: handler esbuild bundling (including --external:pdf-parse) completes with no Cannot find module / TS errors. Unrelated to this change.

Files changed

  • package.json — drop root @astrojs/check devDep
  • cdk/package.json — drop @types/pdf-parse devDep
  • knip.json — add markdown-link-check to docs ignoreDependencies (false-positive suppression)
  • knip-baseline.json — count 78 → 88 + documented reasoning
  • yarn.lock — 7-line removal (@types/pdf-parse entry only)

Dependencies / related

Follow-ups (noted, not touched — out of scope for #607)

  • 44 unused exports + 41 unused types flagged by knip — pre-existing drift; risky to remove without per-symbol analysis. Should be chipped away in future dead-code PRs, ratcheting the baseline down each time.
  • 3 unused files under integrations/jira-forge-app/ (src/index.js, src/proxy.js, test/proxy.test.js) — flagged as unused by knip; likely a Forge-app entry-point that knip's config doesn't recognize as an entry. Worth a scoped knip.json entry or removal in a dedicated PR.

🤖 Generated with Claude Code

…seline knip (#607)

The dead-code ratchet (scripts/check-deadcode-ratchet.mjs) failed on main:
knip counted 91 issues vs the committed baseline of 78 in knip-baseline.json,
so `node scripts/check-deadcode-ratchet.mjs` exited 1. (The issue reported 81;
main has drifted further since it was filed.) The +13 came from three
devDependency findings plus pre-existing unused exports/types.

Resolved all three devDep findings knip reported:

- Removed @types/pdf-parse from cdk/package.json — genuinely dead. pdf-parse@2
  ships its own types (dist/pdf-parse/cjs/index.d.cts) and cdk already has a
  local ambient declaration cdk/src/types/pdf-parse.d.ts; the @types stub was
  the stale v1 API. cdk tsc compile stays green after removal.
- Removed @astrojs/check from the root package.json — a duplicate of the docs
  workspace's own dependency (docs/package.json), which is where `astro check`
  actually runs (docs:check script, docs-astro-check pre-commit hook). Root's
  copy was unused; astro check stays green (0 errors) after removal.
- Suppressed markdown-link-check in knip.json (docs ignoreDependencies) — a
  knip FALSE POSITIVE, not dead code. It is invoked via
  ./node_modules/.bin/markdown-link-check inside docs/scripts/link-check.sh
  (wired into the //docs:link-check mise task), which knip cannot parse.
  Per the ratchet's own guidance, false positives belong in knip.json, not the
  baseline.

Rebaselined knip-baseline.json count 78 -> 88 to the honest post-cleanup count
(empirically measured, not inflated to paper over the failure). The remaining
88 are pre-existing unused exports/types and the jira-forge-app fixture files,
out of scope for #607.

The ratchet is advisory/non-blocking in CI today but now exits 0.

Closes #607

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@scottschreckengaust

Copy link
Copy Markdown
Contributor Author

✅ Acceptance summary (for the reviewer)

#607node scripts/check-deadcode-ratchet.mjs now exits 0 (was exit 1: knip count > baseline).

  • Root cause: the issue reported count 81 > baseline 78; by the time of this fix main had drifted to 91.
  • Fix: removed 2 genuinely-dead devDeps (@types/pdf-parse in cdk — superseded by pdf-parse@2 own types; @astrojs/check at root — duplicate of the docs workspace dep) + suppressed 1 knip false-positive in knip.json (markdown-link-check, invoked via ./node_modules/.bin in docs/scripts/link-check.sh — removing it would break //docs:link-check).
  • Rebaselined knip-baseline.json 78 → 88 = the honest post-cleanup count (91 − 2 − 1), not inflated to paper over the failure.
  • ⚠️ Reviewer note: baseline rose (78→88). This is a deliberate, documented reconciliation of accumulated drift, not new dead code — the ratchet is advisory/non-blocking today. Remaining 44 unused exports + 41 types are deferred to future dead-code PRs to ratchet the baseline down.

/review_pr = approve, zero blocking. Testing: ratchet exits 0; mise run build green (cdk:synth blocked only by a sandbox AWS-cred limit, unrelated).

🔀 Merge guidance (for the reviewer)

Independent — no ordering constraint. Cluster deadcode (disjoint); touches knip-baseline.json, knip.json, cdk/package.json, root package.json, yarn.lock. Not up-to-date with main by 1 commit, but the ruleset does not enforce strict-up-to-date and main’s change was a workflows-only bump (no overlap) → merges cleanly. Native auto-merge is disabled repo-wide; awaits your manual squash-merge.

🤖 orchestrator note (agent) — promotion is orchestrator-driven; merge remains a human action.

@theagenticguy theagenticguy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed by cloning the repo, checking out the head, and reproducing every claim rather than trusting the description. The core of this PR is right, and the description is unusually honest about its own numbers. I verified the arithmetic independently and it holds exactly.

Confirmed:

  • knip on origin/main = 91, on this branch = 88. Each of the three edits removes exactly one finding, so 91 - 3 = 88 is measured, not padded to make the gate pass. The CI job log agrees: Dead-code count holding at baseline (88).
  • The drift is genuine code drift, not a tooling artifact. I ran main under both knip 6.20.0 and the currently pinned 6.23.0 and got 91 under each, so the +13 is real and the version bump in #522 did not cause it.
  • @types/pdf-parse removal is safe. That package tops out at v1.1.5 (the v1 API) against an installed pdf-parse@2.4.5, which ships its own dist/pdf-parse/cjs/index.d.cts.
  • @astrojs/check root removal is safe, and the hoisting worry dissolves on inspection: docs/node_modules/.bin/astro-check is already a symlink up into root node_modules, and docs/package.json still declares the identical ^0.9.9 range, so yarn v1 keeps hoisting it and the retained lock entry is legitimate.
  • markdown-link-check is a true false positive. docs/scripts/link-check.sh invokes ./node_modules/.bin/markdown-link-check, wired through //docs:link-check into drift-prevention into mise run build. The ignoreDependencies entry sits in the correct workspace scope, since the dep is declared in docs/package.json.
  • The yarn.lock edit is minimal and correct. Nothing else requested @types/pdf-parse.

Re-greening an advisory gate that was already red on main is the right call, and rebaselining to the measured value instead of an inflated one is the right instinct. Two things should change first.

1. The baseline absorbs 3 findings that your own rule says belong in knip.json (should-fix). The three files findings under integrations/jira-forge-app/ are live Forge app source, not fixtures. Putting "integrations/**" in knip's ignore drops the count to 85, which I verified by running it. 85 is the honest number, and 88 bakes 3 phantom findings into durable state that every future ratchet PR inherits.

2. The new comment blesses an ambient type declaration that misdescribes the runtime, and there is a live bug behind it (should-fix, pre-existing). Details inline on cdk/package.json. Not introduced by this PR, so I am not asking you to fix it here, only to stop citing it as justification and to file it.

Nits, both pre-existing and neither blocking: scripts/check-deadcode-ratchet.mjs:52 still documents "the installed knip (6.20.0, pinned exactly)" while the pin is now 6.23.0, worth correcting while you are in this file. And the roughly 7 genuine unused export/type regressions inside the +13 merged unnoticed precisely because this job is continue-on-error: true. They deserve a tracking issue, otherwise the "flips to blocking once the baseline is driven to zero" milestone recedes with every merge.

Requesting changes narrowly, on the one-line knip.json change plus the comment corrections. Everything else here is sound and I would happily approve once the baseline reads 85.

Comment thread knip.json
"entry": ["astro.config.mjs", "src/content.config.ts", "src/components/**/*.astro"],
"project": ["src/**/*.{astro,ts,tsx}"],
"ignoreDependencies": ["@astrojs/check", "remark-gfm", "@pagefind/default-ui", "jest"]
"ignoreDependencies": ["@astrojs/check", "markdown-link-check", "remark-gfm", "@pagefind/default-ui", "jest"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The markdown-link-check addition here is correct and well-justified. The gap is what is not here.

knip-baseline.json calls the 3 remaining files findings "the jira-forge-app fixture files," but they are live application source:

  • integrations/jira-forge-app/manifest.yml:10 declares handler: index.handler, so src/index.js is the Forge function entry point.
  • src/index.js:22 imports ./proxy.js.
  • test/proxy.test.js runs under mise //:test:jira-forge-app, which is a dependency of mise run build.

knip flags all three only because integrations/ is not in the root workspaces array (cdk, cli, docs) and is not in knip's ignore, so nothing there is reachable from a configured entry point. That is the textbook definition of the per-category false positive this file's own guidance says to suppress here rather than absorb into the baseline.

I verified the fix. Adding "integrations/**" to the top-level ignore:

"ignore": [
  ".semgrep/**",
  "scripts/**",
  "integrations/**"
]

drops the count from 88 to 85, with files going to 0 and only the pre-existing exports: 44 / types: 41 remaining. A scoped knip workspace entry for the Forge app would work too and is arguably better, since it would keep real dead code there detectable instead of blanket-ignoring the directory.

Worth doing rather than deferring: the baseline is the durable artifact every future dead-code PR ratchets against, so 3 phantom findings embedded now are 3 that can never be cleaned up, and they push the "driven to zero" flip-to-blocking milestone permanently out of reach.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — but as a scoped knip workspace entry rather than a blanket integrations/** ignore (the option you flagged as arguably better). knip.json now declares integrations/jira-forge-app with entry: ["src/index.js!", "test/**/*.test.js"] and project: ["src/**/*.js"], so the Forge function entry point and its tests are reachable and any real dead code there stays detectable instead of being blanket-ignored.

Verified locally: files findings → 0, count drops 88 → 85 (exports: 44 / types: 41 remaining), and node scripts/check-deadcode-ratchet.mjs✅ Dead-code count holding at baseline (85). exit 0. Fixed in e825a75.

Comment thread cdk/package.json
"@types/jest": "^30.0.0",
"@types/js-yaml": "^4.0.9",
"@types/node": "^26",
"@types/pdf-parse": "^1.1.5",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this is correct, and safe for a slightly different reason than the description gives. cdk/tsconfig.json sets "types": ["node"], which already blocked @types/pdf-parse from being auto-included, and the local declare module 'pdf-parse' in cdk/src/types/pdf-parse.d.ts shadows node_modules/@types on explicit import anyway. So this devDep was already inert and its removal is a genuine no-op. Good call either way, since a v1 type stub against an installed v2 is a trap.

The problem is the justification recorded in knip-baseline.json, which describes the removal as "superseded by pdf-parse@2's bundled types plus the local cdk/src/types/pdf-parse.d.ts ambient declaration." Those two are not complementary. The ambient declaration overrides v2's real bundled types, and it describes the v1 API:

function pdfParse(data: Buffer, options?: { max?: number }): Promise<PdfParseResult>;
export = pdfParse;

pdf-parse@2.4.5 exports no callable default. Verified against the installed package:

$ node -e "const m=require('pdf-parse'); console.log(Object.keys(m)); console.log(typeof m.default)"
[ 'AbortException', ..., 'PDFParse', 'VerbosityLevel', 'getException' ]
undefined

So in cdk/src/handlers/shared/attachment-screening.ts:258, pdfParseFn = (mod as any).default ?? mod resolves to the module namespace object, and the call at line 277 throws TypeError: pdfParseFn is not a function. It is caught and rewrapped, so every application/pdf attachment reaching line 201 fails as PDF "<name>" could not be processed. It may be corrupt or use unsupported features. PDF attachment screening is broken for all inputs, and the failure is indistinguishable from a genuinely corrupt PDF.

The { virtual: true } mock at cdk/test/handlers/shared/attachment-screening.test.ts:365 supplies { __esModule: true, default: jest.fn() }, so the suite asserts against a v1 shape that no longer exists and cannot catch this.

This predates the PR: pdf-parse@^2.4.5 and the ambient decl landed together in #434 (2026-06-30), and the as any plus the ambient override is what kept tsc quiet. I am not asking you to fix it here. Two smaller asks:

  1. Drop the "plus the local ambient declaration" clause from the baseline comment, so this PR does not turn a version-mismatched stub into documented policy.
  2. File a follow-up. The v2 shape is new PDFParse({ data: content }).getText() returning a TextResult, and fixing it means deleting the ambient decl, letting v2's own types apply, and replacing the mock with something that reflects the real module.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — reproduced the v2-shape mismatch independently (require('pdf-parse') exports PDFParse/VerbosityLevel/… and .default is undefined, so (mod as any).default ?? mod yields the namespace object and the call throws TypeError). Both asks done:

  1. Dropped the "plus the local ambient declaration" clause from knip-baseline.json so this PR no longer cites the version-mismatched stub as justification.
  2. Filed the follow-up: fix(cdk): PDF attachment screening broken — ambient pdf-parse decl describes v1 API against installed v2 #683 — captures the v1-decl-over-v2 root cause, the silent "could not be processed" symptom for every PDF, the v1-shaped virtual mock that hides it, and the fix (delete the ambient decl, adopt the v2 new PDFParse({ data }).getText() API, and add a real-PDF end-to-end test so a future mismatch fails loudly).

Fixed in e825a75.

Comment thread knip-baseline.json Outdated
{
"count": 78,
"comment": "Dead-code ratchet baseline for issue #282 (cairn MVG gate #6). This is the knip issue count at the time the gate was introduced — pre-existing unused exports/types that are out of scope to remove in the gate PR. The ratchet (scripts/check-deadcode-ratchet.mjs) fails the build only if the count rises above this number. When dead code is removed and the count drops, lower this value in the same PR to lock in the gain. Per-category false positives belong in knip.json, not here."
"count": 88,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per the knip.json comment, this should read 85 once the integrations/** findings move to knip.json where this file's own guidance puts them. I verified 85 by running the ratchet with that change applied.

Two accuracy fixes for the prose on line 3 as well:

  • "the jira-forge-app fixture files" is wrong. src/index.js is the Forge function entry point named in manifest.yml, and test/proxy.test.js runs in mise run build. Calling live source "fixtures" is what makes absorbing them into the baseline look reasonable.
  • The @types/pdf-parse clause credits the local ambient declaration as part of why removal is safe. It is the opposite: that declaration shadows v2's real types and currently misdescribes the runtime. See the cdk/package.json thread.

Separately, this comment is now carrying the full changelog of one PR. The baseline is machine-read for count only, so consider trimming the narrative to a sentence and letting the PR and issue history hold the reasoning, otherwise the next three dead-code PRs each append a paragraph and the actual rule at the top gets buried.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All three addressed in e825a75:

…88→85 (#607)

Addresses review on #673:

- Add a scoped knip workspace entry for integrations/jira-forge-app
  (entry: src/index.js from manifest handler + test/**/*.test.js) instead
  of absorbing its 3 live-source `files` findings into the baseline. Keeps
  real dead code in the Forge app detectable rather than blanket-ignoring
  the directory. Drops the count from 88 to the honest 85.
- Rebaseline knip-baseline.json count 88→85 and trim the comment to the
  durable rule; the remaining 85 are pre-existing exports (44) + types (41),
  tracked in #682. Drops the inaccurate "fixture files" wording and the
  "plus the local ambient declaration" clause (the pdf-parse v2 runtime bug
  behind it is filed as #683).
- Correct the stale "knip (6.20.0)" pin comment to 6.23.0 in the ratchet.

Ratchet: `node scripts/check-deadcode-ratchet.mjs` → holding at baseline (85), exit 0.
@scottschreckengaust

Copy link
Copy Markdown
Contributor Author

@theagenticguy — all review feedback addressed in e825a75. Thanks for the thorough, reproduce-everything review.

Blocking (both resolved):

  1. jira-forge-app findings out of the baseline. Rather than a blanket integrations/** ignore, I added a scoped knip workspace entry for integrations/jira-forge-app (entry: ["src/index.js!", "test/**/*.test.js"], project: ["src/**/*.js"]) — the "arguably better" path you noted, which keeps genuine dead code there detectable. Verified: files → 0, count 88 → 85, ✅ Dead-code count holding at baseline (85). exit 0.
  2. Baseline comment. Set to count: 85; removed the inaccurate "fixture files" wording and the "plus the local ambient declaration" clause; trimmed the narrative back to the durable rule + a one-line note pointing at the tracking issue.

Nits + follow-ups:

Baseline now reads 85. Re-requesting review once checks are green.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore: dead-code ratchet is failing on main (knip count 81 > baseline 78)

2 participants