diff --git a/scripts/client-bundle-purity.spec.ts b/scripts/client-bundle-purity.spec.ts index 6d7db99497..d9b0626670 100644 --- a/scripts/client-bundle-purity.spec.ts +++ b/scripts/client-bundle-purity.spec.ts @@ -95,6 +95,7 @@ describe('client bundle purity gate', () => { expect(resolveId('@deepseek-ai/dsh-session/surface')).toBeNull() expect(resolveId('@deepseek-ai/dsh-brand')).toBeNull() expect(resolveId('@deepseek-ai/dsh-deque')).toBeNull() + expect(resolveId('@deepseek-ai/dsh-util-values')).toBeNull() expect(resolveId('@deepseek-ai/dsh-token-meter/client')).toBeNull() expect(() => resolveId('@deepseek-ai/dsh-token-meter')).toThrow(/purity/) expect(() => resolveId('@deepseek-ai/dsh-token-meter/client/internal')).toThrow(/purity/) diff --git a/scripts/doc-standard.spec.ts b/scripts/doc-standard.spec.ts index 44da82fb3a..fb7f097e43 100644 --- a/scripts/doc-standard.spec.ts +++ b/scripts/doc-standard.spec.ts @@ -76,7 +76,7 @@ const PACKAGE_LIBRARIES: Readonly> = { 'packages/typert/generator': 'Build-time generator run outside any agent runtime.', 'packages/typert/protocol': 'Compiler-independent protocol declarations.', 'packages/util/atomic-write': 'Zero-dependency filesystem write utility.', - 'packages/util/brand': 'Type-only branding primitive erased at compile time.', + 'packages/util/brand': 'Stateless nominal-string and canonical-key constructors.', 'packages/util/crypto': 'Zero-dependency identifier minting utility.', 'packages/util/deque': 'Zero-dependency circular deque utility.', 'packages/util/home-paths': 'Zero-dependency harness-home path resolver.', @@ -85,6 +85,7 @@ const PACKAGE_LIBRARIES: Readonly> = { 'packages/util/output-retention': 'Zero-dependency retention utility.', 'packages/util/time': 'Zero-dependency time-zone canonicalization utility.', 'packages/util/timeout': 'Zero-dependency timeout utility.', + 'packages/util/values': 'Stateless lossless-JSON and immutable-value helpers.', 'packages/util/workspace-path': 'Zero-dependency Workspace path formatter.', } diff --git a/scripts/gen-cordis-catalog.ts b/scripts/gen-cordis-catalog.ts index 1bae81253b..73729da6b9 100644 --- a/scripts/gen-cordis-catalog.ts +++ b/scripts/gen-cordis-catalog.ts @@ -493,7 +493,9 @@ export const LINK_MAP: Readonly> = { SubagentStartRequest: 'subagent.md', AssembleContext: 'system-prompt.md', PromptContext: 'system-prompt.md', + PromptContextOrderName: 'system-prompt.md', PromptSection: 'system-prompt.md', + PromptSectionOrderName: 'system-prompt.md', SystemPrompt: 'system-prompt.md', ToolProviderResult: 'system-prompt.md', JobDoneListener: 'jobs.md', @@ -533,7 +535,9 @@ export const LINK_MAP: Readonly> = { ToolRestriction: 'tools.md', ToolSchema: 'tools.md', SettingsNamespace: 'settings.md', + SettingsNamespaceInput: 'settings.md', SettingsRegisterOptions: 'settings.md', + SettingsSectionHooks: 'settings.md', SettingsScope: 'settings.md', SettingsDescriptor: 'settings.md', SettingsDescribeValue: 'settings.md', diff --git a/scripts/package-dependency-policy.ts b/scripts/package-dependency-policy.ts index 70cbd2a4f1..63054fd183 100644 --- a/scripts/package-dependency-policy.ts +++ b/scripts/package-dependency-policy.ts @@ -29,29 +29,28 @@ const CONFIGURATION_ONLY_DEV_DEPENDENCIES = { '@deepseek-ai/dsh-client-ui-tool': ['@deepseek-ai/dsh-api-remotes'], } as const satisfies Readonly> +/** Workspace packages whose complete runtime surface is safe across duplicate installations. */ +const DUPLICATE_SAFE_PACKAGES: readonly string[] = [ + '@deepseek-ai/dsh-brand', + '@deepseek-ai/dsh-typert-protocol', + '@deepseek-ai/dsh-util-crypto', + '@deepseek-ai/dsh-util-values', +] + /** * Runtime exports whose values remain valid when npm installs another package copy. */ const SAFE_HOST_DEPENDENCY_EXPORTS = { - '@deepseek-ai/dsh-api-session-controller/remote-events': ['SESSION_CONTROLLER_REMOTE_EVENTS'], '@deepseek-ai/dsh-credentials': ['credentialKey'], '@deepseek-ai/dsh-deque': ['Deque'], - '@deepseek-ai/dsh-llm': ['MessageId', 'callConfigEquals', 'deepFreeze', 'freezeMessage'], - '@deepseek-ai/dsh-llm/brand': ['ToolCallId'], - '@deepseek-ai/dsh-session': ['isJsonValue'], - '@deepseek-ai/dsh-session/types': ['SessionId'], - '@deepseek-ai/dsh-settings': ['settingsNamespace'], - '@deepseek-ai/dsh-system-prompt': ['FIRST_PARTY_SECTION_ORDER'], + '@deepseek-ai/dsh-llm': ['callConfigEquals'], '@deepseek-ai/dsh-timeout': ['MAX_TIMER_DELAY_MS'], - '@deepseek-ai/dsh-typert-protocol': ['RemoteError', 'remoteErrorOf'], - '@deepseek-ai/dsh-util-crypto': ['randomUUID'], '@deepseek-ai/schemastery': ['default'], } as const satisfies HostDependencyExports /** Runtime exports that require every consumer to resolve the provider's shared peer instance. */ const PEER_REQUIRED_HOST_EXPORTS = { '@deepseek-ai/dsh-scope': ['carrierKeyOf', 'scopeOf', 'scopeTarget'], - '@deepseek-ai/dsh-typert-protocol': ['Remote', 'TypertRemoteService', 'remoteMethods'], } as const satisfies HostDependencyExports /** Exact import specifier to reviewed runtime exports. */ @@ -63,6 +62,7 @@ export interface PackageDependencyPolicy { readonly clientFaceExclude: readonly string[] readonly hostPackages: readonly string[] readonly configurationOnlyDevDependencies: Readonly> + readonly duplicateSafePackages?: readonly string[] readonly safeHostDependencyExports: HostDependencyExports readonly peerRequiredHostExports: HostDependencyExports } @@ -73,6 +73,7 @@ export const PACKAGE_DEPENDENCY_POLICY: PackageDependencyPolicy = { clientFaceExclude: CLIENT_FACE_EXCLUDE, hostPackages: HOST_DEPENDENCY_PACKAGES, configurationOnlyDevDependencies: CONFIGURATION_ONLY_DEV_DEPENDENCIES, + duplicateSafePackages: DUPLICATE_SAFE_PACKAGES, safeHostDependencyExports: SAFE_HOST_DEPENDENCY_EXPORTS, peerRequiredHostExports: PEER_REQUIRED_HOST_EXPORTS, } diff --git a/scripts/verify-package-dependencies.spec.ts b/scripts/verify-package-dependencies.spec.ts index 2fe2038c7e..4294cff84b 100644 --- a/scripts/verify-package-dependencies.spec.ts +++ b/scripts/verify-package-dependencies.spec.ts @@ -87,6 +87,39 @@ function facts(manifest: PackageDependencyManifest): PackageDependencyFacts { } } +function hostRuntimeFixture(): { + provider: WorkspacePackageManifest + workspaceNames: Set + consumerFacts: PackageDependencyFacts +} { + const consumer = pkg('@f/consumer', 'packages/core/consumer/package.json') + const provider = pkg('@f/provider', 'packages/core/provider/package.json') + const sourcePath = 'packages/core/consumer/src/index.ts' + const specifier = `${provider.name}/api` + const workspaceNames = new Set([CORDIS, consumer.name, provider.name]) + const consumerFacts: PackageDependencyFacts = { + manifestPath: consumer.manifestPath, + role: 'configured-host', + manifest: consumer.manifest, + workspaceNames, + allSourceUses: new Map(), + hostRuntimeSourceUses: new Map([[provider.name, [sourcePath]]]), + hostRuntimeExportUses: [{ + packageName: provider.name, + specifier, + exportName: 'safeValue', + sourcePath, + line: 1, + column: 10, + sourceLine: `import { safeValue } from '${specifier}'`, + }], + peerRequiredHostDependencies: new Set(), + configurationOnlyDevDependencies: new Set(), + clientInject: new Set(), + } + return { provider, workspaceNames, consumerFacts } +} + describe('package dependency scope', () => { it('keeps the measured Host relay roster explicit', () => { expect(PACKAGE_DEPENDENCY_POLICY.clientFaceExclude).toEqual([ @@ -109,20 +142,20 @@ describe('package dependency scope', () => { '@deepseek-ai/dsh-client-ui-theme': ['@deepseek-ai/dsh-api-remotes'], '@deepseek-ai/dsh-client-ui-tool': ['@deepseek-ai/dsh-api-remotes'], }) + expect(PACKAGE_DEPENDENCY_POLICY.duplicateSafePackages).toEqual([ + '@deepseek-ai/dsh-brand', + '@deepseek-ai/dsh-typert-protocol', + '@deepseek-ai/dsh-util-crypto', + '@deepseek-ai/dsh-util-values', + ]) expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/dsh-deque']).toEqual(['Deque']) expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/schemastery']).toEqual(['default']) - expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/dsh-session/types']).toEqual([ - 'SessionId', - ]) - expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/dsh-typert-protocol']).toEqual([ - 'RemoteError', 'remoteErrorOf', - ]) + expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/dsh-session/types']).toBeUndefined() + expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/dsh-typert-protocol']).toBeUndefined() expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-scope']).toEqual([ 'carrierKeyOf', 'scopeOf', 'scopeTarget', ]) - expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-typert-protocol']).toEqual([ - 'Remote', 'TypertRemoteService', 'remoteMethods', - ]) + expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-typert-protocol']).toBeUndefined() }) it('discovers the Client directory, dsh.client declarations, and configured Host packages', () => { @@ -175,29 +208,7 @@ describe('package dependency scope', () => { }) it('rejects stale, duplicate, and unbounded safe Host export entries', () => { - const consumer = pkg('@f/consumer', 'packages/core/consumer/package.json') - const provider = pkg('@f/provider', 'packages/core/provider/package.json') - const workspaceNames = new Set([CORDIS, consumer.name, provider.name]) - const consumerFacts: PackageDependencyFacts = { - manifestPath: consumer.manifestPath, - role: 'configured-host', - manifest: consumer.manifest, - workspaceNames, - allSourceUses: new Map(), - hostRuntimeSourceUses: new Map([[provider.name, ['packages/core/consumer/src/index.ts']]]), - hostRuntimeExportUses: [{ - packageName: provider.name, - specifier: `${provider.name}/api`, - exportName: 'safeValue', - sourcePath: 'packages/core/consumer/src/index.ts', - line: 1, - column: 10, - sourceLine: "import { safeValue } from '@f/provider/api'", - }], - peerRequiredHostDependencies: new Set(), - configurationOnlyDevDependencies: new Set(), - clientInject: new Set(), - } + const { provider, workspaceNames, consumerFacts } = hostRuntimeFixture() expect(collectHostDependencyExportPolicyViolations( [consumerFacts], @@ -217,6 +228,29 @@ describe('package dependency scope', () => { expect.stringContaining('appears in both Host export classifications'), ])) }) + + it('applies a duplicate-safe package classification to its subpaths', () => { + const { provider, workspaceNames, consumerFacts } = hostRuntimeFixture() + + expect(collectHostDependencyExportPolicyViolations( + [consumerFacts], + workspaceNames, + { + duplicateSafePackages: [provider.name], + safeHostDependencyExports: {}, + peerRequiredHostExports: {}, + }, + )).toEqual([]) + expect(collectHostDependencyExportPolicyViolations( + [consumerFacts], + workspaceNames, + { + duplicateSafePackages: [provider.name], + safeHostDependencyExports: { [`${provider.name}/api`]: ['safeValue'] }, + peerRequiredHostExports: {}, + }, + )).toContain(`safeHostDependencyExports redundantly classifies duplicate-install-safe package ${provider.name}/api`) + }) }) describe('face-aware source classification', () => { diff --git a/scripts/verify-package-dependencies.ts b/scripts/verify-package-dependencies.ts index b20e16af00..678af98976 100644 --- a/scripts/verify-package-dependencies.ts +++ b/scripts/verify-package-dependencies.ts @@ -374,10 +374,19 @@ export function readPackageDependencyFacts( export function collectHostDependencyExportPolicyViolations( facts: readonly PackageDependencyFacts[], workspaceNames: ReadonlySet, - policy: Pick, + policy: Pick, ): string[] { const violations: string[] = [] const allRuntimeUses = facts.flatMap(fact => fact.hostRuntimeExportUses) + const duplicateSafePackages = new Set(policy.duplicateSafePackages ?? []) + for (const packageName of duplicates(policy.duplicateSafePackages ?? [])) { + violations.push(`duplicateSafePackages lists ${packageName} more than once`) + } + for (const packageName of duplicateSafePackages) { + if (!workspaceNames.has(packageName)) { + violations.push(`duplicateSafePackages names unknown workspace package ${packageName}`) + } + } const classifications = [ ['safeHostDependencyExports', policy.safeHostDependencyExports], ['peerRequiredHostExports', policy.peerRequiredHostExports], @@ -387,6 +396,8 @@ export function collectHostDependencyExportPolicyViolations( const provider = packageNameOf(specifier) if (provider === undefined || !workspaceNames.has(provider)) { violations.push(`${field} specifier ${specifier} is not a workspace package`) + } else if (duplicateSafePackages.has(provider)) { + violations.push(`${field} redundantly classifies duplicate-install-safe package ${specifier}`) } if (exportNames.length === 0) { violations.push(`${field} lists no exports for ${specifier}`) @@ -414,6 +425,7 @@ export function collectHostDependencyExportPolicyViolations( for (const use of fact.hostRuntimeExportUses) { if (use.packageName === fact.manifest.name || use.packageName === CORDIS) continue if (!workspaceNames.has(use.packageName)) continue + if (duplicateSafePackages.has(use.packageName)) continue if (policy.safeHostDependencyExports[use.specifier]?.includes(use.exportName) === true) continue if (policy.peerRequiredHostExports[use.specifier]?.includes(use.exportName) === true) continue violations.push( diff --git a/scripts/verify-package-readme-limitations.ts b/scripts/verify-package-readme-limitations.ts index 22ac66a278..bfa3180c24 100644 --- a/scripts/verify-package-readme-limitations.ts +++ b/scripts/verify-package-readme-limitations.ts @@ -16,7 +16,7 @@ const CANONICAL = '## Known Limitations and Deferred Work' /** Packages audited as having no limitations section, keyed by repo-relative directory. */ const NO_LIMITATIONS: Readonly> = { - 'packages/util/brand': 'Type-only nominal-branding primitive with no runtime behavior or deferred work.', + 'packages/util/brand': 'Stateless nominal-string and canonical-key helpers have no deferred work.', } /** A heading that reads as a limitations section — canonical or drifted. */ diff --git a/scripts/verify-package-readme-model-experience.ts b/scripts/verify-package-readme-model-experience.ts index ceeb250f6b..cf11ed3d8d 100644 --- a/scripts/verify-package-readme-model-experience.ts +++ b/scripts/verify-package-readme-model-experience.ts @@ -31,10 +31,11 @@ interface SentenceContract { */ const NO_MODEL_EXPERIENCE_SECTION: Readonly> = { 'packages/core/scope': 'The package is a model-agnostic registration and lifecycle primitive; model-facing consumers own any context selection.', - 'packages/util/brand': 'The package is a type-only primitive erased at compile time.', + 'packages/util/brand': 'The package only constructs plain string values and registers nothing model-facing.', 'packages/util/home-paths': 'The package only resolves harness-owned host paths; model-facing consumers own any rendered use.', 'packages/util/launch-environment': 'The package only resolves host environment values; model-facing consumers own any rendered use.', 'packages/util/workspace-path': 'The package only formats Workspace paths for browser UI; it never constructs model input.', + 'packages/util/values': 'The package only validates, snapshots, compares, freezes, or rejects caller-owned values; consumers own every model-facing use.', } /**