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
This commit is contained in:
Peter Steinberger
2026-07-12 19:58:38 -07:00
committed by GitHub
parent c3dbaf4375
commit dce8eed0b9
20 changed files with 154 additions and 265 deletions
+6
View File
@@ -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
+53 -46
View File
@@ -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,
+1 -1
View File
@@ -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
+3 -66
View File
@@ -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",
@@ -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;
@@ -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";
@@ -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
},
+1 -15
View File
@@ -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;
}
-1
View File
@@ -1,6 +1,5 @@
// Public facade for config env var collection and durable state-dir dotenv reads.
export {
applyConfigEnvVars,
cloneEnvWithPlatformSemantics,
collectConfigRuntimeEnvVars,
createConfigRuntimeEnv,
-24
View File
@@ -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, {
-29
View File
@@ -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",
-28
View File
@@ -1882,34 +1882,6 @@ export async function updateSessionStoreEntry(params: {
});
}
export async function applySessionStoreEntryPatch(params: {
storePath: string;
sessionKey: string;
patch: Partial<SessionEntry>;
skipMaintenance?: boolean;
takeCacheOwnership?: boolean;
}): Promise<SessionEntry | null> {
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;
+36 -39
View File
@@ -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);
});
-1
View File
@@ -4,7 +4,6 @@ export {
clampConnectChallengeTimeoutMs,
DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS,
getConnectChallengeTimeoutMsFromEnv,
getPreauthHandshakeTimeoutMsFromEnv,
MAX_CONNECT_CHALLENGE_TIMEOUT_MS,
MIN_CONNECT_CHALLENGE_TIMEOUT_MS,
resolveConnectChallengeTimeoutMs,
+1 -1
View File
@@ -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 {
@@ -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;
+1 -1
View File
@@ -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";
+1 -1
View File
@@ -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 {
@@ -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(`
+31 -2
View File
@@ -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"',
);