From 6bbc57e52672e0ed311e3bb2fc95ee96739eecf8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 15 Aug 2026 19:41:31 -0700 Subject: [PATCH] =?UTF-8?q?refactor(types):=20drain=20chained-assertion=20?= =?UTF-8?q?ledger=20=E2=80=94=20core=20(#124351)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/oxlint-boundary-guards.mjs | 43 +++++++++++------ .../sessions/session-transcript-index.ts | 47 +++++++++++-------- .../session-transcript-projection-rebuild.ts | 10 ++-- src/media/runtime-prompt-image-provenance.ts | 7 +-- src/process/command-queue.capacity-groups.ts | 4 +- src/runtime.ts | 2 +- src/shared/json-schema-defaults.ts | 27 +++++++---- 7 files changed, 89 insertions(+), 51 deletions(-) diff --git a/scripts/oxlint-boundary-guards.mjs b/scripts/oxlint-boundary-guards.mjs index dfbcc3957519..78e2f61b1883 100644 --- a/scripts/oxlint-boundary-guards.mjs +++ b/scripts/oxlint-boundary-guards.mjs @@ -664,22 +664,37 @@ export default { "extensions/zalo", "extensions/zalouser", "packages/ai", - "src/runtime.ts", - "src/acp", + "src/acp/client.ts", // Node and Web ReadableStream types live in separate namespaces. + "src/acp/server.ts", // Node and Web ReadableStream types live in separate namespaces. "src/agents", - "src/channels", - "src/commands", - "src/config", - "src/gateway", - "src/infra", - "src/media", + "src/channels/plugins/config-schema.ts", // Public SDK Zod generics preserve caller schema identity. + "src/commands/channel-test-registry.ts", // Test support. + "src/commands/doctor/cron/legacy-repair.ts", // Partially validated legacy rows cross the canonical cron store type. + "src/commands/doctor/cron/legacy-store-migration.ts", // Legacy loader carries partial rows in the canonical store envelope. + "src/commands/doctor/cron/warnings.ts", // Doctor inspects partially parsed cron rows. + "src/config/schema.hints.ts", // Zod pipe internals cross its public type namespace. + "src/config/sessions/store-entry-shape.ts", // Legacy projection accepts partially validated session records. + "src/gateway/cli-session-history.claude.ts", // External CLI messages cross the canonical transcript redactor. + "src/gateway/mcp-app-standalone.ts", // Generated standalone browser code bridges the DOM namespace. + "src/gateway/server-methods/chat-transcript-inject.ts", // Gateway media blocks exceed the canonical message content union. + "src/gateway/test-http-response.ts", // Test support. + "src/infra/backup-volatile-stat-cache.ts", // node-tar's cache expects full Stats for a synthetic sentinel. + "src/infra/diagnostic-trace-propagation.ts", // Global symbol registry crosses module copies. + "src/infra/net/runtime-fetch.ts", // Undici and DOM fetch types live in separate namespaces. + "src/infra/state-migrations.meeting-transcripts-files.ts", // Legacy summary validation does not prove element types. + "src/infra/unhandled-rejections.ts", // Global symbol registry crosses module copies. "src/meeting-bot", - "src/plugin-sdk", - "src/plugins", - "src/process", - "src/proxy-capture", - "src/shared", - "src/trajectory", + "src/plugin-sdk/channel-config-helpers.ts", // Public SDK accessor generics are intentionally decoupled. + "src/plugin-sdk/provider-stream-shared.ts", // Untyped normalizer events need a transport stream API redesign. + "src/plugin-sdk/qa-runtime.ts", // Public SDK lazy module exposes a narrower runtime surface. + "src/plugins/hook-isolation.ts", // Optional WebAssembly globals bridge runtime type namespaces. + "src/plugins/interactive.ts", // Dynamic plugin context keys cross the generic handler seam. + "src/plugins/loader-runtime-load.ts", // Discovery-only runtime is widened by the registry proxy. + "src/plugins/registry-runtime.ts", // Bundled owner wrapper crosses the public inbound generic. + "src/plugins/runtime/index.ts", // Lazy assembly adds required runtime capabilities after construction. + "src/process/exec-spawn.ts", // Rebuilt Execa options cross its result generic. + "src/proxy-capture/store.sqlite.ts", // Implementation preserves overloaded shipped constructor contracts. + "src/trajectory/export.ts", // Legacy migration mutates pre-canonical transcript entries. "ui/src", ], }), diff --git a/src/config/sessions/session-transcript-index.ts b/src/config/sessions/session-transcript-index.ts index 8f3ad96b45c1..eed56866894e 100644 --- a/src/config/sessions/session-transcript-index.ts +++ b/src/config/sessions/session-transcript-index.ts @@ -7,6 +7,7 @@ // marks the session dirty for its write or maintenance owner to rebuild from // the canonical visible-path resolver. import type { DatabaseSync } from "node:sqlite"; +import type { ColumnType } from "kysely"; import { executeSqliteQuerySync, executeSqliteQueryTakeFirstSync, @@ -27,14 +28,24 @@ import { isSessionTranscriptSideAppendEntry, parseSessionTranscriptTreeEntry, } from "./transcript-tree.js"; -type TranscriptIndexDatabase = Pick< - OpenClawAgentKyselyDatabase, - | "session_windows" - | "session_transcript_active_events" - | "session_transcript_fts" - | "session_transcript_index_state" - | "transcript_events" ->; +type TranscriptIndexDatabase = Omit< + Pick< + OpenClawAgentKyselyDatabase, + | "session_windows" + | "session_transcript_active_events" + | "session_transcript_fts" + | "session_transcript_index_state" + | "transcript_events" + >, + "session_transcript_fts" +> & { + session_transcript_fts: Omit< + OpenClawAgentKyselyDatabase["session_transcript_fts"], + "timestamp" + > & { + timestamp: ColumnType; + }; +}; export type SessionTranscriptProjectionState = { activeEventCount: number; @@ -158,17 +169,15 @@ function deleteActiveEventRows(db: DatabaseSync, sessionId: string): void { function insertFtsRow(db: DatabaseSync, sessionId: string, entry: TranscriptIndexEntry): void { executeSqliteQuerySync( db, - getIndexKysely(db) - .insertInto("session_transcript_fts") - .values({ - text: entry.text, - session_id: sessionId, - message_id: entry.messageId, - role: entry.role, - // FTS5 aux columns are typeless, so codegen types them as string; - // SQLite stores the numeric timestamp natively and readers normalize. - timestamp: entry.timestamp as unknown as string, - }), + getIndexKysely(db).insertInto("session_transcript_fts").values({ + text: entry.text, + session_id: sessionId, + message_id: entry.messageId, + role: entry.role, + // FTS5 aux columns are typeless; the local insert type preserves the + // numeric timestamp SQLite stores while generated readers stay strings. + timestamp: entry.timestamp, + }), ); } diff --git a/src/config/sessions/session-transcript-projection-rebuild.ts b/src/config/sessions/session-transcript-projection-rebuild.ts index 0608f3223626..3c60d7580029 100644 --- a/src/config/sessions/session-transcript-projection-rebuild.ts +++ b/src/config/sessions/session-transcript-projection-rebuild.ts @@ -1,5 +1,5 @@ import type { DatabaseSync } from "node:sqlite"; -import type { Generated } from "kysely"; +import type { ColumnType, Generated } from "kysely"; import { executeSqliteQuerySync, executeSqliteQueryTakeFirstSync, @@ -23,8 +23,12 @@ type TranscriptProjectionDatabase = Pick< session_transcript_active_events: OpenClawAgentKyselyDatabase["session_transcript_active_events"] & { rowid: Generated; }; - session_transcript_fts: OpenClawAgentKyselyDatabase["session_transcript_fts"] & { + session_transcript_fts: Omit< + OpenClawAgentKyselyDatabase["session_transcript_fts"], + "timestamp" + > & { rowid: Generated; + timestamp: ColumnType; }; }; @@ -419,7 +423,7 @@ export function appendPreparedSessionTranscriptProjectionChunkInTransaction( role: row.role, session_id: params.sessionId, text: row.text, - timestamp: row.timestamp as unknown as string, + timestamp: row.timestamp, })), ), ); diff --git a/src/media/runtime-prompt-image-provenance.ts b/src/media/runtime-prompt-image-provenance.ts index 5f011c229c8f..650e260b829e 100644 --- a/src/media/runtime-prompt-image-provenance.ts +++ b/src/media/runtime-prompt-image-provenance.ts @@ -31,9 +31,10 @@ export function readRuntimePromptImageFactIndexes( if (!images?.length) { return undefined; } - const factIndexes = (images as unknown as Record)[ - RUNTIME_PROMPT_IMAGE_FACT_INDEXES - ]; + const runtimeImages: readonly object[] & { + [RUNTIME_PROMPT_IMAGE_FACT_INDEXES]?: unknown; + } = images; + const factIndexes = runtimeImages[RUNTIME_PROMPT_IMAGE_FACT_INDEXES]; return Array.isArray(factIndexes) && factIndexes.length === images.length && factIndexes.every( diff --git a/src/process/command-queue.capacity-groups.ts b/src/process/command-queue.capacity-groups.ts index a2d16968cf15..56f03c4cf4c1 100644 --- a/src/process/command-queue.capacity-groups.ts +++ b/src/process/command-queue.capacity-groups.ts @@ -73,10 +73,10 @@ export function getGroupRegistry(): { groups: Map; groupByLane: Map; } { - const state = getQueueState() as unknown as { + const state: ReturnType & { laneGroups?: Map; laneGroupByLane?: Map; - }; + } = getQueueState(); // Migration: an older singleton (pre-upgrade, inherited via globalThis after // a SIGUSR1 in-process restart) has neither field. Active counts are derived, // so a late-initialized registry cannot desynchronize from lane state. diff --git a/src/runtime.ts b/src/runtime.ts index 588a7ea92dc5..5022c86ca140 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -31,7 +31,7 @@ function shouldEmitRuntimeLog(env: NodeJS.ProcessEnv = process.env): boolean { if (env.OPENCLAW_TEST_RUNTIME_LOG === "1") { return true; } - const maybeMockedLog = console.log as unknown as { mock?: unknown }; + const maybeMockedLog = console.log as typeof console.log & { mock?: unknown }; return typeof maybeMockedLog.mock === "object"; } diff --git a/src/shared/json-schema-defaults.ts b/src/shared/json-schema-defaults.ts index 14d42377ebe1..10b04ce5d03b 100644 --- a/src/shared/json-schema-defaults.ts +++ b/src/shared/json-schema-defaults.ts @@ -15,6 +15,7 @@ type LocalRefResolution = resourceBaseId: string | undefined; } | { found: false }; +type JsonSchemaNode = JsonSchemaValue | JsonSchemaNode[]; const schemaResourceIds = new WeakMap(); let nextSchemaResourceId = 1; const schemaMapKeywords = new Set([ @@ -670,18 +671,26 @@ function inlineLocalRefsForMatch( root: JsonSchemaValue, resourceRoot: JsonSchemaValue, resourceBaseId: string | undefined, + resolvingRefs?: Set, +): JsonSchemaValue; +function inlineLocalRefsForMatch( + schema: JsonSchemaNode, + root: JsonSchemaValue, + resourceRoot: JsonSchemaValue, + resourceBaseId: string | undefined, + resolvingRefs?: Set, +): JsonSchemaNode; +function inlineLocalRefsForMatch( + schema: JsonSchemaNode, + root: JsonSchemaValue, + resourceRoot: JsonSchemaValue, + resourceBaseId: string | undefined, resolvingRefs = new Set(), -): JsonSchemaValue { +): JsonSchemaNode { if (Array.isArray(schema)) { return schema.map((entry) => - inlineLocalRefsForMatch( - entry as JsonSchemaValue, - root, - resourceRoot, - resourceBaseId, - resolvingRefs, - ), - ) as unknown as JsonSchemaValue; + inlineLocalRefsForMatch(entry, root, resourceRoot, resourceBaseId, resolvingRefs), + ); } if (!isRecord(schema)) { return schema;