From 35953a75deceb2ebe9d37dac33c48de3c9354521 Mon Sep 17 00:00:00 2001 From: Yichen Jiang Date: Tue, 20 Oct 2026 11:55:49 +0800 Subject: [PATCH] fix(forge): classify the panel's Host imports under the published dependency policy verify-package-dependencies now covers experimental Client packages, and the forge panel's Host face imported four workspace values that no policy entry classified. appendPluginRecord writes through the Session class's private log, and readers recognize a deadline's TimeoutReason only by instanceof, so both need the provider's shared instance. pluginRecordOf and generatedWorktreeName keep no state; classifying them as peer-required keeps the panel's existing Session and worktree peer edges. With the exports classified, the fixer keeps worktree, Session, and timeout as the panel's only workspace peers, moves its type-only relationships to devDependencies and dsh-typert-protocol to dependencies, and regenerates the lockfile and the module graph. verify-plugin-record-callers counts a string literal that spells appendPluginRecord as a caller, so it now admits the dependency policy file, which names the export to classify it and never calls it. --- docs/module-graph.md | 14 ++----------- docs/module-graph.zh.md | 14 ++----------- .../experimental/client-ui-forge/package.json | 20 +++++-------------- pnpm-lock.yaml | 8 ++++---- scripts/package-dependency-policy.ts | 7 ++++++- scripts/verify-package-dependencies.spec.ts | 5 +++++ scripts/verify-plugin-record-callers.spec.ts | 4 +++- scripts/verify-plugin-record-callers.ts | 9 +++++++-- 8 files changed, 34 insertions(+), 47 deletions(-) diff --git a/docs/module-graph.md b/docs/module-graph.md index 2f115877fe..48d3fc6f51 100644 --- a/docs/module-graph.md +++ b/docs/module-graph.md @@ -935,19 +935,9 @@ flowchart TD pkg_experimental_browser_use_stagehand_native --> pkg_browser_use pkg_experimental_browser_use_stagehand_native --> pkg_system_prompt pkg_experimental_browser_use_stagehand_native --> pkg_tools - pkg_experimental_client_ui_forge --> pkg_agent - pkg_experimental_client_ui_forge --> pkg_agent_controller - pkg_experimental_client_ui_forge --> pkg_experimental_forge - pkg_experimental_client_ui_forge --> pkg_experimental_forge_session pkg_experimental_client_ui_forge --> pkg_experimental_worktree - pkg_experimental_client_ui_forge --> pkg_fs - pkg_experimental_client_ui_forge --> pkg_sandbox - pkg_experimental_client_ui_forge --> pkg_sandbox_policy pkg_experimental_client_ui_forge --> pkg_session - pkg_experimental_client_ui_forge --> pkg_session_projection - pkg_experimental_client_ui_forge --> pkg_subprocess - pkg_experimental_client_ui_forge --> pkg_typert_protocol - pkg_experimental_client_ui_forge --> pkg_working_directory + pkg_experimental_client_ui_forge --> pkg_timeout pkg_experimental_computer_use_cua_driver_native --> pkg_computer_use pkg_experimental_computer_use_cua_driver_native --> pkg_system_prompt pkg_experimental_computer_use_cua_driver_native --> pkg_tools @@ -1735,7 +1725,7 @@ flowchart TD | [`experimental-browser-use-chrome-devtools-mcp`](../packages/experimental/browser-use-chrome-devtools-mcp) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) | | [`experimental-browser-use-playwright-mcp`](../packages/experimental/browser-use-playwright-mcp) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) | | [`experimental-browser-use-stagehand-native`](../packages/experimental/browser-use-stagehand-native) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | -| [`experimental-client-ui-forge`](../packages/experimental/client-ui-forge) | `experimental` | [`agent`](../packages/core/agent), [`agent-controller`](../packages/core/agent-controller), [`experimental-forge`](../packages/experimental/forge), [`experimental-forge-session`](../packages/experimental/forge-session), [`experimental-worktree`](../packages/experimental/worktree), [`fs`](../packages/fs/fs), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`subprocess`](../packages/subprocess/subprocess), [`typert-protocol`](../packages/typert/protocol), [`working-directory`](../packages/session/working-directory) | +| [`experimental-client-ui-forge`](../packages/experimental/client-ui-forge) | `experimental` | [`experimental-worktree`](../packages/experimental/worktree), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) | | [`experimental-computer-use-cua-driver-native`](../packages/experimental/computer-use-cua-driver-native) | `experimental` | [`computer-use`](../packages/computer-use/computer-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | | [`experimental-cua-macos`](../packages/experimental/cua-macos) | `experimental` | [`agent`](../packages/core/agent), [`computer-use`](../packages/computer-use/computer-use), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-questions`](../packages/interaction/user-questions) | | [`experimental-tool-worktree`](../packages/experimental/tool-worktree) | `experimental` | [`experimental-worktree`](../packages/experimental/worktree), [`tools`](../packages/core/tools) | diff --git a/docs/module-graph.zh.md b/docs/module-graph.zh.md index e569e3df31..70aacc9384 100644 --- a/docs/module-graph.zh.md +++ b/docs/module-graph.zh.md @@ -937,19 +937,9 @@ flowchart TD pkg_experimental_browser_use_stagehand_native --> pkg_browser_use pkg_experimental_browser_use_stagehand_native --> pkg_system_prompt pkg_experimental_browser_use_stagehand_native --> pkg_tools - pkg_experimental_client_ui_forge --> pkg_agent - pkg_experimental_client_ui_forge --> pkg_agent_controller - pkg_experimental_client_ui_forge --> pkg_experimental_forge - pkg_experimental_client_ui_forge --> pkg_experimental_forge_session pkg_experimental_client_ui_forge --> pkg_experimental_worktree - pkg_experimental_client_ui_forge --> pkg_fs - pkg_experimental_client_ui_forge --> pkg_sandbox - pkg_experimental_client_ui_forge --> pkg_sandbox_policy pkg_experimental_client_ui_forge --> pkg_session - pkg_experimental_client_ui_forge --> pkg_session_projection - pkg_experimental_client_ui_forge --> pkg_subprocess - pkg_experimental_client_ui_forge --> pkg_typert_protocol - pkg_experimental_client_ui_forge --> pkg_working_directory + pkg_experimental_client_ui_forge --> pkg_timeout pkg_experimental_computer_use_cua_driver_native --> pkg_computer_use pkg_experimental_computer_use_cua_driver_native --> pkg_system_prompt pkg_experimental_computer_use_cua_driver_native --> pkg_tools @@ -1737,7 +1727,7 @@ flowchart TD | [`experimental-browser-use-chrome-devtools-mcp`](../packages/experimental/browser-use-chrome-devtools-mcp) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) | | [`experimental-browser-use-playwright-mcp`](../packages/experimental/browser-use-playwright-mcp) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) | | [`experimental-browser-use-stagehand-native`](../packages/experimental/browser-use-stagehand-native) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | -| [`experimental-client-ui-forge`](../packages/experimental/client-ui-forge) | `experimental` | [`agent`](../packages/core/agent), [`agent-controller`](../packages/core/agent-controller), [`experimental-forge`](../packages/experimental/forge), [`experimental-forge-session`](../packages/experimental/forge-session), [`experimental-worktree`](../packages/experimental/worktree), [`fs`](../packages/fs/fs), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`subprocess`](../packages/subprocess/subprocess), [`typert-protocol`](../packages/typert/protocol), [`working-directory`](../packages/session/working-directory) | +| [`experimental-client-ui-forge`](../packages/experimental/client-ui-forge) | `experimental` | [`experimental-worktree`](../packages/experimental/worktree), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) | | [`experimental-computer-use-cua-driver-native`](../packages/experimental/computer-use-cua-driver-native) | `experimental` | [`computer-use`](../packages/computer-use/computer-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) | | [`experimental-cua-macos`](../packages/experimental/cua-macos) | `experimental` | [`agent`](../packages/core/agent), [`computer-use`](../packages/computer-use/computer-use), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-questions`](../packages/interaction/user-questions) | | [`experimental-tool-worktree`](../packages/experimental/tool-worktree) | `experimental` | [`experimental-worktree`](../packages/experimental/worktree), [`tools`](../packages/core/tools) | diff --git a/packages/experimental/client-ui-forge/package.json b/packages/experimental/client-ui-forge/package.json index 538de45229..748fd71eca 100644 --- a/packages/experimental/client-ui-forge/package.json +++ b/packages/experimental/client-ui-forge/package.json @@ -57,25 +57,15 @@ "license": "MIT", "peerDependencies": { "@deepseek-ai/cordis": "workspace:~", - "@deepseek-ai/dsh-agent": "workspace:*", - "@deepseek-ai/dsh-agent-controller": "workspace:*", - "@deepseek-ai/dsh-experimental-forge": "workspace:*", - "@deepseek-ai/dsh-experimental-forge-session": "workspace:*", "@deepseek-ai/dsh-experimental-worktree": "workspace:*", - "@deepseek-ai/dsh-fs": "workspace:*", - "@deepseek-ai/dsh-sandbox": "workspace:*", - "@deepseek-ai/dsh-sandbox-policy": "workspace:*", "@deepseek-ai/dsh-session": "workspace:*", - "@deepseek-ai/dsh-session-projection": "workspace:*", - "@deepseek-ai/dsh-subprocess": "workspace:*", - "@deepseek-ai/dsh-typert-protocol": "workspace:*", - "@deepseek-ai/dsh-working-directory": "workspace:*" + "@deepseek-ai/dsh-timeout": "workspace:*" }, "dependencies": { "@deepseek-ai/schemastery": "workspace:~", - "@deepseek-ai/dsh-timeout": "workspace:*", "@deepseek-ai/dsh-util-values": "workspace:*", - "zod": "^4.4.3" + "zod": "^4.4.3", + "@deepseek-ai/dsh-typert-protocol": "workspace:*" }, "devDependencies": { "@deepseek-ai/cordis": "workspace:~", @@ -109,13 +99,13 @@ "@deepseek-ai/dsh-subprocess": "workspace:*", "@deepseek-ai/dsh-subprocess-local": "workspace:*", "@deepseek-ai/dsh-system-prompt": "workspace:*", - "@deepseek-ai/dsh-typert-protocol": "workspace:*", "@deepseek-ai/dsh-util-workspace-path": "workspace:*", "@deepseek-ai/dsh-working-directory": "workspace:*", "@testing-library/react": "^16.3.3", "clsx": "^2.1.1", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "@deepseek-ai/dsh-timeout": "workspace:*" }, "files": [ "locale/*.json", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 96cede3702..9447e7a799 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8400,9 +8400,9 @@ importers: packages/experimental/client-ui-forge: dependencies: - '@deepseek-ai/dsh-timeout': + '@deepseek-ai/dsh-typert-protocol': specifier: workspace:* - version: link:../../util/timeout + version: link:../../typert/protocol '@deepseek-ai/dsh-util-values': specifier: workspace:* version: link:../../util/values @@ -8506,9 +8506,9 @@ importers: '@deepseek-ai/dsh-system-prompt': specifier: workspace:* version: link:../../core/system-prompt - '@deepseek-ai/dsh-typert-protocol': + '@deepseek-ai/dsh-timeout': specifier: workspace:* - version: link:../../typert/protocol + version: link:../../util/timeout '@deepseek-ai/dsh-util-workspace-path': specifier: workspace:* version: link:../../util/workspace-path diff --git a/scripts/package-dependency-policy.ts b/scripts/package-dependency-policy.ts index 95e498cdfc..5281c4db4c 100644 --- a/scripts/package-dependency-policy.ts +++ b/scripts/package-dependency-policy.ts @@ -68,9 +68,14 @@ const PEER_REQUIRED_HOST_EXPORTS = { '@deepseek-ai/dsh-user-questions': ['UserQuestionError'], // `instanceof` checks need the translator's own class instance. '@deepseek-ai/dsh-experimental-translator': ['TranslationError'], + // Stateless; a safe classification would turn the forge panel's worktree peer into a dependency. + '@deepseek-ai/dsh-experimental-worktree': ['generatedWorktreeName'], '@deepseek-ai/dsh-scope': ['carrierKeyOf', 'createScope', 'scopeOf', 'scopeTarget'], - '@deepseek-ai/dsh-session': ['SESSION_FORMAT_VERSION'], + // appendPluginRecord writes through the Session class's private log; pluginRecordOf is stateless. + '@deepseek-ai/dsh-session': ['SESSION_FORMAT_VERSION', 'appendPluginRecord', 'pluginRecordOf'], '@deepseek-ai/dsh-session-persistence': ['SessionPersistenceNotFoundError'], + // Readers recognize a deadline's TimeoutReason only by `instanceof` against their own copy. + '@deepseek-ai/dsh-timeout': ['deadline'], } as const satisfies HostDependencyExports /** Exact import specifier to reviewed runtime exports. */ diff --git a/scripts/verify-package-dependencies.spec.ts b/scripts/verify-package-dependencies.spec.ts index ca5f646337..4736079d59 100644 --- a/scripts/verify-package-dependencies.spec.ts +++ b/scripts/verify-package-dependencies.spec.ts @@ -247,6 +247,11 @@ describe('package dependency scope', () => { expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-typert-protocol']).toBeUndefined() expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-experimental-translator']) .toEqual(['TranslationError']) + expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-experimental-worktree']) + .toEqual(['generatedWorktreeName']) + expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-session']) + .toEqual(['SESSION_FORMAT_VERSION', 'appendPluginRecord', 'pluginRecordOf']) + expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-timeout']).toEqual(['deadline']) }) it('discovers the Client directory, dsh.client declarations, and configured Host packages', () => { diff --git a/scripts/verify-plugin-record-callers.spec.ts b/scripts/verify-plugin-record-callers.spec.ts index c1ceedf82f..70b45a4963 100644 --- a/scripts/verify-plugin-record-callers.spec.ts +++ b/scripts/verify-plugin-record-callers.spec.ts @@ -6,6 +6,7 @@ import { afterEach, describe, expect, it } from 'vitest' import { checkDeclaredPluginRecordWrites, checkPluginRecordCallers, + DEPENDENCY_POLICY_FILE, findPluginRecordReferences, isAdmittedCaller, OWNER_FILE, @@ -132,10 +133,11 @@ describe('verify-plugin-record-callers', () => { expect(checkPluginRecordCallers(corpus(source))).toEqual([]) }) - it('admits experimental source, the owner, tests, and this gate', () => { + it('admits experimental source, the owner, tests, this gate, and the dependency policy', () => { for (const file of [ EXPERIMENTAL, OWNER_FILE, + DEPENDENCY_POLICY_FILE, 'packages/session/session-persistence/tests/live-write-contract.ts', 'apps/web/tests/session.e2e.ts', 'examples/demo/tests/demo.spec.ts', diff --git a/scripts/verify-plugin-record-callers.ts b/scripts/verify-plugin-record-callers.ts index 742096ab9c..e1a1b55e39 100644 --- a/scripts/verify-plugin-record-callers.ts +++ b/scripts/verify-plugin-record-callers.ts @@ -4,7 +4,8 @@ * `appendPluginRecord` in `@deepseek-ai/dsh-session` appends an ignorable `plugin:` record that the * experimental persistence catalogue lists and that a Session format migration keeps only on a best-effort * basis. Experimental packages may rely on that; a release package must declare its events instead. - * The owning module and test files, which exercise the operation, are exempt. + * The owning module and test files, which exercise the operation, are exempt, as is the published + * dependency policy, which names the function to classify the export without calling it. * * Discovery is syntax-aware: every identifier spelled `appendPluginRecord` counts — an import, a * named re-export, a call, a namespace member, a destructured binding, or an alias source — and so @@ -42,6 +43,9 @@ const GATE_FILES: ReadonlySet = new Set([ 'scripts/verify-plugin-record-callers.spec.ts', ]) +/** The published dependency policy names the restricted function to classify the export's package edge. */ +export const DEPENDENCY_POLICY_FILE = 'scripts/package-dependency-policy.ts' + const SOURCE_EXTENSION = /\.(?:[cm]?[jt]s|[jt]sx)$/u /** A line continuation, which a string or template literal drops from its value. */ @@ -99,10 +103,11 @@ export function isTestFile(file: string): boolean { /** * Whether a file may reference the restricted function. * @param file - repository-relative path, in POSIX separators. - * @returns whether the file is experimental source, the owner, a test, or this gate. + * @returns whether the file is experimental source, the owner, a test, this gate, or the dependency policy. */ export function isAdmittedCaller(file: string): boolean { return file.startsWith(EXPERIMENTAL_PREFIX) || file === OWNER_FILE || isTestFile(file) || GATE_FILES.has(file) + || file === DEPENDENCY_POLICY_FILE } function scriptKind(file: string): ts.ScriptKind {