Skip to content

#59 dogfood: switch root knip.json to production-marked config#61

Merged
devill merged 26 commits into
mainfrom
claude/ecstatic-pasteur-2iezgz
Jun 23, 2026
Merged

#59 dogfood: switch root knip.json to production-marked config#61
devill merged 26 commits into
mainfrom
claude/ecstatic-pasteur-2iezgz

Conversation

@devill

@devill devill commented Jun 21, 2026

Copy link
Copy Markdown
Owner

Adopt the gated knip double-run on habit-hooks itself by marking entry
and project patterns with trailing ! and listing test files as
unmarked entry. The production pass now surfaces test-only / dead-code
findings for clearing.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB

claude added 26 commits June 21, 2026 06:22
Adopt the gated knip double-run on habit-hooks itself by marking entry
and project patterns with trailing `!` and listing test files as
unmarked entry. The production pass now surfaces test-only / dead-code
findings for clearing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip's production pass flagged `requiresNodeRuntime` in src/wrap/resolve.ts
as unused-in-production: only resolve.test.ts imported it directly, while
production reaches the logic solely through `spawnTarget`. Removed the
`export` (making it a private helper) and dropped the direct unit test —
both branches stay covered by the existing `spawnTarget` tests, which now
exercise the helper the way production does.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip's production pass flagged `fallbackNotice` in src/wrap/notices.ts as
unused-in-production: only notices.test.ts imported it directly, while
production reaches it through the public `noticesFor`. Removed the `export`
and dropped the standalone unit test — the surviving `noticesFor` test
asserts the same fallback string through the public entry point, so no
coverage is lost.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip's production pass flagged `resolveFix` in src/mapper/mapper.ts as
unused-in-production: only mapper.test.ts imported it directly, a back door
into the module. Production reaches the logic through the public
mapIssues -> resolveAction -> resolveActionFix -> resolveFix chain. Demoted
it to a private function and rewrote the test block to exercise all seven
fix-resolution cases through the public mapIssues entry point (via a
test-local routeAs helper), preserving every case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
…sBranch

knip's production pass flagged `getMergeBase` in src/git/scope.ts as
unused-in-production: only scope.test.ts imported it directly. Production
reaches it transitively through the public `getChangedVsBranch`. Demoted it
to a private helper and rewrote the test to drive through
getChangedVsBranch with real branch divergence (main advanced past the
branch point), so the assertion genuinely depends on correct merge-base
resolution.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip's production pass flagged the `GitScopeError` export in
src/git/resolve-scope.ts: the class is thrown in production (requireRepo,
reached via resolveScope) but caught only by the generic Error handler in
cli.ts that reads `.message` — production never references the type name.
Only resolve-scope.test.ts imported the name, for a toThrow(GitScopeError)
assertion. Demoted the class to module-private (still thrown) and changed
the test to assert the observable message through the public resolveScope
entry point.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip's production pass flagged `listPrompts` in src/prompts/registry.ts as
unused-in-production: only registry.test.ts and coverage.test.ts walked the
full registry; production uses point lookups via lookupPrompt. Extracted
the seed catalogue (RuleSeed, supplementalSeeds, buildPrompt, and a new
allSeeds()) into src/prompts/seeds.ts, which buildRegistry now consumes in
production. Deleted listPrompts; tests enumerate via allSeeds() and add a
check that every seed is reachable through the production lookupPrompt path.
allSeeds() dedups last-wins to match the registry Map, so severities are
unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip's production pass flagged `buildPresetSensors` in src/sensors/preset.ts:
a thin legacy adapter over buildDefaultSensors('typescript', ...) that no
production path reached — runner.ts -> assembleSensors calls
buildDefaultSensors directly. Only preset.test.ts used the adapter. Deleted
the adapter and its orphaned imports and retargeted the test at the real
production entry point (buildDefaultSensors), preserving the TS preset
composition assertions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
…eError

knip's production pass flagged both error classes in src/baseline/store.ts
as unused-in-production: they are thrown in production (validateVersion,
parseRaw, shape validation, all reached via loadBaseline) but their names
were imported only by store.test.ts for .toThrow(<class>) identity checks.
Demoted both to module-private classes (still thrown) and dropped the
identity assertions; the paired message-regex assertions through loadBaseline
preserve coverage and match the messages production throws.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip's production pass flagged both helpers in src/config/tool-smells.ts as
unused-in-production: production consumes only the derived consts
(JSCPD_SMELL = singleSmellFor('jscpd'), COMMENT_SMELL =
catalogueSmell('non-essential-comment')), while the functions themselves
were imported only by tool-smells.test.ts. Demoted both to module-private
(still building the consts at module load) and rewrote the tests to assert
the single-pick / identity-pick invariants through the production-consumed
consts. Retired three error-path tests that fed synthetic inputs production
never produces; the load-time guards still take the suite red on a real
catalogue regression.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip's production pass flagged both helpers in
src/cli/init/install-commands.ts as unused-in-production: production reaches
them only transitively via the public installCommandsFor, while the
exports were imported directly only by install-commands.test.ts. Demoted
both to module-private and migrated every distinct case (per-package-manager
command assembly via lockfile seeding; per-tool package expansion) to run
through installCommandsFor. Test-count drop is consolidation, not lost cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip's production pass flagged `detectLanguage` in
src/cli/init/scaffold-config.ts as unused-in-production: production reaches
it only transitively via the public scaffoldConfig, while the export was
imported directly only by scaffold-config.test.ts. Demoted it to
module-private and rewired all five detection tests (python via
pyproject.toml / setup.py, typescript default, scaffold carries language,
explicit override) through scaffoldConfig, asserting the written config's
language. The sibling detectLanguageWithReason export (used by
report-language.ts) and the pre-existing DetectedLanguage type are untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
…umerKnipMajor

knip's production pass flagged buildKnipArgs, consumerKnipMajor and
resolveKnipBin in src/checks/knip-wrap.ts. They are defined in knip-resolve.ts
and were re-exported from knip-wrap.ts solely so the test could import them
from ./knip-wrap.js. resolveKnipBin and buildKnipArgs are genuine
production-surface exports of knip-resolve.ts (knip-wrap imports and calls
them), so the test now imports them directly from ./knip-resolve.js and the
re-export line is gone. consumerKnipMajor was only used internally within
knip-resolve.ts, so it is un-exported (private); its three version-detection
edge cases (v6 omits classMembers, missing/unparseable package.json falls
back to v5 and adds classMembers) are rewritten to drive through the public
knipWrap.run via a consumer knip bin shim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
…nternals

knip's production pass flagged tryBundledJscpdBin, resolveJscpdBin and
runJscpdWrap in src/checks/jscpd-wrap.ts. The real production entry is the
jscpdWrap Check (consumed by sensors/registry.ts); these were exported only
for tests. Mirroring the knip-wrap fix: extracted bin resolution into a new
sibling module src/checks/jscpd-resolve.ts where resolveJscpdBin is a genuine
production export (jscpd-wrap imports and calls it); tryBundledJscpdBin moved
there and made private. runJscpdWrap is un-exported and its tests drive
through jscpdWrap.run. Also removed the test-only `tmpRoot` seam from the
production path; cleanup is now verified by redirecting TMPDIR and asserting
the scratch dir ends empty. The -n noSymlinks invariant and arg construction
are byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
…apper

knip's production pass flagged src/sensors/python-preset.ts as an unused
file: production builds python sensors through the registry
(runner -> assembleSensors -> buildDefaultSensors('python') ->
DEFAULT_SENSOR_IDS.python), never through this module. python-preset.ts was
a superseded thin wrapper (issue #16 made the registry the single
construction site) whose only importer was its own test. Deleted the file
and its test; added a python-preset structural block to preset.test.ts that
asserts the sensor composition against the live buildDefaultSensors('python')
path. ruff mapping, spawn-failure, and end-to-end python coverage remain in
adapter/notices/python-acceptance tests. This also clears the pre-existing
unused PythonPresetInput type.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip flagged both exports in src/baseline/auto-prune.ts as unused (default
and production passes). Both are referenced only internally by the public
runWithAutoPrune (used by cli.ts and exercised by tests); no other file
imported them, so the export keyword was gratuitous. Demoted both to
module-private. No behaviour change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip flagged reportSkillResult in src/cli/init/reporters.ts as unused
(default and production). It is called only internally by the sibling
reportSkillResults (plural), which is the genuinely-used export (run.ts).
Dropped the gratuitous export keyword. No behaviour change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip flagged globsFor (export) and DiscoverOptions (type) in src/discover.ts
as unused (default and production). Both are referenced only internally by
the public discoverFiles; no other file imports them. Dropped the export on
both. discoverFiles (the real entry point) is untouched. No behaviour change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip flagged the `export type { BaselineFile }` re-export in
src/baseline/commands.ts as unused: BaselineFile is exported from store.ts
(where it is consumed across the baseline modules and runner) and nobody
imports it from commands.ts. Removed the re-export and the import that only
fed it; BaselineEntry stays (genuinely used here).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip flagged ReapResult in src/baseline/reap.ts as unused: it is only the
return-type annotation of reapBaseline in the same file; no other module
imports the name. Dropped the export keyword.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip flagged DetectedLanguage in src/cli/init/scaffold-config.ts as unused:
it only annotates detectLanguageWithReason's return in the same file; no
other module imports the name. Dropped the export keyword.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip flagged both types in src/guide/guide.ts as unused: they only annotate
the guide() function's signature in the same file; no other module imports
the names. Dropped the export keyword on both.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip flagged SensorFactory in src/sensors/registry.ts as unused: it is used
only internally (the registry Map and sensorFactory's return type); no other
module imports the name. Dropped the export keyword.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip flagged SensorRunInput in src/sensors/runner.ts as unused: it only
annotates the run() method param in the same file; no other module imports
the name. Dropped the export keyword.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip flagged WrapperSpec in src/sensors/wrapper-script.ts as unused: it only
annotates runWrapper/wrapperScriptSensor params in the same file; no other
module imports the name. Dropped the export keyword.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
knip flagged LeafSpec (src/sensors/leaf.ts), and the checkLeafSensor +
LeafSpec re-exports plus the PresetInput type (src/sensors/preset.ts) as
unused. registry.ts imports checkLeafSensor/LeafSpec directly from leaf.js,
so preset.ts's re-exports of them were gratuitous; PresetInput was orphaned
when buildPresetSensors was removed. Demoted LeafSpec to module-private in
leaf.ts, trimmed preset.ts to only the still-consumed
issueToViolation/violationToIssue re-exports, and deleted PresetInput with
its now-unused imports.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RzQYUPZUBMKMaVwMDGPJAB
@devill devill merged commit 5c4ecc8 into main Jun 23, 2026
2 checks passed
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.

2 participants