From dce8eed0b9fbaf32b5b517769f07bf063900f593 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 12 Jul 2026 19:58:38 -0700 Subject: [PATCH] ci(deadcode): restore enforced unused-export ratchet (#105826) * ci(deadcode): restore export ratchet * chore(deadcode): refresh export baseline * refactor(sessions): remove obsolete patch writer * refactor(deadcode): classify current export residue * fix(deadcode): preserve exported signature types * chore(deadcode): sync export baseline after rebase * chore(deadcode): classify pairing test exports * fix(ci): refresh plugin SDK declaration budget --- .github/workflows/ci.yml | 6 ++ config/knip.config.ts | 99 ++++++++++--------- docs/ci.md | 2 +- scripts/deadcode-exports.baseline.mjs | 69 +------------ .../lib/plugin-sdk-declaration-budget.d.mts | 4 +- scripts/lib/plugin-sdk-declaration-budget.mjs | 6 +- .../lib/session-accessor-debt-baseline.json | 2 +- src/agents/auth-profiles/state.ts | 16 +-- src/config/env-vars.ts | 1 - src/config/sessions.cache.test.ts | 24 ----- src/config/sessions.test.ts | 29 ------ src/config/sessions/store.ts | 28 ------ src/gateway/handshake-timeouts.test.ts | 75 +++++++------- src/gateway/handshake-timeouts.ts | 1 - src/gateway/server-shared-auth-generation.ts | 2 +- .../server.auth.default-token.suite.ts | 8 +- src/gateway/server.auth.test-helpers.ts | 2 +- src/infra/restart.ts | 2 +- test/scripts/check-deadcode-exports.test.ts | 10 ++ test/scripts/ci-workflow-guards.test.ts | 33 ++++++- 20 files changed, 154 insertions(+), 265 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce84ab827702..1d70b9975da8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1613,6 +1613,12 @@ jobs: has_package_script "deadcode:unused-files"; then pnpm deadcode:dependencies pnpm deadcode:unused-files + if has_package_script "deadcode:exports"; then + pnpm deadcode:exports + elif [[ "$HISTORICAL_TARGET" != "true" ]]; then + echo "Current CI targets must provide the deadcode:exports package script." >&2 + exit 1 + fi elif [[ "$HISTORICAL_TARGET" == "true" ]] && has_package_script "deadcode:ci"; then pnpm deadcode:ci else diff --git a/config/knip.config.ts b/config/knip.config.ts index 2247f940c663..7d493c33e011 100644 --- a/config/knip.config.ts +++ b/config/knip.config.ts @@ -79,62 +79,69 @@ const rootBundledPluginRuntimeDependencies = [ "tokenjuice", ] as const; +// These files are test infrastructure, so their exports are intentionally +// available to tests without becoming part of the production dead-code scan. +const ignoredTestSupportFiles = [ + "**/__tests__/**", + "src/test-utils/**", + "**/test-helpers/**", + "**/test-fixtures/**", + "**/test-support/**", + "**/test-*.ts", + "**/vitest*.{ts,mjs}", + "**/*test-helpers.ts", + "**/*test-fixtures.ts", + "**/*test-harness.ts", + "**/*test-utils.ts", + "**/*test-support.ts", + "**/*test-shared.ts", + "**/*mocks.ts", + "**/*.e2e-mocks.ts", + "**/*.e2e-*.ts", + "**/*.fixture-test-support.ts", + "**/*.harness.ts", + "**/*.job-fixtures.ts", + "**/*.mock-harness.ts", + "**/*.menu-test-support.ts", + "**/*.suite-helpers.ts", + "**/*.test-setup.ts", + "**/job-fixtures.ts", + "**/*test-mocks.ts", + "**/*test-runtime*.ts", + "**/*.mock-setup.ts", + "**/*.cases.ts", + "**/*.e2e-harness.ts", + "**/*.fixture.ts", + "**/*.fixtures.ts", + "**/*.mocks.ts", + "**/*.mocks.shared.ts", + "**/*.route-test-support.ts", + "**/*.shared-test.ts", + "**/*.suite.ts", + "**/*.test-runtime.ts", + "**/*.testkit.ts", + "**/*.test-fixtures.ts", + "**/*.test-harness.ts", + "**/*.test-helper.ts", + "**/*.test-helpers.ts", + "**/*.test-mocks.ts", + "**/*.test-utils.ts", + "test/helpers/live-image-probe.ts", +] as const; + const config = { ignoreFiles: [ "scripts/**", "packages/*/dist/**", - "**/__tests__/**", - "src/test-utils/**", - "**/test-helpers/**", - "**/test-fixtures/**", - "**/test-support/**", "**/live-*.ts", - "**/test-*.ts", - "**/vitest*.{ts,mjs}", - "**/*test-helpers.ts", - "**/*test-fixtures.ts", - "**/*test-harness.ts", - "**/*test-utils.ts", - "**/*test-support.ts", - "**/*test-shared.ts", - "**/*mocks.ts", - "**/*.e2e-mocks.ts", - "**/*.e2e-*.ts", - "**/*.fixture-test-support.ts", - "**/*.harness.ts", - "**/*.job-fixtures.ts", - "**/*.mock-harness.ts", - "**/*.menu-test-support.ts", - "**/*.suite-helpers.ts", - "**/*.test-setup.ts", - "**/job-fixtures.ts", - "**/*test-mocks.ts", - "**/*test-runtime*.ts", - "**/*.mock-setup.ts", - "**/*.cases.ts", - "**/*.e2e-harness.ts", - "**/*.fixture.ts", - "**/*.fixtures.ts", - "**/*.mocks.ts", - "**/*.mocks.shared.ts", - "**/*.route-test-support.ts", - "**/*.shared-test.ts", - "**/*.suite.ts", - "**/*.test-runtime.ts", - "**/*.testkit.ts", - "**/*.test-fixtures.ts", - "**/*.test-harness.ts", - "**/*.test-helper.ts", - "**/*.test-helpers.ts", - "**/*.test-mocks.ts", - "**/*.test-utils.ts", - "test/helpers/live-image-probe.ts", "src/secrets/credential-matrix.ts", "src/shared/text/assistant-visible-text.ts", bundledPluginFile("telegram", "src/bot/reply-threading.ts"), bundledPluginFile("telegram", "src/draft-chunking.ts"), ], - ignore: ["packages/*/dist/**"], + // Knip's `ignoreFiles` only suppresses unused-file findings. Test helpers + // belong in `ignore` so they do not inflate unused-export/type findings. + ignore: ["packages/*/dist/**", ...ignoredTestSupportFiles], workspaces: { ".": { entry: rootEntries, diff --git a/docs/ci.md b/docs/ci.md index 62711e16b773..635d5aa99adc 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -115,7 +115,7 @@ job budget. Android CI runs both `testPlayDebugUnitTest` and `testThirdPartyDebugUnitTest` and then builds the Play debug APK. The third-party flavor has no separate source set or manifest; its unit-test lane still compiles the flavor with the SMS/call-log BuildConfig flags, while avoiding a duplicate debug APK packaging job on every Android-relevant push. -The `check-dependencies` shard runs a production Knip dependency-only pass and the unused-file allowlist check. The unused-file guard fails when a PR adds a new unreviewed unused file or leaves a stale allowlist entry, while preserving intentional dynamic plugin, generated, build, live-test, and package bridge surfaces that Knip cannot resolve statically. The unused-exports baseline remains available as an advisory maintenance scan through `pnpm deadcode:exports`; after deleting dead code, regenerate it with `pnpm deadcode:exports:update`. +The `check-dependencies` shard runs production Knip dependency, unused-file, and unused-export checks. The unused-file guard fails when a PR adds a new unreviewed unused file or leaves a stale allowlist entry, while preserving intentional dynamic plugin, generated, build, live-test, and package bridge surfaces that Knip cannot resolve statically. The unused-export guard excludes test-support files, then fails on new findings or stale required baseline entries; after deleting dead exports, regenerate the shrink-only baseline with `pnpm deadcode:exports:update`. Historical targets run the export guard when they provide it and retain their older dead-code fallback otherwise. ## ClawSweeper activity forwarding diff --git a/scripts/deadcode-exports.baseline.mjs b/scripts/deadcode-exports.baseline.mjs index 9713defd622c..fb43c8172eff 100644 --- a/scripts/deadcode-exports.baseline.mjs +++ b/scripts/deadcode-exports.baseline.mjs @@ -1373,8 +1373,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "extensions/qa-lab/src/suite-summary.ts: readQaSuiteFailedOrSkippedScenarioCountFromSummary", "extensions/qa-lab/src/suite-summary.ts: readQaSuiteFailedScenarioCountFromSummary", "extensions/qa-lab/src/suite.ts: runQaScenarioWithFlakeRetry", - "extensions/qa-lab/src/test-file-scenario-runner.ts: QaScenarioCommandExecution", - "extensions/qa-lab/src/test-file-scenario-runner.ts: qaTestFileScenarioRunnerTesting", "extensions/qa-lab/src/token-efficiency-report.ts: BuildTokenEfficiencyReportParams", "extensions/qa-lab/src/token-efficiency-report.ts: TokenEfficiencyReport", "extensions/qa-lab/src/token-efficiency-report.ts: TokenEfficiencyRow", @@ -1531,8 +1529,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "extensions/slack/src/monitor/message-handler/prepare-routing.ts: SlackRoutingContextDeps", "extensions/slack/src/monitor/message-handler/prepare-routing.ts: testing", "extensions/slack/src/monitor/message-handler/prepare-thread-context-root.ts: SlackThreadRootCandidate", - "extensions/slack/src/monitor/message-handler/prepare.test-helpers.ts: createSlackSessionStoreFixture", - "extensions/slack/src/monitor/message-handler/prepare.test-helpers.ts: createSlackTestAccount", "extensions/slack/src/monitor/message-handler/preview-finalize.ts: __testing", "extensions/slack/src/monitor/message-handler/preview-finalize.ts: testing", "extensions/slack/src/monitor/message-handler/subteam-mentions.ts: clearSlackSubteamMentionCacheForTest", @@ -1818,7 +1814,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "extensions/zalouser/src/zca-client.ts: Style", "extensions/zalouser/src/zca-client.ts: ThreadType", "src/acp/control-plane/active-turns.ts: resetAcpActiveTurnsForTests", - "src/acp/runtime/adapter-contract.testkit.ts: AcpRuntimeAdapterContractParams", "src/acp/runtime/registry.ts: AcpRuntimeBackend", "src/acp/runtime/session-meta.ts: resolveSessionStorePathForAcp", "src/agents/agent-bundle-lsp-runtime.ts: spawnLspServerProcess", @@ -1858,7 +1853,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/agents/auth-profiles/runtime-snapshots.ts: getRuntimeAuthProfileStoreCredentialMutationRevision", "src/agents/auth-profiles/runtime-snapshots.ts: getRuntimeAuthProfileStoreStateMutationRevision", "src/agents/auth-profiles/runtime-snapshots.ts: testing", - "src/agents/auth-profiles/state.ts: savePersistedAuthProfileState", "src/agents/auth-profiles/store.ts: AuthProfileStorePersistenceSnapshot", "src/agents/auth-profiles/store.ts: CommittedAuthProfileStoreSave", "src/agents/auth-profiles/store.ts: testing", @@ -2075,11 +2069,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/agents/live-auth-keys.ts: isAnthropicBillingError", "src/agents/live-model-switch.ts: hasDifferentLiveSessionModelSelection", "src/agents/live-model-switch.ts: resolveLiveSessionModelSelection", - "src/agents/live-test-helpers.ts: CompleteSimpleContent", - "src/agents/live-test-helpers.ts: completeSimpleWithTimeout", - "src/agents/live-test-helpers.ts: logLiveProgress", - "src/agents/live-test-helpers.ts: requiresLiveProfileCredential", - "src/agents/live-test-helpers.ts: resolveLiveCredentialPrecedence", "src/agents/mcp-ui-resource.ts: MCP_APP_RESOURCE_MAX_BYTES", "src/agents/mcp-ui-resource.ts: MCP_APP_RESOURCE_MIME_TYPE", "src/agents/mcp-ui-resource.ts: testing", @@ -2293,7 +2282,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/agents/system-prompt.ts: buildAgentBootstrapSystemContext", "src/agents/system-prompt.ts: buildAgentBootstrapSystemPromptSections", "src/agents/system-prompt.ts: buildRuntimeLine", - "src/agents/test-helpers/agent-message-fixtures.ts: castAgentMessages", "src/agents/tool-call-id.ts: isValidCloudCodeAssistToolId", "src/agents/tool-call-id.ts: sanitizeToolCallId", "src/agents/tool-loop-detection.ts: CRITICAL_THRESHOLD", @@ -2411,10 +2399,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/auto-reply/reply/commands-subagents-dispatch.ts: COMMAND", "src/auto-reply/reply/commands-subagents/shared.ts: COMMAND", "src/auto-reply/reply/commands-tasks.ts: buildTasksReply", - "src/auto-reply/reply/commands.test-harness.ts: baseCommandTestConfig", - "src/auto-reply/reply/commands.test-harness.ts: buildPluginsCommandParams", - "src/auto-reply/reply/commands.test-harness.ts: ConfigSnapshotMock", - "src/auto-reply/reply/commands.test-harness.ts: configureInMemoryTaskRegistryStoreForTests", "src/auto-reply/reply/completion-delivery-policy.ts: resolveCompletionChatType", "src/auto-reply/reply/delivery-hints.ts: MESSAGE_TOOL_ONLY_DELIVERY_HINT", "src/auto-reply/reply/delivery-hints.ts: ROOM_EVENT_DELIVERY_HINT", @@ -2846,7 +2830,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/cli/startup-metadata.ts: testing", "src/cli/startup-trace.ts: GatewayStartupTraceSource", "src/cli/tagline.ts: DEFAULT_TAGLINE", - "src/cli/test-runtime-capture.ts: mockRuntimeModule", "src/cli/update-cli/plugin-payload-validation.ts: PluginPayloadSmokeFailureReason", "src/cli/update-cli/plugin-payload-validation.ts: PluginPayloadSmokeResult", "src/cli/update-cli/progress.ts: inferUpdateFailureHints", @@ -3047,7 +3030,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/config/commands.ts: isCommandFlagEnabled", "src/config/commands.ts: isRestartEnabled", "src/config/cron-limits.ts: DEFAULT_CRON_TRIGGER_MIN_INTERVAL_MS", - "src/config/env-vars.ts: applyConfigEnvVars", "src/config/env-vars.ts: collectDurableServiceEnvVars", "src/config/env-vars.ts: readStateDirDotEnvVars", "src/config/includes.ts: deepMerge", @@ -3155,7 +3137,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/config/sessions/store-writer-state.ts: SessionStoreWriterQueue", "src/config/sessions/store-writer.ts: RunExclusiveSessionStoreWriteOptions", "src/config/sessions/store.ts: applySessionEntryLifecycleMutation", - "src/config/sessions/store.ts: applySessionStoreEntryPatch", "src/config/sessions/store.ts: archiveRemovedSessionTranscripts", "src/config/sessions/store.ts: capEntryCount", "src/config/sessions/store.ts: cleanupSessionLifecycleArtifacts", @@ -3494,7 +3475,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/gateway/handshake-timeouts.ts: clampConnectChallengeTimeoutMs", "src/gateway/handshake-timeouts.ts: DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS", "src/gateway/handshake-timeouts.ts: getConnectChallengeTimeoutMsFromEnv", - "src/gateway/handshake-timeouts.ts: getPreauthHandshakeTimeoutMsFromEnv", "src/gateway/handshake-timeouts.ts: MAX_CONNECT_CHALLENGE_TIMEOUT_MS", "src/gateway/handshake-timeouts.ts: MIN_CONNECT_CHALLENGE_TIMEOUT_MS", "src/gateway/handshake-timeouts.ts: resolveConnectChallengeTimeoutMs", @@ -3634,7 +3614,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/gateway/server-shared-auth-generation.ts: claimSharedGatewaySessionGeneration", "src/gateway/server-shared-auth-generation.ts: replaceSharedGatewaySessionGenerationState", "src/gateway/server-shared-auth-generation.ts: setCurrentSharedGatewaySessionGeneration", - "src/gateway/server-shared-auth-generation.ts: setRequiredSharedGatewaySessionGeneration", "src/gateway/server-startup-outcomes.ts: GATEWAY_STARTUP_SUBSYSTEMS", "src/gateway/server-startup-outcomes.ts: GatewayStartupOutcome", "src/gateway/server-startup-outcomes.ts: GatewayStartupOutcomeRecorderParams", @@ -3944,11 +3923,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/infra/outbound/delivery-queue-recovery.ts: PendingDeliveryDrainDecision", "src/infra/outbound/delivery-queue-recovery.ts: RecoverySummary", "src/infra/outbound/delivery-queue-storage.ts: FailPendingDeliveryResult", - "src/infra/outbound/delivery-queue.test-helpers.ts: asDeliverFn", - "src/infra/outbound/delivery-queue.test-helpers.ts: createRecoveryLog", - "src/infra/outbound/delivery-queue.test-helpers.ts: installDeliveryQueueTmpDirHooks", - "src/infra/outbound/delivery-queue.test-helpers.ts: readQueuedEntry", - "src/infra/outbound/delivery-queue.test-helpers.ts: setQueuedEntryState", "src/infra/outbound/delivery-queue.ts: ActiveDeliveryClaimResult", "src/infra/outbound/delivery-queue.ts: computeBackoffMs", "src/infra/outbound/delivery-queue.ts: failPendingDelivery", @@ -4069,7 +4043,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/infra/restart.ts: __testing", "src/infra/restart.ts: DEFAULT_RESTART_DEFERRAL_TIMEOUT_MS", "src/infra/restart.ts: emitGatewayRestart", - "src/infra/restart.ts: emitGatewayRestartWithSignalAdmission", "src/infra/restart.ts: findGatewayPidsOnPortSync", "src/infra/restart.ts: GatewayRestartEmitResult", "src/infra/restart.ts: RestartAttempt", @@ -4241,9 +4214,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/model-catalog/provider-index/index.ts: OpenClawProviderIndexPluginInstall", "src/model-catalog/provider-index/index.ts: OpenClawProviderIndexProviderAuthChoice", "src/music-generation/capabilities.ts: resolveMusicGenerationMode", - "src/music-generation/live-test-helpers.ts: parseCsvFilter", - "src/music-generation/live-test-helpers.ts: parseProviderModelMap", - "src/music-generation/live-test-helpers.ts: redactLiveApiKey", "src/music-generation/runtime-types.ts: ListRuntimeMusicGenerationProvidersParams", "src/music-generation/runtime-types.ts: RuntimeMusicGenerationProvider", "src/music-generation/runtime.ts: GenerateMusicParams", @@ -4266,6 +4236,9 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/node-host/runner.ts: resolveNodeHostGatewayDeviceFamily", "src/node-host/runner.ts: resolveNodeHostGatewayPlatform", "src/node-host/runner.ts: shouldExitNodeHostOnReconnectPaused", + "src/pairing/pairing-store-sqlite.ts: ChannelPairingState", + "src/pairing/pairing-store-sqlite.ts: readChannelPairingStateSnapshot", + "src/pairing/pairing-store-sqlite.ts: writeChannelPairingStateSnapshot", "src/pairing/pairing-store.types.ts: ReadChannelAllowFromStoreForAccount", "src/pairing/pairing-store.types.ts: UpsertChannelPairingRequestForAccount", "src/pairing/setup-code.ts: PairingSetupCommandResult", @@ -4273,8 +4246,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/pairing/setup-code.ts: PairingSetupPayload", "src/pairing/setup-code.ts: PairingSetupResolution", "src/pairing/setup-code.ts: ResolvePairingSetupOptions", - "src/plugin-sdk/test-helpers/image-fixtures.ts: createTinyJpegBuffer", - "src/plugin-sdk/test-helpers/provider-auth-contract.ts: OpenAICodexProviderAuthContractOptions", "src/plugin-state/plugin-state-store.sqlite.ts: probePluginStateStore", "src/plugin-state/plugin-state-store.sqlite.ts: seedPluginStateDatabaseEntriesForTests", "src/plugin-state/plugin-state-store.sqlite.ts: setMaxPluginStateEntriesPerPluginForTests", @@ -4393,9 +4364,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/plugins/hook-decision-types.ts: mergeHookDecisions", "src/plugins/hook-registry.types.ts: PluginLegacyHookRegistration", "src/plugins/hook-runner-global-state.ts: HookRunnerGlobalState", - "src/plugins/hooks.test-helpers.ts: addStaticTestHooks", - "src/plugins/hooks.test-helpers.ts: createHookRunnerWithRegistry", - "src/plugins/hooks.test-helpers.ts: TEST_PLUGIN_AGENT_CTX", "src/plugins/hooks.ts: HookFailurePolicy", "src/plugins/hooks.ts: HookRunnerLogger", "src/plugins/hooks.ts: HookRunnerOptions", @@ -4674,7 +4642,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/plugins/provider-oauth-flow.ts: OAuthPrompt", "src/plugins/provider-openai-chatgpt-oauth-tls.ts: OpenAIOAuthTlsPreflightResult", "src/plugins/provider-runtime.runtime.ts: buildProviderMissingAuthMessageWithPlugin", - "src/plugins/provider-runtime.test-support.ts: expectedAugmentedOpenaiCodexCatalogEntries", "src/plugins/provider-thinking.types.ts: ProviderThinkingLevel", "src/plugins/provider-thinking.types.ts: ProviderThinkingLevelId", "src/plugins/provider-thinking.types.ts: ProviderThinkingModelCompat", @@ -4825,15 +4792,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/plugins/source-display.ts: PluginSourceRoots", "src/plugins/source-display.ts: resolvePluginSourceRoots", "src/plugins/status-dependencies-core.ts: PluginDependencyEntry", - "src/plugins/status.test-helpers.ts: createBundledPluginRecord", - "src/plugins/status.test-helpers.ts: createCompatibilityNotice", - "src/plugins/status.test-helpers.ts: createCustomHook", - "src/plugins/status.test-helpers.ts: createPluginLoadResult", - "src/plugins/status.test-helpers.ts: createTypedHook", - "src/plugins/status.test-helpers.ts: DEPRECATED_MEMORY_EMBEDDING_PROVIDER_API_MESSAGE", - "src/plugins/status.test-helpers.ts: HOOK_ONLY_MESSAGE", - "src/plugins/status.test-helpers.ts: LEGACY_BEFORE_AGENT_START_MESSAGE", - "src/plugins/status.test-helpers.ts: REMOVED_SESSION_TRANSCRIPT_FILE_API_MESSAGE", "src/plugins/tools.ts: PluginToolMeta", "src/plugins/tools.ts: resetPluginToolDescriptorCache", "src/plugins/tools.ts: resetPluginToolFactoryCache", @@ -5074,7 +5032,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/skills/runtime/session-snapshot.ts: resetResolvedSkillsCacheForTests", "src/skills/runtime/session-snapshot.ts: ReusableSkillSnapshotParams", "src/skills/runtime/session-snapshot.ts: ReusableSkillSnapshotResult", - "src/skills/test-support/e2e-test-helpers.ts: writeWorkspaceSkills", "src/skills/types.ts: SkillCommandDispatchSpec", "src/skills/types.ts: SkillExposure", "src/skills/workshop/curator.ts: ARCHIVE_AFTER_MS", @@ -5162,23 +5119,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/tasks/task-registry.ts: resetTaskRegistryForTests", "src/tasks/task-registry.ts: setTaskRegistryControlRuntimeForTests", "src/tasks/task-registry.ts: setTaskRegistryDeliveryRuntimeForTests", - "src/test-helpers/state-dir-env.ts: restoreStateDirEnv", - "src/test-helpers/state-dir-env.ts: setStateDirEnv", - "src/test-helpers/state-dir-env.ts: snapshotStateDirEnv", - "src/test-utils/channel-plugins.ts: BindingResolverTestPlugin", - "src/test-utils/channel-plugins.ts: createBindingResolverTestPlugin", - "src/test-utils/channel-plugins.ts: createChannelTestPluginBase", - "src/test-utils/channel-plugins.ts: createDirectOutboundTestAdapter", - "src/test-utils/channel-plugins.ts: TestChannelRegistration", - "src/test-utils/env.ts: captureFullEnv", - "src/test-utils/env.ts: createPathResolutionEnv", - "src/test-utils/env.ts: withPathResolutionEnv", - "src/test-utils/plugin-registration.ts: createCapturedPluginRegistration", - "src/test-utils/provider-usage-fetch.ts: toRequestUrl", - "src/test-utils/session-state-cleanup.ts: resetSessionStateCleanupRuntimeForTests", - "src/test-utils/session-state-cleanup.ts: setSessionStateCleanupRuntimeForTests", - "src/test-utils/vitest-spies.ts: mockProcessPlatform", - "src/test-utils/vitest-spies.ts: RestorableMock", "src/tools/diagnostics.ts: ToolPlanContractErrorCode", "src/tools/protocol.ts: ToolProtocolDescriptor", "src/trajectory/paths.ts: resolveTrajectoryPointerOpenFlags", @@ -5221,9 +5161,6 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [ "src/utils/message-channel.ts: NativeApprovalChannel", "src/utils/queue-helpers.ts: clearQueueSummaryState", "src/utils/timer-delay.ts: resolveFiniteTimeoutDelayMs", - "src/video-generation/live-test-helpers.ts: parseCsvFilter", - "src/video-generation/live-test-helpers.ts: parseProviderModelMap", - "src/video-generation/live-test-helpers.ts: redactLiveApiKey", "src/video-generation/runtime-types.ts: ListRuntimeVideoGenerationProvidersParams", "src/video-generation/runtime-types.ts: RuntimeVideoGenerationProvider", "src/web-search/runtime-types.ts: ListWebSearchProvidersParams", diff --git a/scripts/lib/plugin-sdk-declaration-budget.d.mts b/scripts/lib/plugin-sdk-declaration-budget.d.mts index adc3356e7345..bde50e334fdc 100644 --- a/scripts/lib/plugin-sdk-declaration-budget.d.mts +++ b/scripts/lib/plugin-sdk-declaration-budget.d.mts @@ -10,5 +10,5 @@ export function evaluatePluginSdkDeclarationBudget({ budgetKind: string; shouldFail: boolean; }; -export const MAX_PUBLIC_PLUGIN_SDK_DECLARATION_BYTES: 5100000; -export const MAX_PRIVATE_QA_PUBLIC_PLUGIN_SDK_DECLARATION_BYTES: 5125000; +export const MAX_PUBLIC_PLUGIN_SDK_DECLARATION_BYTES: 5200000; +export const MAX_PRIVATE_QA_PUBLIC_PLUGIN_SDK_DECLARATION_BYTES: 5225000; diff --git a/scripts/lib/plugin-sdk-declaration-budget.mjs b/scripts/lib/plugin-sdk-declaration-budget.mjs index e39f7eb2e677..259291a0f150 100644 --- a/scripts/lib/plugin-sdk-declaration-budget.mjs +++ b/scripts/lib/plugin-sdk-declaration-budget.mjs @@ -1,9 +1,9 @@ -// Raised for the plugins.uninstall/catalog actions and sessions.search protocol surfaces; +// Raised for the plugins.uninstall/catalog actions and current session/plugin protocol surfaces; // the cap exists to force a conscious decision on published declaration growth. -export const MAX_PUBLIC_PLUGIN_SDK_DECLARATION_BYTES = 5_100_000; +export const MAX_PUBLIC_PLUGIN_SDK_DECLARATION_BYTES = 5_200_000; // Private-only entrypoints reshape chunks reachable from public roots but are never published. // Bound that topology overhead without counting local-only declarations as package surface. -export const MAX_PRIVATE_QA_PUBLIC_PLUGIN_SDK_DECLARATION_BYTES = 5_125_000; +export const MAX_PRIVATE_QA_PUBLIC_PLUGIN_SDK_DECLARATION_BYTES = 5_225_000; export function isPrivateQaPluginSdkBuild(env) { return env.OPENCLAW_BUILD_PRIVATE_QA === "1"; diff --git a/scripts/lib/session-accessor-debt-baseline.json b/scripts/lib/session-accessor-debt-baseline.json index 6321303d3aaf..072b5857a0da 100644 --- a/scripts/lib/session-accessor-debt-baseline.json +++ b/scripts/lib/session-accessor-debt-baseline.json @@ -12,7 +12,7 @@ "src/config/sessions/session-accessor.sqlite.ts": 2, "src/config/sessions/session-accessor.ts": 12, "src/config/sessions/store-load.ts": 5, - "src/config/sessions/store.ts": 16, + "src/config/sessions/store.ts": 15, "src/config/sessions/test-helpers.ts": 2, "src/config/sessions/transcript.ts": 2 }, diff --git a/src/agents/auth-profiles/state.ts b/src/agents/auth-profiles/state.ts index 3ed1182d4b62..42153e2a238a 100644 --- a/src/agents/auth-profiles/state.ts +++ b/src/agents/auth-profiles/state.ts @@ -3,7 +3,6 @@ * This state tracks order, last-good profile, and cooldown/failure metadata * separately from secret-bearing credentials. */ -import { isDeepStrictEqual } from "node:util"; import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id"; import { asFiniteNumber } from "@openclaw/normalization-core/number-coercion"; import { isRecord } from "@openclaw/normalization-core/record-coerce"; @@ -11,7 +10,7 @@ import { normalizeOptionalString } from "@openclaw/normalization-core/string-coe import { normalizeTrimmedStringList } from "@openclaw/normalization-core/string-normalization"; import type { OpenClawAgentDatabase } from "../../state/openclaw-agent-db.js"; import { AUTH_STORE_VERSION } from "./constants.js"; -import { readPersistedAuthProfileStateRaw, writePersistedAuthProfileStateRaw } from "./sqlite.js"; +import { readPersistedAuthProfileStateRaw } from "./sqlite.js"; import type { AuthProfileBlockedReason, AuthProfileBlockedSource, @@ -212,16 +211,3 @@ export function buildPersistedAuthProfileState( ...(state.usageStats ? { usageStats: state.usageStats } : {}), }; } - -/** Saves auth profile runtime state when it differs from the persisted payload. */ -export function savePersistedAuthProfileState( - store: AuthProfileState, - agentDir?: string, -): AuthProfileStateStore | null { - const payload = buildPersistedAuthProfileState(store); - const existingRaw = readPersistedAuthProfileStateRaw(agentDir); - if (!payload || !isDeepStrictEqual(existingRaw, payload)) { - writePersistedAuthProfileStateRaw(payload, agentDir); - } - return payload; -} diff --git a/src/config/env-vars.ts b/src/config/env-vars.ts index 1ca68a40da2a..eea5904a167e 100644 --- a/src/config/env-vars.ts +++ b/src/config/env-vars.ts @@ -1,6 +1,5 @@ // Public facade for config env var collection and durable state-dir dotenv reads. export { - applyConfigEnvVars, cloneEnvWithPlatformSemantics, collectConfigRuntimeEnvVars, createConfigRuntimeEnv, diff --git a/src/config/sessions.cache.test.ts b/src/config/sessions.cache.test.ts index 8c4e2a169abc..63f56a44a0e6 100644 --- a/src/config/sessions.cache.test.ts +++ b/src/config/sessions.cache.test.ts @@ -18,7 +18,6 @@ import { writeSessionStoreCache, } from "./sessions/store-cache.js"; import { - applySessionStoreEntryPatch, clearSessionStoreCacheForTest, loadSessionStore, readSessionEntries, @@ -846,29 +845,6 @@ describe("Session Store Cache", () => { ); }); - it("detaches caller-owned patch objects before publishing writer-owned caches", async () => { - await saveSessionStore(storePath, { - "session:1": createSessionEntry({ sessionId: "id-1" }), - "session:2": createSessionEntry({ sessionId: "id-2" }), - }); - const before = loadSessionStore(storePath, { clone: false }); - const untouched = before["session:2"]; - const deliveryContext = { channel: "telegram", to: "chat-1" }; - - await applySessionStoreEntryPatch({ - storePath, - sessionKey: "session:1", - patch: { deliveryContext }, - }); - deliveryContext.to = "mutated-after-persist"; - - const cached = loadSessionStore(storePath, { clone: false }); - expect(cached["session:2"]).toBe(untouched); - expect( - expectDefined(cached["session:1"], 'cached["session:1"] test invariant').deliveryContext?.to, - ).toBe("chat-1"); - }); - it("falls back to full projection when untouched entries need prompt blob repair", async () => { const prompt = "skill prompt ".repeat(80); await saveSessionStore(storePath, { diff --git a/src/config/sessions.test.ts b/src/config/sessions.test.ts index dc9442b8c4b0..9b6230cdec62 100644 --- a/src/config/sessions.test.ts +++ b/src/config/sessions.test.ts @@ -7,7 +7,6 @@ import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { createDeferred } from "../test-utils/deferred.js"; import { withEnv } from "../test-utils/env.js"; import { - applySessionStoreEntryPatch, buildGroupDisplayName, deriveSessionKey, loadSessionStore, @@ -537,34 +536,6 @@ describe("sessions", () => { expect(store[sessionKey]?.reasoningLevel).toBe("on"); }); - it("applySessionStoreEntryPatch applies a precomputed patch without a callback", async () => { - const sessionKey = "agent:main:main"; - const { storePath } = await createSessionStoreFixture({ - prefix: "applySessionStoreEntryPatch", - entries: { - [sessionKey]: { - sessionId: "sess-1", - updatedAt: 100, - reasoningLevel: "on", - }, - }, - }); - - const result = await applySessionStoreEntryPatch({ - storePath, - sessionKey, - patch: { - updatedAt: 200, - thinkingLevel: "high", - }, - }); - - expect(result?.thinkingLevel).toBe("high"); - const store = loadSessionStore(storePath); - expect(store[sessionKey]?.updatedAt).toBeGreaterThanOrEqual(200); - expect(store[sessionKey]?.reasoningLevel).toBe("on"); - }); - it("updateSessionStoreEntry returns null when session key does not exist", async () => { const { storePath } = await createSessionStoreFixture({ prefix: "updateSessionStoreEntry-missing", diff --git a/src/config/sessions/store.ts b/src/config/sessions/store.ts index 6af4dda56170..b0edda2d101a 100644 --- a/src/config/sessions/store.ts +++ b/src/config/sessions/store.ts @@ -1882,34 +1882,6 @@ export async function updateSessionStoreEntry(params: { }); } -export async function applySessionStoreEntryPatch(params: { - storePath: string; - sessionKey: string; - patch: Partial; - skipMaintenance?: boolean; - takeCacheOwnership?: boolean; -}): Promise { - const { storePath, sessionKey, patch } = params; - return await runExclusiveSessionStoreWrite(storePath, async () => { - const store = loadMutableSessionStoreForWriter(storePath); - const resolved = resolveSessionStoreEntry({ store, sessionKey }); - const existing = resolved.existing; - if (!existing) { - return null; - } - const next = mergeSessionEntry(existing, patch); - return await persistResolvedSessionEntry({ - storePath, - store, - resolved, - next, - skipMaintenance: params.skipMaintenance, - takeCacheOwnership: params.takeCacheOwnership ?? true, - returnDetached: params.takeCacheOwnership !== true, - }); - }); -} - type SessionEntryPatchParams = SessionEntryWorkflowOptions & { sessionKey: string; fallbackEntry?: SessionEntry; diff --git a/src/gateway/handshake-timeouts.test.ts b/src/gateway/handshake-timeouts.test.ts index 829092439830..c4b8503928e8 100644 --- a/src/gateway/handshake-timeouts.test.ts +++ b/src/gateway/handshake-timeouts.test.ts @@ -6,7 +6,6 @@ import { clampConnectChallengeTimeoutMs, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, getConnectChallengeTimeoutMsFromEnv, - getPreauthHandshakeTimeoutMsFromEnv, MAX_CONNECT_CHALLENGE_TIMEOUT_MS, MIN_CONNECT_CHALLENGE_TIMEOUT_MS, resolveConnectChallengeTimeoutMs, @@ -27,23 +26,29 @@ describe("gateway handshake timeouts", () => { test("prefers OPENCLAW_HANDSHAKE_TIMEOUT_MS and falls back on the test-only env", () => { expect( - getPreauthHandshakeTimeoutMsFromEnv({ - OPENCLAW_HANDSHAKE_TIMEOUT_MS: "75", - OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS: "20", + resolvePreauthHandshakeTimeoutMs({ + env: { + OPENCLAW_HANDSHAKE_TIMEOUT_MS: "75", + OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS: "20", + }, }), ).toBe(75); expect( - getPreauthHandshakeTimeoutMsFromEnv({ - OPENCLAW_HANDSHAKE_TIMEOUT_MS: "", - OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS: "20", - VITEST: "1", + resolvePreauthHandshakeTimeoutMs({ + env: { + OPENCLAW_HANDSHAKE_TIMEOUT_MS: "", + OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS: "20", + VITEST: "1", + }, }), ).toBe(20); expect( - getPreauthHandshakeTimeoutMsFromEnv({ - OPENCLAW_HANDSHAKE_TIMEOUT_MS: " +75 ", - OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS: "20", - VITEST: "1", + resolvePreauthHandshakeTimeoutMs({ + env: { + OPENCLAW_HANDSHAKE_TIMEOUT_MS: " +75 ", + OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS: "20", + VITEST: "1", + }, }), ).toBe(75); }); @@ -74,8 +79,8 @@ describe("gateway handshake timeouts", () => { test("caps preauth handshake timeout env and config values to the safe timer range", () => { expect( - getPreauthHandshakeTimeoutMsFromEnv({ - OPENCLAW_HANDSHAKE_TIMEOUT_MS: "3000000000", + resolvePreauthHandshakeTimeoutMs({ + env: { OPENCLAW_HANDSHAKE_TIMEOUT_MS: "3000000000" }, }), ).toBe(MAX_SAFE_TIMEOUT_DELAY_MS); expect( @@ -97,36 +102,28 @@ describe("gateway handshake timeouts", () => { test("ignores invalid handshake timeout overrides and falls back safely", () => { expect( - getPreauthHandshakeTimeoutMsFromEnv({ - OPENCLAW_HANDSHAKE_TIMEOUT_MS: "abc", + resolvePreauthHandshakeTimeoutMs({ env: { OPENCLAW_HANDSHAKE_TIMEOUT_MS: "abc" } }), + ).toBe(DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS); + expect(resolvePreauthHandshakeTimeoutMs({ env: { OPENCLAW_HANDSHAKE_TIMEOUT_MS: "-1" } })).toBe( + DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, + ); + expect(resolvePreauthHandshakeTimeoutMs({ env: { OPENCLAW_HANDSHAKE_TIMEOUT_MS: "0" } })).toBe( + DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, + ); + expect( + resolvePreauthHandshakeTimeoutMs({ + env: { + OPENCLAW_HANDSHAKE_TIMEOUT_MS: " ", + OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS: "20", + VITEST: "1", + }, }), ).toBe(DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS); expect( - getPreauthHandshakeTimeoutMsFromEnv({ - OPENCLAW_HANDSHAKE_TIMEOUT_MS: "-1", - }), + resolvePreauthHandshakeTimeoutMs({ env: { OPENCLAW_HANDSHAKE_TIMEOUT_MS: "1e3" } }), ).toBe(DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS); expect( - getPreauthHandshakeTimeoutMsFromEnv({ - OPENCLAW_HANDSHAKE_TIMEOUT_MS: "0", - }), - ).toBe(DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS); - expect( - getPreauthHandshakeTimeoutMsFromEnv({ - OPENCLAW_HANDSHAKE_TIMEOUT_MS: " ", - OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS: "20", - VITEST: "1", - }), - ).toBe(DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS); - expect( - getPreauthHandshakeTimeoutMsFromEnv({ - OPENCLAW_HANDSHAKE_TIMEOUT_MS: "1e3", - }), - ).toBe(DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS); - expect( - getPreauthHandshakeTimeoutMsFromEnv({ - OPENCLAW_HANDSHAKE_TIMEOUT_MS: "0x10", - }), + resolvePreauthHandshakeTimeoutMs({ env: { OPENCLAW_HANDSHAKE_TIMEOUT_MS: "0x10" } }), ).toBe(DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS); }); diff --git a/src/gateway/handshake-timeouts.ts b/src/gateway/handshake-timeouts.ts index de3f109b99f8..81aa86cb54ad 100644 --- a/src/gateway/handshake-timeouts.ts +++ b/src/gateway/handshake-timeouts.ts @@ -4,7 +4,6 @@ export { clampConnectChallengeTimeoutMs, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, getConnectChallengeTimeoutMsFromEnv, - getPreauthHandshakeTimeoutMsFromEnv, MAX_CONNECT_CHALLENGE_TIMEOUT_MS, MIN_CONNECT_CHALLENGE_TIMEOUT_MS, resolveConnectChallengeTimeoutMs, diff --git a/src/gateway/server-shared-auth-generation.ts b/src/gateway/server-shared-auth-generation.ts index c8ff7e67576c..bb789d3d699e 100644 --- a/src/gateway/server-shared-auth-generation.ts +++ b/src/gateway/server-shared-auth-generation.ts @@ -170,7 +170,7 @@ export function restoreOwnedCurrentSharedGatewaySessionGeneration( } /** Update the required marker as one ownership-changing mutation. */ -export function setRequiredSharedGatewaySessionGeneration( +function setRequiredSharedGatewaySessionGeneration( state: SharedGatewaySessionGenerationState, required: string | undefined | null, ): void { diff --git a/src/gateway/server.auth.default-token.suite.ts b/src/gateway/server.auth.default-token.suite.ts index 91aa6e969bae..d4f39507372b 100644 --- a/src/gateway/server.auth.default-token.suite.ts +++ b/src/gateway/server.auth.default-token.suite.ts @@ -12,7 +12,6 @@ import { createSignedDevice, expectHelloOkServerVersion, getFreePort, - getPreauthHandshakeTimeoutMsFromEnv, GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES, MIN_PROBE_PROTOCOL_VERSION, @@ -22,6 +21,7 @@ import { PROTOCOL_VERSION, readConnectChallengeNonce, resolveGatewayTokenOrEnv, + resolvePreauthHandshakeTimeoutMs, rpcReq, sendRawConnectReq, startGatewayServer, @@ -113,7 +113,7 @@ export function registerDefaultAuthTokenSuite(): void { try { await withGatewayServer(async ({ port: isolatedPort }) => { const ws = await openWs(isolatedPort); - const handshakeTimeoutMs = getPreauthHandshakeTimeoutMsFromEnv(); + const handshakeTimeoutMs = resolvePreauthHandshakeTimeoutMs(); const closed = await waitForWsClose(ws, handshakeTimeoutMs + 10_000); expect(closed).toBe(true); }); @@ -132,9 +132,9 @@ export function registerDefaultAuthTokenSuite(): void { process.env.OPENCLAW_HANDSHAKE_TIMEOUT_MS = "75"; process.env.OPENCLAW_TEST_HANDSHAKE_TIMEOUT_MS = "20"; try { - expect(getPreauthHandshakeTimeoutMsFromEnv()).toBe(75); + expect(resolvePreauthHandshakeTimeoutMs()).toBe(75); process.env.OPENCLAW_HANDSHAKE_TIMEOUT_MS = ""; - expect(getPreauthHandshakeTimeoutMsFromEnv()).toBe(20); + expect(resolvePreauthHandshakeTimeoutMs()).toBe(20); } finally { if (prevHandshakeTimeout === undefined) { delete process.env.OPENCLAW_HANDSHAKE_TIMEOUT_MS; diff --git a/src/gateway/server.auth.test-helpers.ts b/src/gateway/server.auth.test-helpers.ts index da88570e41d3..20aea15d308a 100644 --- a/src/gateway/server.auth.test-helpers.ts +++ b/src/gateway/server.auth.test-helpers.ts @@ -430,6 +430,6 @@ export { writeTrustedProxyControlUiConfig, }; export { ConnectErrorDetailCodes } from "../../packages/gateway-protocol/src/connect-error-details.js"; -export { getPreauthHandshakeTimeoutMsFromEnv } from "./handshake-timeouts.js"; +export { resolvePreauthHandshakeTimeoutMs } from "./handshake-timeouts.js"; export { PROTOCOL_VERSION } from "../../packages/gateway-protocol/src/index.js"; export { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js"; diff --git a/src/infra/restart.ts b/src/infra/restart.ts index b47c2498ae95..5285c57d2123 100644 --- a/src/infra/restart.ts +++ b/src/infra/restart.ts @@ -434,7 +434,7 @@ export function emitGatewayRestart( * The caller must already own root-work admission. Scheduled restarts use the * independent-root wrapper below; config reloads run inside their reload root. */ -export function emitGatewayRestartWithSignalAdmission( +function emitGatewayRestartWithSignalAdmission( reasonOverride?: string, intent?: GatewayRestartIntent, ): boolean { diff --git a/test/scripts/check-deadcode-exports.test.ts b/test/scripts/check-deadcode-exports.test.ts index 16988a482469..d5e2a988f3ca 100644 --- a/test/scripts/check-deadcode-exports.test.ts +++ b/test/scripts/check-deadcode-exports.test.ts @@ -1,5 +1,6 @@ // Check Deadcode Exports tests cover parsing, ratcheting, and baseline emission. import { describe, expect, it } from "vitest"; +import knipConfig from "../../config/knip.config.ts"; import { compareUnusedExportsToBaseline, formatUnusedExportBaseline, @@ -8,6 +9,15 @@ import { } from "../../scripts/check-deadcode-exports.mjs"; describe("check-deadcode-exports", () => { + it("excludes test support from every Knip issue type", () => { + expect(knipConfig.ignore).toContain("**/test-helpers/**"); + expect(knipConfig.ignore).toContain("**/*.test-utils.ts"); + expect(knipConfig.ignoreFiles).not.toContain("**/test-helpers/**"); + expect(knipConfig.ignoreFiles).toContain("scripts/**"); + expect(knipConfig.ignore).not.toContain("**/live-*.ts"); + expect(knipConfig.ignoreFiles).toContain("**/live-*.ts"); + }); + it("parses all compact export sections and expands symbol lists", () => { expect( parseKnipCompactUnusedExports(` diff --git a/test/scripts/ci-workflow-guards.test.ts b/test/scripts/ci-workflow-guards.test.ts index 6ef038e55dd8..8ec5cb659a52 100644 --- a/test/scripts/ci-workflow-guards.test.ts +++ b/test/scripts/ci-workflow-guards.test.ts @@ -1752,7 +1752,22 @@ describe("ci workflow guards", () => { scripts: ["deadcode:dependencies", "deadcode:unused-files", "deadcode:exports"], }); expect(modern.status, modern.output).toBe(0); - expect(modern.calls).toEqual(["deadcode:dependencies", "deadcode:unused-files"]); + expect(modern.calls).toEqual([ + "deadcode:dependencies", + "deadcode:unused-files", + "deadcode:exports", + ]); + + const frozenWithExports = runDependencyCheckFixture({ + historicalTarget: true, + scripts: ["deadcode:dependencies", "deadcode:unused-files", "deadcode:exports"], + }); + expect(frozenWithExports.status, frozenWithExports.output).toBe(0); + expect(frozenWithExports.calls).toEqual([ + "deadcode:dependencies", + "deadcode:unused-files", + "deadcode:exports", + ]); const frozen = runDependencyCheckFixture({ historicalTarget: true, @@ -1766,6 +1781,16 @@ describe("ci workflow guards", () => { expect(frozen.status, frozen.output).toBe(0); expect(frozen.calls).toEqual(["deadcode:dependencies", "deadcode:unused-files"]); + const currentWithoutExports = runDependencyCheckFixture({ + historicalTarget: false, + scripts: ["deadcode:dependencies", "deadcode:unused-files"], + }); + expect(currentWithoutExports.status).toBe(1); + expect(currentWithoutExports.calls).toEqual(["deadcode:dependencies", "deadcode:unused-files"]); + expect(currentWithoutExports.output).toContain( + "Current CI targets must provide the deadcode:exports package script.", + ); + const legacy = runDependencyCheckFixture({ historicalTarget: true, scripts: ["deadcode:ci"], @@ -2067,7 +2092,11 @@ describe("ci workflow guards", () => { expect(checkShard.run).toContain('elif [[ "$HISTORICAL_TARGET" != "true" ]]'); expect(checkShard.run).toContain('has_package_script "deadcode:dependencies"'); expect(checkShard.run).toContain('has_package_script "deadcode:unused-files"'); - expect(checkShard.run).not.toContain('has_package_script "deadcode:exports"'); + expect(checkShard.run).toContain('has_package_script "deadcode:exports"'); + expect(checkShard.run).toContain("pnpm deadcode:exports"); + expect(checkShard.run).toContain( + "Current CI targets must provide the deadcode:exports package script.", + ); expect(checkShard.run).toContain( 'elif [[ "$HISTORICAL_TARGET" == "true" ]] && has_package_script "deadcode:ci"', );