fix(invariants): join package checks at startup
This commit is contained in:
@@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import { CodeRuntime } from '@deepseek-ai/dsh-code-runtime'
|
||||
import type { CodeRunRequest, CodeRunResult } from '@deepseek-ai/dsh-code-runtime'
|
||||
import { InvariantError } from '@deepseek-ai/dsh-invariants'
|
||||
|
||||
/**
|
||||
* Minimal concrete runtime: records requests, "executes" by invoking every
|
||||
@@ -96,6 +97,13 @@ describe('CodeRuntime service seam', () => {
|
||||
child.provide('codeRuntime', value as unknown as CodeRuntime)
|
||||
},
|
||||
}
|
||||
await expect(ctx.plugin(invalidRuntime)).rejects.toThrow(message)
|
||||
let caught: unknown
|
||||
try {
|
||||
await ctx.plugin(invalidRuntime)
|
||||
} catch (error) {
|
||||
caught = error
|
||||
}
|
||||
expect(caught).toBeInstanceOf(InvariantError)
|
||||
expect((caught as Error).message).toMatch(message)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,13 +12,10 @@ export const name = 'jsonrpc-demo-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert that Loader configuration, rather than a hidden root plugin, owns composition. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const packageEntry = await import('./index.ts')
|
||||
assertInvariant(fail, Object.keys(packageEntry).length === 0,
|
||||
'the JSON-RPC demo library entrypoint must remain empty because cordis.yml owns composition')
|
||||
return () => {}
|
||||
}, 'jsonrpc-demo: validate bin-only entrypoint')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const packageEntry = await import('./index.ts')
|
||||
assertInvariant(fail, Object.keys(packageEntry).length === 0,
|
||||
'the JSON-RPC demo library entrypoint must remain empty because cordis.yml owns composition')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,22 +12,21 @@ export const name = 'hook-protocol-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert blocking-exit decoding and restrictive merge precedence. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { parseHookOutput } = await import('./codec.ts')
|
||||
const { mergeHookOutputs } = await import('./merge.ts')
|
||||
const blocked = parseHookOutput(2, '', ' denied ')
|
||||
assertInvariant(fail, blocked.decision === 'block' && blocked.reason === 'denied',
|
||||
'exit 2 must decode as a block whose reason is trimmed stderr')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const [{ parseHookOutput }, { mergeHookOutputs }] = await Promise.all([
|
||||
import('./codec.ts'),
|
||||
import('./merge.ts'),
|
||||
])
|
||||
const blocked = parseHookOutput(2, '', ' denied ')
|
||||
assertInvariant(fail, blocked.decision === 'block' && blocked.reason === 'denied',
|
||||
'exit 2 must decode as a block whose reason is trimmed stderr')
|
||||
|
||||
const merged = mergeHookOutputs([
|
||||
{ exitCode: 0, stderr: '', stdout: '', decision: 'allow', reason: 'permitted' },
|
||||
{ exitCode: 0, stderr: '', stdout: '', decision: 'deny', reason: 'forbidden' },
|
||||
])
|
||||
assertInvariant(fail, merged.decision === 'deny' && merged.reason === 'forbidden',
|
||||
'deny must override allow and retain only the winning decision reason')
|
||||
return () => {}
|
||||
}, 'hook-protocol: validate decode and merge algebra')
|
||||
const merged = mergeHookOutputs([
|
||||
{ exitCode: 0, stderr: '', stdout: '', decision: 'allow', reason: 'permitted' },
|
||||
{ exitCode: 0, stderr: '', stdout: '', decision: 'deny', reason: 'forbidden' },
|
||||
])
|
||||
assertInvariant(fail, merged.decision === 'deny' && merged.reason === 'forbidden',
|
||||
'deny must override allow and retain only the winning decision reason')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
43
packages/sandbox/sandbox/tests/invariant.spec.ts
Normal file
43
packages/sandbox/sandbox/tests/invariant.spec.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import { InvariantError } from '@deepseek-ai/dsh-invariants'
|
||||
import { SandboxProvider } from '@deepseek-ai/dsh-sandbox'
|
||||
import type { ConfinedArgv, SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
|
||||
|
||||
class StubSandboxProvider extends SandboxProvider {
|
||||
confine(argv: readonly string[], _policy: SandboxPolicy): ConfinedArgv {
|
||||
return {
|
||||
argv: [...argv],
|
||||
enforcement: 'full',
|
||||
denialSignatures: [],
|
||||
runnerFailureSignatures: [],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe('sandbox package invariant', () => {
|
||||
it('accepts a provider that exposes the confinement seam', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(StubSandboxProvider)
|
||||
expect(ctx.sandbox).toBeInstanceOf(StubSandboxProvider)
|
||||
})
|
||||
|
||||
it('rejects a service binding without confine()', async () => {
|
||||
const ctx = new Context()
|
||||
const invalidSandbox = {
|
||||
name: 'invalid-sandbox',
|
||||
apply(child: Context) {
|
||||
child.provide('sandbox', {} as SandboxProvider)
|
||||
},
|
||||
}
|
||||
let caught: unknown
|
||||
try {
|
||||
await ctx.plugin(invalidSandbox)
|
||||
} catch (error) {
|
||||
caught = error
|
||||
}
|
||||
expect(caught).toBeInstanceOf(InvariantError)
|
||||
expect(caught).toHaveProperty('packageName', '@deepseek-ai/dsh-sandbox')
|
||||
expect((caught as Error).message).toMatch(/must expose method "confine"/)
|
||||
})
|
||||
})
|
||||
@@ -12,24 +12,23 @@ export const name = 'create-sdk-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert the bin-only entrypoint and its core argument mapping. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { parseCreateArgs } = await import('./args.ts')
|
||||
const packageEntry = await import('./index.ts')
|
||||
assertInvariant(fail, Object.keys(packageEntry).length === 0,
|
||||
'the create-sdk library entrypoint must remain empty because the package is bin-only')
|
||||
const parsed = parseCreateArgs([
|
||||
'workspace', '--provider=custom', '--base-url=https://example.test', '--interface=embed', '--no-install',
|
||||
])
|
||||
assertInvariant(fail,
|
||||
parsed.directory === 'workspace'
|
||||
&& parsed.provider === 'custom'
|
||||
&& parsed.baseURL === 'https://example.test'
|
||||
&& parsed.runInterface === 'embed'
|
||||
&& parsed.install === false,
|
||||
'create-sdk arguments must preserve directory, provider, base URL, interface, and negative install flags')
|
||||
return () => {}
|
||||
}, 'create-sdk: validate bin and argument contracts')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const [{ parseCreateArgs }, packageEntry] = await Promise.all([
|
||||
import('./args.ts'),
|
||||
import('./index.ts'),
|
||||
])
|
||||
assertInvariant(fail, Object.keys(packageEntry).length === 0,
|
||||
'the create-sdk library entrypoint must remain empty because the package is bin-only')
|
||||
const parsed = parseCreateArgs([
|
||||
'workspace', '--provider=custom', '--base-url=https://example.test', '--interface=embed', '--no-install',
|
||||
])
|
||||
assertInvariant(fail,
|
||||
parsed.directory === 'workspace'
|
||||
&& parsed.provider === 'custom'
|
||||
&& parsed.baseURL === 'https://example.test'
|
||||
&& parsed.runInterface === 'embed'
|
||||
&& parsed.install === false,
|
||||
'create-sdk arguments must preserve directory, provider, base URL, interface, and negative install flags')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,20 +12,17 @@ export const name = 'helper-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert FeatureId's zero-cost representation and boundary validation. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { featureId } = await import('./ids.ts')
|
||||
assertInvariant(fail, featureId('local-plugin') === 'local-plugin',
|
||||
'a valid feature id must preserve its runtime string value')
|
||||
let rejected = false
|
||||
try {
|
||||
featureId('Invalid Feature')
|
||||
} catch (error) {
|
||||
rejected = error instanceof Error
|
||||
}
|
||||
assertInvariant(fail, rejected, 'feature ids must reject values outside lowercase kebab-case')
|
||||
return () => {}
|
||||
}, 'dsh-helper: validate feature identities')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const { featureId } = await import('./ids.ts')
|
||||
assertInvariant(fail, featureId('local-plugin') === 'local-plugin',
|
||||
'a valid feature id must preserve its runtime string value')
|
||||
let rejected = false
|
||||
try {
|
||||
featureId('Invalid Feature')
|
||||
} catch (error) {
|
||||
rejected = error instanceof Error
|
||||
}
|
||||
assertInvariant(fail, rejected, 'feature ids must reject values outside lowercase kebab-case')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,22 +12,19 @@ export const name = 'scripts-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert the launcher's opaque post-separator forwarding boundary. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { splitForwardedArgs } = await import('./forwarding.ts')
|
||||
const plain = splitForwardedArgs(['dev', 'src/index.ts'])
|
||||
const separated = splitForwardedArgs(['dev', 'src/index.ts', '--', '--inspect', '9229'])
|
||||
assertInvariant(fail,
|
||||
plain.launcher.length === 2
|
||||
&& plain.forwarded.length === 0
|
||||
&& separated.launcher.length === 2
|
||||
&& separated.launcher[1] === 'src/index.ts'
|
||||
&& separated.forwarded.length === 2
|
||||
&& separated.forwarded[0] === '--inspect'
|
||||
&& separated.forwarded[1] === '9229',
|
||||
'dsh-sdk must split the first delimiter without interpreting forwarded runtime arguments')
|
||||
return () => {}
|
||||
}, 'dsh-sdk: validate command argument contracts')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const { splitForwardedArgs } = await import('./forwarding.ts')
|
||||
const plain = splitForwardedArgs(['dev', 'src/index.ts'])
|
||||
const separated = splitForwardedArgs(['dev', 'src/index.ts', '--', '--inspect', '9229'])
|
||||
assertInvariant(fail,
|
||||
plain.launcher.length === 2
|
||||
&& plain.forwarded.length === 0
|
||||
&& separated.launcher.length === 2
|
||||
&& separated.launcher[1] === 'src/index.ts'
|
||||
&& separated.forwarded.length === 2
|
||||
&& separated.forwarded[0] === '--inspect'
|
||||
&& separated.forwarded[1] === '9229',
|
||||
'dsh-sdk must split the first delimiter without interpreting forwarded runtime arguments')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,19 +12,16 @@ export const name = 'telemetry-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert the final telemetry redaction boundary removes secrets without corrupting ordinary package metadata. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const [{ DEFAULT_REDACTION_PLACEHOLDER, SecretRedactor }, { telemetryRedactionViolation }] = await Promise.all([
|
||||
import('./secret-redactor.ts'),
|
||||
import('./redaction-contract.ts'),
|
||||
])
|
||||
const redactor = new SecretRedactor()
|
||||
const violation = telemetryRedactionViolation(redactor, DEFAULT_REDACTION_PLACEHOLDER, PACKAGE_NAME)
|
||||
assertInvariant(fail,
|
||||
violation === undefined,
|
||||
violation ?? 'telemetry redaction contract failed without a diagnostic')
|
||||
return () => {}
|
||||
}, 'telemetry: validate secret-redaction boundary')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const [{ telemetryRedactionViolation }, { DEFAULT_REDACTION_PLACEHOLDER, SecretRedactor }] = await Promise.all([
|
||||
import('./redaction-contract.ts'),
|
||||
import('./secret-redactor.ts'),
|
||||
])
|
||||
const redactor = new SecretRedactor()
|
||||
const violation = telemetryRedactionViolation(redactor, DEFAULT_REDACTION_PLACEHOLDER, PACKAGE_NAME)
|
||||
assertInvariant(fail,
|
||||
violation === undefined,
|
||||
violation ?? 'telemetry redaction contract failed without a diagnostic')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,15 +12,12 @@ export const name = 'subagent-inprocess-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert that structured-output guidance names the tool it actually installs. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { STRUCTURED_OUTPUT_INSTRUCTION, STRUCTURED_OUTPUT_TOOL } = await import('./structured-protocol.ts')
|
||||
assertInvariant(fail, /^[a-z][a-z0-9_]*$/.test(STRUCTURED_OUTPUT_TOOL),
|
||||
'the structured-output tool must retain a stable lowercase protocol name')
|
||||
assertInvariant(fail, STRUCTURED_OUTPUT_INSTRUCTION.includes(STRUCTURED_OUTPUT_TOOL),
|
||||
'the structured-output instruction must name the exact installed tool')
|
||||
return () => {}
|
||||
}, 'subagent-inprocess: validate structured-output protocol')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const { STRUCTURED_OUTPUT_INSTRUCTION, STRUCTURED_OUTPUT_TOOL } = await import('./structured-protocol.ts')
|
||||
assertInvariant(fail, /^[a-z][a-z0-9_]*$/.test(STRUCTURED_OUTPUT_TOOL),
|
||||
'the structured-output tool must retain a stable lowercase protocol name')
|
||||
assertInvariant(fail, STRUCTURED_OUTPUT_INSTRUCTION.includes(STRUCTURED_OUTPUT_TOOL),
|
||||
'the structured-output instruction must name the exact installed tool')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,19 +13,24 @@ export const name = 'subagent-subprocess-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert ambient credential scrubbing and explicit credential precedence. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { buildChildEnv } = await import('./index.ts')
|
||||
const scrubbed = buildChildEnv({})
|
||||
const ambientSensitiveNames = Object.keys(process.env).filter(key => SENSITIVE_ENV_PATTERN.test(key))
|
||||
assertInvariant(fail, ambientSensitiveNames.every(key => !Object.hasOwn(scrubbed, key)),
|
||||
'subprocess environments must omit every credential-shaped ambient variable')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const { buildChildEnv } = await import('./index.ts')
|
||||
const ambientProbe = `DSH_INVARIANT_AMBIENT_TOKEN_${process.pid}`
|
||||
assertInvariant(fail, SENSITIVE_ENV_PATTERN.test(ambientProbe),
|
||||
'the invariant ambient probe must remain credential-shaped')
|
||||
process.env[ambientProbe] = 'must-not-reach-child'
|
||||
let scrubbed: NodeJS.ProcessEnv
|
||||
try {
|
||||
scrubbed = buildChildEnv({})
|
||||
} finally {
|
||||
Reflect.deleteProperty(process.env, ambientProbe)
|
||||
}
|
||||
assertInvariant(fail, !Object.hasOwn(scrubbed, ambientProbe),
|
||||
'subprocess environments must omit every credential-shaped ambient variable')
|
||||
|
||||
const explicit = buildChildEnv({ DSH_INVARIANT_TOKEN: 'explicit-child-value' })
|
||||
assertInvariant(fail, explicit.DSH_INVARIANT_TOKEN === 'explicit-child-value',
|
||||
'explicit child credentials must be applied after ambient scrubbing')
|
||||
return () => {}
|
||||
}, 'subagent-subprocess: validate child environment isolation')
|
||||
const explicit = buildChildEnv({ DSH_INVARIANT_TOKEN: 'explicit-child-value' })
|
||||
assertInvariant(fail, explicit.DSH_INVARIANT_TOKEN === 'explicit-child-value',
|
||||
'explicit child credentials must be applied after ambient scrubbing')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,25 +12,22 @@ export const name = 'acp-snapshot-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert stable JSON-RPC correlation and volatile-value tokenization. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { normalizeStdout } = await import('./normalize.ts')
|
||||
const sessionId = '12345678-1234-1234-1234-123456789abc'
|
||||
const volatile = { sessionIds: [sessionId], cwd: '/tmp/dsh-acp-invariant' }
|
||||
const raw = [
|
||||
JSON.stringify({ jsonrpc: '2.0', id: 'request-7', result: { cwd: volatile.cwd } }),
|
||||
JSON.stringify({ jsonrpc: '2.0', id: 'request-7', result: { sessionId } }),
|
||||
].join('\n')
|
||||
const normalized = normalizeStdout(raw, volatile)
|
||||
assertInvariant(fail,
|
||||
normalized.includes('"id":1')
|
||||
&& normalized.includes('"cwd":"{{cwd}}"')
|
||||
&& normalized.includes('"sessionId":"{{sessionId}}"'),
|
||||
'ACP normalization must preserve RPC correlation while tokenizing cwd and session ids')
|
||||
assertInvariant(fail, normalizeStdout(normalized, volatile) === normalized,
|
||||
'ACP stdout normalization must be idempotent')
|
||||
return () => {}
|
||||
}, 'acp-snapshot: validate stable transcript normalization')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const { normalizeStdout } = await import('./normalize.ts')
|
||||
const sessionId = '12345678-1234-1234-1234-123456789abc'
|
||||
const volatile = { sessionIds: [sessionId], cwd: '/tmp/dsh-acp-invariant' }
|
||||
const raw = [
|
||||
JSON.stringify({ jsonrpc: '2.0', id: 'request-7', result: { cwd: volatile.cwd } }),
|
||||
JSON.stringify({ jsonrpc: '2.0', id: 'request-7', result: { sessionId } }),
|
||||
].join('\n')
|
||||
const normalized = normalizeStdout(raw, volatile)
|
||||
assertInvariant(fail,
|
||||
normalized.includes('"id":1')
|
||||
&& normalized.includes('"cwd":"{{cwd}}"')
|
||||
&& normalized.includes('"sessionId":"{{sessionId}}"'),
|
||||
'ACP normalization must preserve RPC correlation while tokenizing cwd and session ids')
|
||||
assertInvariant(fail, normalizeStdout(normalized, volatile) === normalized,
|
||||
'ACP stdout normalization must be idempotent')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,16 +12,13 @@ export const name = 'agent-loop-testkit-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert the awaitable helper shape and optional-options call boundary. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { mountAgentLoopTestDependencies } = await import('./index.ts')
|
||||
assertInvariant(fail,
|
||||
mountAgentLoopTestDependencies.constructor.name === 'AsyncFunction',
|
||||
'the prerequisite mount helper must remain awaitable so tests cannot race service activation')
|
||||
assertInvariant(fail, mountAgentLoopTestDependencies.length === 1,
|
||||
'the prerequisite mount helper must keep its options argument optional')
|
||||
return () => {}
|
||||
}, 'agent-loop-testkit: validate prerequisite mount boundary')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const { mountAgentLoopTestDependencies } = await import('./index.ts')
|
||||
assertInvariant(fail,
|
||||
mountAgentLoopTestDependencies.constructor.name === 'AsyncFunction',
|
||||
'the prerequisite mount helper must remain awaitable so tests cannot race service activation')
|
||||
assertInvariant(fail, mountAgentLoopTestDependencies.length === 1,
|
||||
'the prerequisite mount helper must keep its options argument optional')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,7 +16,7 @@ Defaults are `enabled: true`, `package_allowlist: []`, and `package_blocklist: [
|
||||
|
||||
Each entry is a case-sensitive JavaScript regular-expression source compiled with `new RegExp(pattern)`. Matching is unanchored unless the source supplies `^` and `$`; `/pattern/flags` syntax is not parsed. Blank, whitespace-padded, invalid, or duplicate entries within one list fail service startup. A valid pattern may match no currently loaded package so later loading and HMR remain deterministic.
|
||||
|
||||
`ctx.invariants.register(packageName, installer)` reserves one active registration for the full npm package name, including when filters keep its installer inactive, and returns its disposer. An enabled contribution runs in a dedicated child Cordis fiber. The installer can declare its required service surface through `installer.inject` and receives `fail(message)`, which throws an `InvariantError` bound to the registering package. Installer failure disposes the child and releases ownership atomically.
|
||||
`ctx.invariants.register(packageName, installer)` reserves one active registration for the full npm package name, including when filters keep its installer inactive, and returns its disposer. An enabled contribution runs in a dedicated child Cordis fiber. The installer can declare its required service surface through `installer.inject` and receives `fail(message)`, which throws an `InvariantError` bound to the registering package. Synchronous or asynchronous installer completion is joined before registration succeeds; failure disposes the child and releases ownership atomically.
|
||||
|
||||
The service owns every registration fiber, while the returned disposer also belongs to the companion fiber. Unloading either side removes the listeners and reservation completely. A companion can therefore reload and register the same package name without retaining trace state or duplicate listeners; packages that need an existing baseline rebuild it during installation.
|
||||
|
||||
@@ -32,7 +32,7 @@ Packages select the narrowest runtime form that protects their public contract:
|
||||
|---|---|
|
||||
| Cordis plugin | `observePluginInvariant` validates the plugin's own declared name, required injections, owned effect group, provided services, and optional package-specific relation for existing, late, and HMR-activated fibers. |
|
||||
| Cordis service seam | `observeServiceInvariant` plus `serviceShapeViolation` validates current and future structural implementations, including conforming third-party backends and test doubles. |
|
||||
| Pure library, bin, or support package | `assertInvariant` checks stable protocol algebra, parser mapping, path/timeout/retention rules, normalization, or entrypoint shape in a child effect. |
|
||||
| Pure library, bin, or support package | `assertInvariant` checks stable protocol algebra, parser mapping, path/timeout/retention rules, normalization, or entrypoint shape during child startup. |
|
||||
|
||||
Four companions additionally install stateful event and request checks:
|
||||
|
||||
@@ -62,7 +62,7 @@ ctx.plugin(InvariantService, {
|
||||
ctx.plugin(SessionInvariant)
|
||||
```
|
||||
|
||||
The standard agent spine mounts the service and the four stateful companions. Custom compositions explicitly add the companions for the packages whose contracts they want checked and may disable or filter them without changing package entrypoints. Vitest mounts every package companion against an explicitly enabled service for ordinary Cordis roots, so all package checks execute across unit, snapshot, and e2e suites; focused invariant-service tests construct their own topology to exercise filtering and lifecycle behavior.
|
||||
The standard agent spine mounts the service and the four stateful companions. Custom compositions explicitly add the companions for the packages whose contracts they want checked and may disable or filter them without changing package entrypoints. Plugin and service helpers multiplex package contracts through indexed lifecycle listeners shared by the Cordis root, while contribution disposal removes only that owner's contract. Vitest gives every ordinary root an explicitly enabled service and mounts the current test package's companion; one exhaustive topology test mounts all companions once, and focused invariant-service tests construct their own topology to exercise filtering and lifecycle behavior.
|
||||
|
||||
## Model Experience
|
||||
|
||||
|
||||
@@ -28,15 +28,15 @@ export interface Config {
|
||||
*/
|
||||
export type InvariantFailure = (message: string) => never
|
||||
|
||||
/** Install one package's listeners into the registration's child context. */
|
||||
/** Install one package's checks into the registration's child context. */
|
||||
export interface InvariantInstaller {
|
||||
/**
|
||||
* Install the package contribution.
|
||||
* @param ctx - child context owned by this invariant registration.
|
||||
* @param fail - reporter bound to the registering package name.
|
||||
* @returns nothing after synchronous listener installation completes.
|
||||
* @returns nothing, or a promise settling after asynchronous checks finish.
|
||||
*/
|
||||
(ctx: Context, fail: InvariantFailure): void
|
||||
(ctx: Context, fail: InvariantFailure): void | Promise<void>
|
||||
/** Services the child installer fiber may access. */
|
||||
readonly inject?: Inject
|
||||
}
|
||||
@@ -70,11 +70,111 @@ function collectEffectLabels(fiber: Fiber): ReadonlySet<string> {
|
||||
return labels
|
||||
}
|
||||
|
||||
/** One package check routed by a root-shared plugin lifecycle dispatcher. */
|
||||
interface PluginObservation {
|
||||
readonly callback: globalThis.Function | undefined
|
||||
readonly contract: PluginInvariantContract
|
||||
readonly fail: InvariantFailure
|
||||
}
|
||||
|
||||
/** Indexed plugin checks and the two lifecycle listeners shared by one root. */
|
||||
interface PluginObservationHub {
|
||||
readonly byCallback: Map<globalThis.Function, Set<PluginObservation>>
|
||||
readonly byName: Map<string, Set<PluginObservation>>
|
||||
}
|
||||
|
||||
const pluginObservationHubs = new WeakMap<Context, PluginObservationHub>()
|
||||
|
||||
/** Check one already-matched active plugin fiber. */
|
||||
function inspectPluginObservation(observation: PluginObservation, fiber: Fiber): void {
|
||||
if (fiber.state !== FiberState.ACTIVE || fiber.uid === null) return
|
||||
const { callback, contract, fail } = observation
|
||||
if (callback !== undefined && fiber.name !== contract.name) {
|
||||
fail(`active plugin name must be ${JSON.stringify(contract.name)}, got ${JSON.stringify(fiber.name)}`)
|
||||
}
|
||||
const injections = new Set(Object.keys(fiber.inject))
|
||||
for (const service of contract.inject ?? []) {
|
||||
if (!injections.has(service)) fail(`active plugin must inject ${JSON.stringify(service)}`)
|
||||
}
|
||||
|
||||
const effectLabels = collectEffectLabels(fiber)
|
||||
for (const requirement of contract.effects ?? []) {
|
||||
const alternatives = typeof requirement === 'string' ? [requirement] : requirement
|
||||
if (!alternatives.some(label => effectLabels.has(label))) {
|
||||
fail(`active plugin must own effect ${alternatives.map(label => JSON.stringify(label)).join(' or ')}`)
|
||||
}
|
||||
}
|
||||
for (const service of contract.services ?? []) {
|
||||
const provided = Reflect.ownKeys(fiber.ctx.reflect.store).some((key) => {
|
||||
const implementation = fiber.ctx.reflect.store[key as symbol]
|
||||
return implementation?.fiber === fiber && implementation.name === service
|
||||
})
|
||||
if (!provided) fail(`active plugin must provide service ${JSON.stringify(service)}`)
|
||||
}
|
||||
const message = contract.validate?.(fiber, effectLabels)
|
||||
if (message !== undefined) fail(message)
|
||||
}
|
||||
|
||||
/** Route one lifecycle notification only to checks that can match its runtime. */
|
||||
function inspectObservedPlugin(hub: PluginObservationHub, fiber: Fiber): void {
|
||||
const callback = fiber.runtime?.callback
|
||||
if (callback !== undefined) {
|
||||
for (const observation of hub.byCallback.get(callback) ?? []) {
|
||||
inspectPluginObservation(observation, fiber)
|
||||
}
|
||||
}
|
||||
const runtimeName = fiber.runtime?.name
|
||||
if (runtimeName !== undefined) {
|
||||
for (const observation of hub.byName.get(runtimeName) ?? []) {
|
||||
inspectPluginObservation(observation, fiber)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the root's shared plugin dispatcher, creating its two listeners once. */
|
||||
function pluginObservationHub(ctx: Context): PluginObservationHub {
|
||||
const root = ctx.root
|
||||
const existing = pluginObservationHubs.get(root)
|
||||
if (existing !== undefined) return existing
|
||||
|
||||
const hub: PluginObservationHub = {
|
||||
byCallback: new Map(),
|
||||
byName: new Map(),
|
||||
}
|
||||
pluginObservationHubs.set(root, hub)
|
||||
root.on('internal/plugin', (fiber) => { inspectObservedPlugin(hub, fiber) }, { global: true })
|
||||
root.on('internal/status', (fiber) => { inspectObservedPlugin(hub, fiber) }, { global: true })
|
||||
return hub
|
||||
}
|
||||
|
||||
/** Add one plugin observation to a typed exact-key index. */
|
||||
function addIndexedPluginObservation<Key>(
|
||||
index: Map<Key, Set<PluginObservation>>,
|
||||
key: Key,
|
||||
observation: PluginObservation,
|
||||
): () => void {
|
||||
const observations = index.get(key) ?? new Set<PluginObservation>()
|
||||
index.set(key, observations)
|
||||
observations.add(observation)
|
||||
return () => {
|
||||
observations.delete(observation)
|
||||
if (observations.size === 0) index.delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
/** Add one observation to its exact callback or runtime-name index. */
|
||||
function addPluginObservation(hub: PluginObservationHub, observation: PluginObservation): () => void {
|
||||
if (observation.callback === undefined) {
|
||||
return addIndexedPluginObservation(hub.byName, observation.contract.name, observation)
|
||||
}
|
||||
return addIndexedPluginObservation(hub.byCallback, observation.callback, observation)
|
||||
}
|
||||
|
||||
/**
|
||||
* Observe one package plugin and fail whenever an active fiber violates its
|
||||
* declared name, dependency, effect, service, or package-specific contract.
|
||||
* Existing fibers are checked immediately; later starts and HMR activations
|
||||
* are checked through Cordis lifecycle events.
|
||||
* are checked through two indexed lifecycle listeners shared by the root.
|
||||
* @param ctx - invariant child context that owns the observers.
|
||||
* @param fail - reporter bound to the package that owns the plugin.
|
||||
* @param contract - expected runtime facts for the package plugin.
|
||||
@@ -90,50 +190,79 @@ export function observePluginInvariant(
|
||||
fail('invariant contract does not identify a Cordis plugin')
|
||||
}
|
||||
|
||||
const inspect = (fiber: Fiber): void => {
|
||||
const matches = callback === undefined
|
||||
? fiber.runtime?.name === contract.name
|
||||
: fiber.runtime?.callback === callback
|
||||
if (!matches || fiber.state !== FiberState.ACTIVE) return
|
||||
if (callback !== undefined && fiber.name !== contract.name) {
|
||||
fail(`active plugin name must be ${JSON.stringify(contract.name)}, got ${JSON.stringify(fiber.name)}`)
|
||||
}
|
||||
const injections = new Set(Object.keys(fiber.inject))
|
||||
for (const service of contract.inject ?? []) {
|
||||
if (!injections.has(service)) fail(`active plugin must inject ${JSON.stringify(service)}`)
|
||||
}
|
||||
|
||||
const effectLabels = collectEffectLabels(fiber)
|
||||
for (const requirement of contract.effects ?? []) {
|
||||
const alternatives = typeof requirement === 'string' ? [requirement] : requirement
|
||||
if (!alternatives.some(label => effectLabels.has(label))) {
|
||||
fail(`active plugin must own effect ${alternatives.map(label => JSON.stringify(label)).join(' or ')}`)
|
||||
}
|
||||
}
|
||||
for (const service of contract.services ?? []) {
|
||||
const provided = Reflect.ownKeys(fiber.ctx.reflect.store).some((key) => {
|
||||
const implementation = fiber.ctx.reflect.store[key as symbol]
|
||||
return implementation?.fiber === fiber && implementation.name === service
|
||||
})
|
||||
if (!provided) fail(`active plugin must provide service ${JSON.stringify(service)}`)
|
||||
}
|
||||
const message = contract.validate?.(fiber, effectLabels)
|
||||
if (message !== undefined) fail(message)
|
||||
}
|
||||
const observation: PluginObservation = { callback, contract, fail }
|
||||
|
||||
if (contract.plugin === undefined) {
|
||||
for (const runtime of ctx.registry.values()) {
|
||||
for (const fiber of runtime.fibers) inspect(fiber)
|
||||
if (runtime.name !== contract.name) continue
|
||||
for (const fiber of runtime.fibers) inspectPluginObservation(observation, fiber)
|
||||
}
|
||||
} else {
|
||||
for (const fiber of ctx.registry.get(contract.plugin)?.fibers ?? []) inspect(fiber)
|
||||
for (const fiber of ctx.registry.get(contract.plugin)?.fibers ?? []) {
|
||||
inspectPluginObservation(observation, fiber)
|
||||
}
|
||||
}
|
||||
const hub = pluginObservationHub(ctx)
|
||||
ctx.effect(
|
||||
() => addPluginObservation(hub, observation),
|
||||
`invariants.observePlugin(${JSON.stringify(contract.name)})`,
|
||||
)
|
||||
}
|
||||
|
||||
/** One structural check routed by a root-shared service lifecycle dispatcher. */
|
||||
interface ServiceObservation {
|
||||
readonly fail: InvariantFailure
|
||||
readonly validate: (value: unknown) => string | undefined
|
||||
}
|
||||
|
||||
/** Service checks and the single service listener shared by one root. */
|
||||
interface ServiceObservationHub {
|
||||
readonly byName: Map<string, Set<ServiceObservation>>
|
||||
}
|
||||
|
||||
const serviceObservationHubs = new WeakMap<Context, ServiceObservationHub>()
|
||||
|
||||
/** Check one present service implementation. */
|
||||
function inspectServiceObservation(observation: ServiceObservation, value: unknown): void {
|
||||
if (value === undefined) return
|
||||
const message = observation.validate(value)
|
||||
if (message !== undefined) observation.fail(message)
|
||||
}
|
||||
|
||||
/** Return the root's shared service dispatcher, creating its listener once. */
|
||||
function serviceObservationHub(ctx: Context): ServiceObservationHub {
|
||||
const root = ctx.root
|
||||
const existing = serviceObservationHubs.get(root)
|
||||
if (existing !== undefined) return existing
|
||||
|
||||
const hub: ServiceObservationHub = { byName: new Map() }
|
||||
serviceObservationHubs.set(root, hub)
|
||||
root.on('internal/service', (name, value: unknown) => {
|
||||
for (const observation of hub.byName.get(name) ?? []) {
|
||||
inspectServiceObservation(observation, value)
|
||||
}
|
||||
}, { global: true })
|
||||
return hub
|
||||
}
|
||||
|
||||
/** Add one service observation to its exact service-name index. */
|
||||
function addServiceObservation(
|
||||
hub: ServiceObservationHub,
|
||||
serviceName: string,
|
||||
observation: ServiceObservation,
|
||||
): () => void {
|
||||
const observations = hub.byName.get(serviceName) ?? new Set<ServiceObservation>()
|
||||
hub.byName.set(serviceName, observations)
|
||||
observations.add(observation)
|
||||
return () => {
|
||||
observations.delete(observation)
|
||||
if (observations.size === 0) hub.byName.delete(serviceName)
|
||||
}
|
||||
ctx.on('internal/plugin', inspect, { global: true })
|
||||
ctx.on('internal/status', inspect, { global: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate every current and future implementation bound to one Cordis service.
|
||||
* Validate every current and future implementation bound to one Cordis
|
||||
* service through the root's indexed shared service listener.
|
||||
* @param ctx - invariant child context that owns the service observer.
|
||||
* @param fail - reporter bound to the package that owns the service seam.
|
||||
* @param serviceName - Cordis service name to observe.
|
||||
@@ -146,16 +275,14 @@ export function observeServiceInvariant(
|
||||
serviceName: string,
|
||||
validate: (value: unknown) => string | undefined,
|
||||
): void {
|
||||
const inspect = (value: unknown): void => {
|
||||
if (value === undefined) return
|
||||
const message = validate(value)
|
||||
if (message !== undefined) fail(message)
|
||||
}
|
||||
const observation: ServiceObservation = { fail, validate }
|
||||
const current: unknown = ctx.get(serviceName)
|
||||
inspect(current)
|
||||
ctx.on('internal/service', (name, value: unknown) => {
|
||||
if (name === serviceName) inspect(value)
|
||||
}, { global: true })
|
||||
inspectServiceObservation(observation, current)
|
||||
const hub = serviceObservationHub(ctx)
|
||||
ctx.effect(
|
||||
() => addServiceObservation(hub, serviceName, observation),
|
||||
`invariants.observeService(${JSON.stringify(serviceName)})`,
|
||||
)
|
||||
}
|
||||
|
||||
/** Structural runtime surface required from a Cordis service implementation. */
|
||||
@@ -297,7 +424,7 @@ export class InvariantService extends Service {
|
||||
* even when filtering disables its checks. Enabled installers run in a child
|
||||
* fiber; failure disposes that fiber and releases the reservation.
|
||||
* @param packageName - full npm package name that owns the contribution.
|
||||
* @param installer - synchronous listener installer for the child context.
|
||||
* @param installer - listener or startup-check installer for the child context.
|
||||
* @returns an effect-scoped disposer for the registration.
|
||||
*/
|
||||
register(packageName: string, installer: InvariantInstaller): () => void {
|
||||
@@ -324,11 +451,11 @@ export class InvariantService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
const installInvariant = (childCtx: Context) => {
|
||||
const installInvariant = (childCtx: Context) => (
|
||||
installer(childCtx, (message): never => {
|
||||
throw new InvariantError(packageName, message)
|
||||
})
|
||||
}
|
||||
)
|
||||
const child = ctx.plugin(installer.inject === undefined
|
||||
? installInvariant
|
||||
: Object.assign(installInvariant, { inject: installer.inject }))
|
||||
|
||||
@@ -273,6 +273,25 @@ describe('InvariantService lifecycle', () => {
|
||||
expect(retry).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('joins asynchronous checks and rolls back their effects on failure', async () => {
|
||||
const { ctx } = await setup()
|
||||
const leaked = vi.fn()
|
||||
const failed = runtimeRegistration(ctx.invariants.register('@deepseek-ai/dsh-async-probe', async (child, fail) => {
|
||||
child.on('invariants-test/ping', leaked, { global: true })
|
||||
await Promise.resolve()
|
||||
fail('asynchronous check failed')
|
||||
}))
|
||||
await expect(Promise.resolve(failed)).rejects.toThrow(/asynchronous check failed/)
|
||||
ctx.emit('invariants-test/ping')
|
||||
expect(leaked).not.toHaveBeenCalled()
|
||||
|
||||
const retry = runtimeRegistration(ctx.invariants.register('@deepseek-ai/dsh-async-probe', async () => {
|
||||
await Promise.resolve()
|
||||
}))
|
||||
await retry
|
||||
await retry()
|
||||
})
|
||||
|
||||
it('releases a synchronous reservation if the service fiber is already inactive', async () => {
|
||||
const { ctx, fiber } = await setup()
|
||||
const service = ctx.invariants
|
||||
@@ -282,11 +301,15 @@ describe('InvariantService lifecycle', () => {
|
||||
})
|
||||
|
||||
describe('package-owned invariant helpers', () => {
|
||||
interface InvariantDisposer {
|
||||
(): void | Promise<void>
|
||||
}
|
||||
|
||||
async function registerInstaller(
|
||||
ctx: Context,
|
||||
packageName: string,
|
||||
installer: InvariantInstaller,
|
||||
): Promise<() => void> {
|
||||
): Promise<InvariantDisposer> {
|
||||
const registration = runtimeRegistration(ctx.invariants.register(packageName, installer))
|
||||
const dispose = await Promise.resolve(registration)
|
||||
return dispose
|
||||
@@ -376,6 +399,38 @@ describe('package-owned invariant helpers', () => {
|
||||
await ctx.plugin(plugin)
|
||||
})
|
||||
|
||||
it('multiplexes same-runtime plugin checks through one root listener pair and disposes each owner', async () => {
|
||||
const { ctx } = await setup()
|
||||
const firstValidation = vi.fn(() => undefined)
|
||||
const secondValidation = vi.fn(() => undefined)
|
||||
const first = await registerInstaller(ctx, '@deepseek-ai/dsh-shared-plugin-first', (child, fail) => {
|
||||
observePluginInvariant(child, fail, { name: 'shared-plugin-probe', validate: firstValidation })
|
||||
})
|
||||
const second = await registerInstaller(ctx, '@deepseek-ai/dsh-shared-plugin-second', (child, fail) => {
|
||||
observePluginInvariant(child, fail, { name: 'shared-plugin-probe', validate: secondValidation })
|
||||
})
|
||||
const rootEffectLabels = ctx.fiber.getEffects().map(effect => effect.label)
|
||||
expect(rootEffectLabels.filter(label => label === 'ctx.on("internal/plugin")')).toHaveLength(1)
|
||||
expect(rootEffectLabels.filter(label => label === 'ctx.on("internal/status")')).toHaveLength(1)
|
||||
|
||||
const plugin = effectPlugin({ name: 'shared-plugin-probe' })
|
||||
const firstFiber = await ctx.plugin(plugin)
|
||||
expect(firstValidation).toHaveBeenCalledOnce()
|
||||
expect(secondValidation).toHaveBeenCalledOnce()
|
||||
|
||||
await first()
|
||||
await firstFiber.dispose()
|
||||
const secondFiber = await ctx.plugin(plugin)
|
||||
expect(firstValidation).toHaveBeenCalledOnce()
|
||||
expect(secondValidation).toHaveBeenCalledTimes(2)
|
||||
|
||||
await second()
|
||||
await secondFiber.dispose()
|
||||
await ctx.plugin(plugin)
|
||||
expect(firstValidation).toHaveBeenCalledOnce()
|
||||
expect(secondValidation).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('rejects a contract that does not identify a plugin', async () => {
|
||||
const { ctx } = await setup()
|
||||
const registration = runtimeRegistration(ctx.invariants.register('@deepseek-ai/dsh-invalid-plugin', (child, fail) => {
|
||||
@@ -449,12 +504,46 @@ describe('package-owned invariant helpers', () => {
|
||||
.rejects.toThrow(/wrong watched service/)
|
||||
})
|
||||
|
||||
it('multiplexes same-name service checks through one root listener and disposes each owner', async () => {
|
||||
const { ctx } = await setup()
|
||||
const firstValidation = vi.fn(() => undefined)
|
||||
const secondValidation = vi.fn(() => undefined)
|
||||
const first = await registerInstaller(ctx, '@deepseek-ai/dsh-shared-service-first', (child, fail) => {
|
||||
observeServiceInvariant(child, fail, 'watchedInvariantProbe', firstValidation)
|
||||
})
|
||||
const second = await registerInstaller(ctx, '@deepseek-ai/dsh-shared-service-second', (child, fail) => {
|
||||
observeServiceInvariant(child, fail, 'watchedInvariantProbe', secondValidation)
|
||||
})
|
||||
const rootEffectLabels = ctx.fiber.getEffects().map(effect => effect.label)
|
||||
expect(rootEffectLabels.filter(label => label === 'ctx.on("internal/service")')).toHaveLength(1)
|
||||
|
||||
const firstFiber = await ctx.plugin(WatchedInvariantProbeService)
|
||||
expect(firstValidation).toHaveBeenCalledOnce()
|
||||
expect(secondValidation).toHaveBeenCalledOnce()
|
||||
|
||||
await first()
|
||||
const firstCallsAfterDisposal = firstValidation.mock.calls.length
|
||||
const secondCallsBeforeRemount = secondValidation.mock.calls.length
|
||||
await firstFiber.dispose()
|
||||
const secondFiber = await ctx.plugin(WatchedInvariantProbeService)
|
||||
expect(firstValidation).toHaveBeenCalledTimes(firstCallsAfterDisposal)
|
||||
expect(secondValidation.mock.calls.length).toBeGreaterThan(secondCallsBeforeRemount)
|
||||
|
||||
await second()
|
||||
const firstCallsAfterBothDisposals = firstValidation.mock.calls.length
|
||||
const secondCallsAfterBothDisposals = secondValidation.mock.calls.length
|
||||
await secondFiber.dispose()
|
||||
await ctx.plugin(WatchedInvariantProbeService)
|
||||
expect(firstValidation).toHaveBeenCalledTimes(firstCallsAfterBothDisposals)
|
||||
expect(secondValidation).toHaveBeenCalledTimes(secondCallsAfterBothDisposals)
|
||||
})
|
||||
|
||||
it('reports synchronous package assertions through the bound failure reporter', async () => {
|
||||
const { ctx } = await setup()
|
||||
const valid = await registerInstaller(ctx, '@deepseek-ai/dsh-valid-assertion', (_child, fail) => {
|
||||
assertInvariant(fail, true, 'must stay true')
|
||||
})
|
||||
valid()
|
||||
await valid()
|
||||
|
||||
const invalid = runtimeRegistration(ctx.invariants.register('@deepseek-ai/dsh-invalid-assertion', (_child, fail) => {
|
||||
assertInvariant(fail, false, 'must stay true')
|
||||
|
||||
@@ -12,23 +12,20 @@ export const name = 'loader-smoke-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert default source mode and plain-Node built-artifact launch resolution. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { resolveExampleLaunch, resolveExampleMode } = await import('./index.ts')
|
||||
assertInvariant(fail, resolveExampleMode('') === 'src',
|
||||
'an empty example-mode selection must preserve source-mode development')
|
||||
const launch = resolveExampleLaunch({
|
||||
srcBin: '/workspace/probe/src/bin.ts',
|
||||
mode: 'lib',
|
||||
})
|
||||
assertInvariant(fail,
|
||||
launch.command === process.execPath
|
||||
&& launch.args.length === 1
|
||||
&& launch.args[0] === '/workspace/probe/lib/bin.js'
|
||||
&& launch.env.TSX_TSCONFIG_PATH === undefined,
|
||||
'built example launches must use plain Node, the derived lib entry, and no tsx paths map')
|
||||
return () => {}
|
||||
}, 'loader-smoke: validate source and built launch resolution')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const { resolveExampleLaunch, resolveExampleMode } = await import('./index.ts')
|
||||
assertInvariant(fail, resolveExampleMode('') === 'src',
|
||||
'an empty example-mode selection must preserve source-mode development')
|
||||
const launch = resolveExampleLaunch({
|
||||
srcBin: '/workspace/probe/src/bin.ts',
|
||||
mode: 'lib',
|
||||
})
|
||||
assertInvariant(fail,
|
||||
launch.command === process.execPath
|
||||
&& launch.args.length === 1
|
||||
&& launch.args[0] === '/workspace/probe/lib/bin.js'
|
||||
&& launch.env.TSX_TSCONFIG_PATH === undefined,
|
||||
'built example launches must use plain Node, the derived lib entry, and no tsx paths map')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,18 +13,15 @@ export const name = 'app-boot-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert ordinary and replay config-path selection. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { resolveConfigPath } = await import('./config-path.ts')
|
||||
const cwd = '/tmp/dsh-app-boot-invariant'
|
||||
const ordinary = resolveConfigPath('cordis.yml', undefined, cwd)
|
||||
const replay = resolveConfigPath('cordis.yml', 'replay', cwd)
|
||||
assertInvariant(fail, ordinary === resolve(cwd, 'cordis.yml'),
|
||||
'ordinary app boot must retain the requested config basename')
|
||||
assertInvariant(fail, replay === resolve(cwd, 'cordis.snapshot.yml'),
|
||||
'snapshot replay must select cordis.snapshot.yml in the requested config directory')
|
||||
return () => {}
|
||||
}, 'app-boot: validate ordinary and replay config selection')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const { resolveConfigPath } = await import('./config-path.ts')
|
||||
const cwd = '/tmp/dsh-app-boot-invariant'
|
||||
const ordinary = resolveConfigPath('cordis.yml', undefined, cwd)
|
||||
const replay = resolveConfigPath('cordis.yml', 'replay', cwd)
|
||||
assertInvariant(fail, ordinary === resolve(cwd, 'cordis.yml'),
|
||||
'ordinary app boot must retain the requested config basename')
|
||||
assertInvariant(fail, replay === resolve(cwd, 'cordis.snapshot.yml'),
|
||||
'snapshot replay must select cordis.snapshot.yml in the requested config directory')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,13 +12,10 @@ export const name = 'brand-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert that the nominal-type primitive remains erased at runtime. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const brandRuntime = await import('./index.ts')
|
||||
assertInvariant(fail, Object.keys(brandRuntime).length === 0,
|
||||
'the branded-id primitive must remain type-only with no runtime exports')
|
||||
return () => {}
|
||||
}, 'brand: validate type-only runtime erasure')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const brandRuntime = await import('./index.ts')
|
||||
assertInvariant(fail, Object.keys(brandRuntime).length === 0,
|
||||
'the branded-id primitive must remain type-only with no runtime exports')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,17 +13,14 @@ export const name = 'home-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert the canonical environment key and configured-path precedence. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { DSH_HOME_ENV, resolveDshHome } = await import('./index.ts')
|
||||
const environmentKey: string = DSH_HOME_ENV
|
||||
assertInvariant(fail, environmentKey === ['DSH', 'HOME'].join('_'),
|
||||
'the canonical Harness home environment key must remain DSH_HOME')
|
||||
const configured = 'relative-invariant-home'
|
||||
assertInvariant(fail, resolveDshHome(configured) === resolve(configured),
|
||||
'an explicitly configured Harness home must normalize to an absolute path')
|
||||
return () => {}
|
||||
}, 'home: validate canonical DSH home resolution')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const { DSH_HOME_ENV, resolveDshHome } = await import('./index.ts')
|
||||
const environmentKey: string = DSH_HOME_ENV
|
||||
assertInvariant(fail, environmentKey === ['DSH', 'HOME'].join('_'),
|
||||
'the canonical Harness home environment key must remain DSH_HOME')
|
||||
const configured = 'relative-invariant-home'
|
||||
assertInvariant(fail, resolveDshHome(configured) === resolve(configured),
|
||||
'an explicitly configured Harness home must normalize to an absolute path')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,17 +14,14 @@ export const name = 'paths-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert tilde expansion and explicit-over-environment home precedence. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { DSH_HOME_ENV, expandHomePath, resolveDshHome } = await import('./index.ts')
|
||||
assertInvariant(fail, expandHomePath('~/invariant-probe') === join(homedir(), 'invariant-probe'),
|
||||
'supported tilde prefixes must expand against the operating-system home')
|
||||
const configured = 'relative-invariant-home'
|
||||
const resolved = resolveDshHome(configured, { [DSH_HOME_ENV]: '/ignored-environment-home' })
|
||||
assertInvariant(fail, resolved === resolve(configured),
|
||||
'an explicit DSH home must override the environment and normalize to an absolute path')
|
||||
return () => {}
|
||||
}, 'paths: validate DSH home resolution')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const { DSH_HOME_ENV, expandHomePath, resolveDshHome } = await import('./index.ts')
|
||||
assertInvariant(fail, expandHomePath('~/invariant-probe') === join(homedir(), 'invariant-probe'),
|
||||
'supported tilde prefixes must expand against the operating-system home')
|
||||
const configured = 'relative-invariant-home'
|
||||
const resolved = resolveDshHome(configured, { [DSH_HOME_ENV]: '/ignored-environment-home' })
|
||||
assertInvariant(fail, resolved === resolve(configured),
|
||||
'an explicit DSH home must override the environment and normalize to an absolute path')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,24 +12,21 @@ export const name = 'retention-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert exact head-retention accounting after the budget is exceeded. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { ItemRetainer } = await import('./index.ts')
|
||||
const retainer = new ItemRetainer<string>({ kind: 'head', maxItems: 2 })
|
||||
retainer.push('first')
|
||||
retainer.push('second')
|
||||
retainer.push('third')
|
||||
const result = retainer.finish()
|
||||
assertInvariant(fail,
|
||||
result.items.join(',') === 'first,second'
|
||||
&& result.seen === 3
|
||||
&& result.kept === 2
|
||||
&& result.truncated
|
||||
&& result.omitted.kind === 'exact'
|
||||
&& result.omitted.count === 1,
|
||||
'head retention must keep the prefix and report exact seen, kept, and omitted counts')
|
||||
return () => {}
|
||||
}, 'retention: validate exact head accounting')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const { ItemRetainer } = await import('./index.ts')
|
||||
const retainer = new ItemRetainer<string>({ kind: 'head', maxItems: 2 })
|
||||
retainer.push('first')
|
||||
retainer.push('second')
|
||||
retainer.push('third')
|
||||
const result = retainer.finish()
|
||||
assertInvariant(fail,
|
||||
result.items.join(',') === 'first,second'
|
||||
&& result.seen === 3
|
||||
&& result.kept === 2
|
||||
&& result.truncated
|
||||
&& result.omitted.kind === 'exact'
|
||||
&& result.omitted.count === 1,
|
||||
'head retention must keep the prefix and report exact seen, kept, and omitted counts')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,19 +12,16 @@ export const name = 'timeout-invariant'
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** Assert default-before-cap arithmetic and capability-code classification. */
|
||||
const install: InvariantInstaller = (ctx, fail) => {
|
||||
ctx.effect(async () => {
|
||||
const { clampTimeout, TimeoutReason, timeoutOf } = await import('./index.ts')
|
||||
assertInvariant(fail,
|
||||
clampTimeout(undefined, 50, 30) === 30 && clampTimeout(20, 50, 30) === 20,
|
||||
'timeout resolution must apply the default before capping and preserve smaller requests')
|
||||
const reason = new TimeoutReason('INVARIANT_TIMEOUT', 25)
|
||||
assertInvariant(fail, timeoutOf({ reason }, 'INVARIANT_TIMEOUT') === reason,
|
||||
'timeout classification must recover a matching capability-owned reason')
|
||||
assertInvariant(fail, timeoutOf({ reason }, 'FOREIGN_TIMEOUT') === undefined,
|
||||
'timeout classification must reject a reason owned by another capability')
|
||||
return () => {}
|
||||
}, 'timeout: validate resolution and reason classification')
|
||||
const install: InvariantInstaller = async (_ctx, fail) => {
|
||||
const { clampTimeout, TimeoutReason, timeoutOf } = await import('./index.ts')
|
||||
assertInvariant(fail,
|
||||
clampTimeout(undefined, 50, 30) === 30 && clampTimeout(20, 50, 30) === 20,
|
||||
'timeout resolution must apply the default before capping and preserve smaller requests')
|
||||
const reason = new TimeoutReason('INVARIANT_TIMEOUT', 25)
|
||||
assertInvariant(fail, timeoutOf({ reason }, 'INVARIANT_TIMEOUT') === reason,
|
||||
'timeout classification must recover a matching capability-owned reason')
|
||||
assertInvariant(fail, timeoutOf({ reason }, 'FOREIGN_TIMEOUT') === undefined,
|
||||
'timeout classification must reject a reason owned by another capability')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user