test(deps): enforce runtime dependency ownership
This commit is contained in:
parent
9135a13a8b
commit
34bdc81b47
8 changed files with 100 additions and 46 deletions
|
|
@ -95,6 +95,7 @@ describe('client bundle purity gate', () => {
|
||||||
expect(resolveId('@deepseek-ai/dsh-session/surface')).toBeNull()
|
expect(resolveId('@deepseek-ai/dsh-session/surface')).toBeNull()
|
||||||
expect(resolveId('@deepseek-ai/dsh-brand')).toBeNull()
|
expect(resolveId('@deepseek-ai/dsh-brand')).toBeNull()
|
||||||
expect(resolveId('@deepseek-ai/dsh-deque')).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/client')).toBeNull()
|
||||||
expect(() => resolveId('@deepseek-ai/dsh-token-meter')).toThrow(/purity/)
|
expect(() => resolveId('@deepseek-ai/dsh-token-meter')).toThrow(/purity/)
|
||||||
expect(() => resolveId('@deepseek-ai/dsh-token-meter/client/internal')).toThrow(/purity/)
|
expect(() => resolveId('@deepseek-ai/dsh-token-meter/client/internal')).toThrow(/purity/)
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ const PACKAGE_LIBRARIES: Readonly<Record<string, string>> = {
|
||||||
'packages/typert/generator': 'Build-time generator run outside any agent runtime.',
|
'packages/typert/generator': 'Build-time generator run outside any agent runtime.',
|
||||||
'packages/typert/protocol': 'Compiler-independent protocol declarations.',
|
'packages/typert/protocol': 'Compiler-independent protocol declarations.',
|
||||||
'packages/util/atomic-write': 'Zero-dependency filesystem write utility.',
|
'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/crypto': 'Zero-dependency identifier minting utility.',
|
||||||
'packages/util/deque': 'Zero-dependency circular deque utility.',
|
'packages/util/deque': 'Zero-dependency circular deque utility.',
|
||||||
'packages/util/home-paths': 'Zero-dependency harness-home path resolver.',
|
'packages/util/home-paths': 'Zero-dependency harness-home path resolver.',
|
||||||
|
|
@ -85,6 +85,7 @@ const PACKAGE_LIBRARIES: Readonly<Record<string, string>> = {
|
||||||
'packages/util/output-retention': 'Zero-dependency retention utility.',
|
'packages/util/output-retention': 'Zero-dependency retention utility.',
|
||||||
'packages/util/time': 'Zero-dependency time-zone canonicalization utility.',
|
'packages/util/time': 'Zero-dependency time-zone canonicalization utility.',
|
||||||
'packages/util/timeout': 'Zero-dependency timeout 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.',
|
'packages/util/workspace-path': 'Zero-dependency Workspace path formatter.',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -493,7 +493,9 @@ export const LINK_MAP: Readonly<Record<string, string>> = {
|
||||||
SubagentStartRequest: 'subagent.md',
|
SubagentStartRequest: 'subagent.md',
|
||||||
AssembleContext: 'system-prompt.md',
|
AssembleContext: 'system-prompt.md',
|
||||||
PromptContext: 'system-prompt.md',
|
PromptContext: 'system-prompt.md',
|
||||||
|
PromptContextOrderName: 'system-prompt.md',
|
||||||
PromptSection: 'system-prompt.md',
|
PromptSection: 'system-prompt.md',
|
||||||
|
PromptSectionOrderName: 'system-prompt.md',
|
||||||
SystemPrompt: 'system-prompt.md',
|
SystemPrompt: 'system-prompt.md',
|
||||||
ToolProviderResult: 'system-prompt.md',
|
ToolProviderResult: 'system-prompt.md',
|
||||||
JobDoneListener: 'jobs.md',
|
JobDoneListener: 'jobs.md',
|
||||||
|
|
@ -533,7 +535,9 @@ export const LINK_MAP: Readonly<Record<string, string>> = {
|
||||||
ToolRestriction: 'tools.md',
|
ToolRestriction: 'tools.md',
|
||||||
ToolSchema: 'tools.md',
|
ToolSchema: 'tools.md',
|
||||||
SettingsNamespace: 'settings.md',
|
SettingsNamespace: 'settings.md',
|
||||||
|
SettingsNamespaceInput: 'settings.md',
|
||||||
SettingsRegisterOptions: 'settings.md',
|
SettingsRegisterOptions: 'settings.md',
|
||||||
|
SettingsSectionHooks: 'settings.md',
|
||||||
SettingsScope: 'settings.md',
|
SettingsScope: 'settings.md',
|
||||||
SettingsDescriptor: 'settings.md',
|
SettingsDescriptor: 'settings.md',
|
||||||
SettingsDescribeValue: 'settings.md',
|
SettingsDescribeValue: 'settings.md',
|
||||||
|
|
|
||||||
|
|
@ -29,29 +29,28 @@ const CONFIGURATION_ONLY_DEV_DEPENDENCIES = {
|
||||||
'@deepseek-ai/dsh-client-ui-tool': ['@deepseek-ai/dsh-api-remotes'],
|
'@deepseek-ai/dsh-client-ui-tool': ['@deepseek-ai/dsh-api-remotes'],
|
||||||
} as const satisfies Readonly<Record<string, readonly string[]>>
|
} as const satisfies Readonly<Record<string, readonly string[]>>
|
||||||
|
|
||||||
|
/** 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.
|
* Runtime exports whose values remain valid when npm installs another package copy.
|
||||||
*/
|
*/
|
||||||
const SAFE_HOST_DEPENDENCY_EXPORTS = {
|
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-credentials': ['credentialKey'],
|
||||||
'@deepseek-ai/dsh-deque': ['Deque'],
|
'@deepseek-ai/dsh-deque': ['Deque'],
|
||||||
'@deepseek-ai/dsh-llm': ['MessageId', 'callConfigEquals', 'deepFreeze', 'freezeMessage'],
|
'@deepseek-ai/dsh-llm': ['callConfigEquals'],
|
||||||
'@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-timeout': ['MAX_TIMER_DELAY_MS'],
|
'@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'],
|
'@deepseek-ai/schemastery': ['default'],
|
||||||
} as const satisfies HostDependencyExports
|
} as const satisfies HostDependencyExports
|
||||||
|
|
||||||
/** Runtime exports that require every consumer to resolve the provider's shared peer instance. */
|
/** Runtime exports that require every consumer to resolve the provider's shared peer instance. */
|
||||||
const PEER_REQUIRED_HOST_EXPORTS = {
|
const PEER_REQUIRED_HOST_EXPORTS = {
|
||||||
'@deepseek-ai/dsh-scope': ['carrierKeyOf', 'scopeOf', 'scopeTarget'],
|
'@deepseek-ai/dsh-scope': ['carrierKeyOf', 'scopeOf', 'scopeTarget'],
|
||||||
'@deepseek-ai/dsh-typert-protocol': ['Remote', 'TypertRemoteService', 'remoteMethods'],
|
|
||||||
} as const satisfies HostDependencyExports
|
} as const satisfies HostDependencyExports
|
||||||
|
|
||||||
/** Exact import specifier to reviewed runtime exports. */
|
/** Exact import specifier to reviewed runtime exports. */
|
||||||
|
|
@ -63,6 +62,7 @@ export interface PackageDependencyPolicy {
|
||||||
readonly clientFaceExclude: readonly string[]
|
readonly clientFaceExclude: readonly string[]
|
||||||
readonly hostPackages: readonly string[]
|
readonly hostPackages: readonly string[]
|
||||||
readonly configurationOnlyDevDependencies: Readonly<Record<string, readonly string[]>>
|
readonly configurationOnlyDevDependencies: Readonly<Record<string, readonly string[]>>
|
||||||
|
readonly duplicateSafePackages?: readonly string[]
|
||||||
readonly safeHostDependencyExports: HostDependencyExports
|
readonly safeHostDependencyExports: HostDependencyExports
|
||||||
readonly peerRequiredHostExports: HostDependencyExports
|
readonly peerRequiredHostExports: HostDependencyExports
|
||||||
}
|
}
|
||||||
|
|
@ -73,6 +73,7 @@ export const PACKAGE_DEPENDENCY_POLICY: PackageDependencyPolicy = {
|
||||||
clientFaceExclude: CLIENT_FACE_EXCLUDE,
|
clientFaceExclude: CLIENT_FACE_EXCLUDE,
|
||||||
hostPackages: HOST_DEPENDENCY_PACKAGES,
|
hostPackages: HOST_DEPENDENCY_PACKAGES,
|
||||||
configurationOnlyDevDependencies: CONFIGURATION_ONLY_DEV_DEPENDENCIES,
|
configurationOnlyDevDependencies: CONFIGURATION_ONLY_DEV_DEPENDENCIES,
|
||||||
|
duplicateSafePackages: DUPLICATE_SAFE_PACKAGES,
|
||||||
safeHostDependencyExports: SAFE_HOST_DEPENDENCY_EXPORTS,
|
safeHostDependencyExports: SAFE_HOST_DEPENDENCY_EXPORTS,
|
||||||
peerRequiredHostExports: PEER_REQUIRED_HOST_EXPORTS,
|
peerRequiredHostExports: PEER_REQUIRED_HOST_EXPORTS,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,39 @@ function facts(manifest: PackageDependencyManifest): PackageDependencyFacts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hostRuntimeFixture(): {
|
||||||
|
provider: WorkspacePackageManifest
|
||||||
|
workspaceNames: Set<string>
|
||||||
|
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', () => {
|
describe('package dependency scope', () => {
|
||||||
it('keeps the measured Host relay roster explicit', () => {
|
it('keeps the measured Host relay roster explicit', () => {
|
||||||
expect(PACKAGE_DEPENDENCY_POLICY.clientFaceExclude).toEqual([
|
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-theme': ['@deepseek-ai/dsh-api-remotes'],
|
||||||
'@deepseek-ai/dsh-client-ui-tool': ['@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/dsh-deque']).toEqual(['Deque'])
|
||||||
expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/schemastery']).toEqual(['default'])
|
expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/schemastery']).toEqual(['default'])
|
||||||
expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/dsh-session/types']).toEqual([
|
expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/dsh-session/types']).toBeUndefined()
|
||||||
'SessionId',
|
expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/dsh-typert-protocol']).toBeUndefined()
|
||||||
])
|
|
||||||
expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/dsh-typert-protocol']).toEqual([
|
|
||||||
'RemoteError', 'remoteErrorOf',
|
|
||||||
])
|
|
||||||
expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-scope']).toEqual([
|
expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-scope']).toEqual([
|
||||||
'carrierKeyOf', 'scopeOf', 'scopeTarget',
|
'carrierKeyOf', 'scopeOf', 'scopeTarget',
|
||||||
])
|
])
|
||||||
expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-typert-protocol']).toEqual([
|
expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-typert-protocol']).toBeUndefined()
|
||||||
'Remote', 'TypertRemoteService', 'remoteMethods',
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('discovers the Client directory, dsh.client declarations, and configured Host packages', () => {
|
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', () => {
|
it('rejects stale, duplicate, and unbounded safe Host export entries', () => {
|
||||||
const consumer = pkg('@f/consumer', 'packages/core/consumer/package.json')
|
const { provider, workspaceNames, consumerFacts } = hostRuntimeFixture()
|
||||||
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(),
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(collectHostDependencyExportPolicyViolations(
|
expect(collectHostDependencyExportPolicyViolations(
|
||||||
[consumerFacts],
|
[consumerFacts],
|
||||||
|
|
@ -217,6 +228,29 @@ describe('package dependency scope', () => {
|
||||||
expect.stringContaining('appears in both Host export classifications'),
|
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', () => {
|
describe('face-aware source classification', () => {
|
||||||
|
|
|
||||||
|
|
@ -374,10 +374,19 @@ export function readPackageDependencyFacts(
|
||||||
export function collectHostDependencyExportPolicyViolations(
|
export function collectHostDependencyExportPolicyViolations(
|
||||||
facts: readonly PackageDependencyFacts[],
|
facts: readonly PackageDependencyFacts[],
|
||||||
workspaceNames: ReadonlySet<string>,
|
workspaceNames: ReadonlySet<string>,
|
||||||
policy: Pick<PackageDependencyPolicy, 'peerRequiredHostExports' | 'safeHostDependencyExports'>,
|
policy: Pick<PackageDependencyPolicy, 'duplicateSafePackages' | 'peerRequiredHostExports' | 'safeHostDependencyExports'>,
|
||||||
): string[] {
|
): string[] {
|
||||||
const violations: string[] = []
|
const violations: string[] = []
|
||||||
const allRuntimeUses = facts.flatMap(fact => fact.hostRuntimeExportUses)
|
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 = [
|
const classifications = [
|
||||||
['safeHostDependencyExports', policy.safeHostDependencyExports],
|
['safeHostDependencyExports', policy.safeHostDependencyExports],
|
||||||
['peerRequiredHostExports', policy.peerRequiredHostExports],
|
['peerRequiredHostExports', policy.peerRequiredHostExports],
|
||||||
|
|
@ -387,6 +396,8 @@ export function collectHostDependencyExportPolicyViolations(
|
||||||
const provider = packageNameOf(specifier)
|
const provider = packageNameOf(specifier)
|
||||||
if (provider === undefined || !workspaceNames.has(provider)) {
|
if (provider === undefined || !workspaceNames.has(provider)) {
|
||||||
violations.push(`${field} specifier ${specifier} is not a workspace package`)
|
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) {
|
if (exportNames.length === 0) {
|
||||||
violations.push(`${field} lists no exports for ${specifier}`)
|
violations.push(`${field} lists no exports for ${specifier}`)
|
||||||
|
|
@ -414,6 +425,7 @@ export function collectHostDependencyExportPolicyViolations(
|
||||||
for (const use of fact.hostRuntimeExportUses) {
|
for (const use of fact.hostRuntimeExportUses) {
|
||||||
if (use.packageName === fact.manifest.name || use.packageName === CORDIS) continue
|
if (use.packageName === fact.manifest.name || use.packageName === CORDIS) continue
|
||||||
if (!workspaceNames.has(use.packageName)) 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.safeHostDependencyExports[use.specifier]?.includes(use.exportName) === true) continue
|
||||||
if (policy.peerRequiredHostExports[use.specifier]?.includes(use.exportName) === true) continue
|
if (policy.peerRequiredHostExports[use.specifier]?.includes(use.exportName) === true) continue
|
||||||
violations.push(
|
violations.push(
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ const CANONICAL = '## Known Limitations and Deferred Work'
|
||||||
|
|
||||||
/** Packages audited as having no limitations section, keyed by repo-relative directory. */
|
/** Packages audited as having no limitations section, keyed by repo-relative directory. */
|
||||||
const NO_LIMITATIONS: Readonly<Record<string, string>> = {
|
const NO_LIMITATIONS: Readonly<Record<string, string>> = {
|
||||||
'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. */
|
/** A heading that reads as a limitations section — canonical or drifted. */
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,11 @@ interface SentenceContract {
|
||||||
*/
|
*/
|
||||||
const NO_MODEL_EXPERIENCE_SECTION: Readonly<Record<string, string>> = {
|
const NO_MODEL_EXPERIENCE_SECTION: Readonly<Record<string, string>> = {
|
||||||
'packages/core/scope': 'The package is a model-agnostic registration and lifecycle primitive; model-facing consumers own any context selection.',
|
'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/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/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/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.',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue