refactor: apply repository naming contract

Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
This commit is contained in:
Tianyi Cui
2026-08-13 00:36:22 +08:00
parent 101df7cf58
commit a2d0f7f411
3281 changed files with 21730 additions and 21592 deletions

View File

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/runtime-diagnostics/invariants/README.md
README.md: 1435a7ad9a4e994ad2c89b5a94e18e86889a831f
README.zh.md: 09ee5006c7d9b43c97468778632239780c49949b

View File

@@ -0,0 +1,85 @@
# dsh-invariants
English | [中文](README.zh.md)
Configurable registry service for package-owned runtime invariant checks. The root plugin registers `ctx.invariants`; it contains no product checks or product-package imports. Every workspace package publishes a `./invariant` companion that registers its exact npm package name.
## Service: `InvariantRegistry` (`ctx.invariants`)
```ts
interface Config {
enabled?: boolean
package_allowlist?: string[]
package_blocklist?: string[]
}
```
Defaults are `enabled: true`, `package_allowlist: []`, and `package_blocklist: []`. A package is selected only when the service is enabled, the allowlist is empty or at least one allowlist pattern matches its full npm name, and no blocklist pattern matches. Blocklist matches therefore override allowlist matches.
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 services 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 listeners, trace state, and the reservation. A companion can therefore reload and register the same package name without retaining its previous state. Session-backed companions rebuild their baseline from durable events; live-only companions observe operations that begin after reload.
`InvariantError` extends `Error`, carries stable `code: 'INVARIANT'`, and exposes the owning `packageName` without adding a product dependency to the service.
Session itself owns immutable, surface-valid log storage in every composition: it takes one lossless JSON snapshot of each candidate, validates complete cited source-event coverage and positional replacement, restricts `tool/result` replacement to one current result's `content`, deep-freezes the accepted record, and exposes the log through immutable array snapshots. The `dsh-session` invariant companion checks the remaining cross-record rules that Session does not own.
## Package companions
Publication and registration are exhaustive; runtime assertions are deliberately not synthetic. A companion installs a check only when its package owns an observable event relationship or relevant mutable-data relationship. Confirming a required method, plugin name, injection, effect, or fixed pure-function result is a type, load, or unit-test concern rather than a runtime invariant.
When no plausible runtime relationship exists, the companion uses an empty installer with a package-specific leading `No runtime invariant:` comment explaining why. This is common for pure utilities, thin implementations whose behavior is already observed through their interface package, composition-only packages, binaries, persistence adapters whose contracts require crash and round-trip tests, and test-support packages. The explanation must be revisited when the owner gains mutable state or an event protocol.
The current executable companions protect these relationships:
| Companion | Checks |
|---|---|
| `dsh-session`, `dsh-agent`, `dsh-scope`, `dsh-agent-loop` | Session enclosure and call/result trace, agent-status transitions, inbox FIFO conservation, scoped subjects, and model-request reconstruction. |
| `dsh-llm`, `dsh-llm-retry`, `dsh-tools`, `dsh-system-prompt` | Stream grammar, durable retry position and bounds, tool-pipeline stages and frozen results, and authoritative prompt-assembly data. |
| `dsh-compaction`, `dsh-hook-protocol`, `dsh-sandbox-policy` | Durable compaction and hook pairing, compaction metadata, and sandbox-mode vocabulary. |
| `dsh-fs`, `dsh-subagent`, `dsh-workflow` | Filesystem event identity, provider/child pairing, and workflow/agent lifecycle identity. |
| `dsh-goal`, `dsh-goal-round-driver` | Durable goal source/content agreement, revision and lifecycle transitions, timestamps, sequential admitted rounds, and reconstructed continuation prompts. |
| `dsh-permission-presets`, `dsh-user-approval` | Active-preset references and approval asked/decided audit pairing. |
| `dsh-jobs`, `dsh-tool-todo` | Task snapshot lifecycle/ownership fields and durable whole-list todo structure. |
| `dsh-time-context` | Durable clock readings agree with the session's open turn and next pre-step position and elapsed baseline; rendered time parses and does not postdate its event. |
The root entrypoint of each owner remains independent of diagnostics. Loading the service alone installs no product checks, and loading a companion without the service waits on its declared `invariants` injection.
`pnpm run verify-package-invariants` discovers all workspace packages. It rejects generated markers, unexplained empty installers, non-empty installers that omit or ignore the reporter, incorrect registration names, and incomplete export, publication, dependency, TypeScript-reference, or bundle wiring. This source rule is a minimum ownership check; focused tests prove each executable companion's semantics.
## Composition
```ts
import type { Context } from '@deepseek-ai/cordis'
import InvariantRegistry from '@deepseek-ai/dsh-invariants'
import * as SessionInvariant from '@deepseek-ai/dsh-session/invariant'
declare const ctx: Context
ctx.plugin(InvariantRegistry, {
enabled: true,
package_allowlist: ['^@deepseek-ai/dsh-'],
package_blocklist: ['^@deepseek-ai/dsh-agent-loop$'],
})
ctx.plugin(SessionInvariant)
```
The standard agent composition mounts the service and its four core stateful companions. Custom compositions explicitly add companions for other loaded packages whose contracts they want checked; filters can disable or select registrations without changing package entrypoints.
Every ordinary Vitest topology mounts an explicitly enabled service and the current test package's companion. Focused suites cover valid and invalid observations for executable companions, while one exhaustive topology mounts all companions to prove registration and disposal wiring.
## Model Experience
None, as the service and companions observe runtime events and mutable snapshots without altering prompts, messages, schemas, streams, or tool results.
#### KV Cache effect
None; invariant checks do not assemble or send provider requests.
## Known Limitations and Deferred Work
- Request reconstruction covers requests explicitly marked by the loop before freezing; direct one-shot LLM calls remain outside that marker contract even when callers freeze them or attach a session id.
- Live-only lifecycle companions cannot reconstruct operations that began before their own reload. Standard and test compositions mount them before the corresponding operations begin.
- Regular-expression filters are fixed for the service lifetime; changing them requires ordinary Cordis plugin reload.

View File

@@ -0,0 +1,85 @@
# dsh-invariants
[English](README.md) | 中文
用于包自有运行时不变量检查的可配置注册表服务。根插件注册 `ctx.invariants`;它不包含产品检查或产品包导入。每个工作区包都发布一个 `./invariant` 配套入口,用于注册其精确 npm 包名。
## 服务:`InvariantRegistry``ctx.invariants`
```ts
interface Config {
enabled?: boolean
package_allowlist?: string[]
package_blocklist?: string[]
}
```
默认值为 `enabled: true``package_allowlist: []``package_blocklist: []`。只有在服务启用、allowlist 为空或至少一个 allowlist pattern 匹配完整 npm 名称,且没有 blocklist pattern 匹配时包才被选中。因此blocklist 匹配优先于 allowlist 匹配。
每个条目都是区分大小写的 JavaScript 正则表达式源,使用 `new RegExp(pattern)` 编译。除非源提供 `^``$`,否则匹配不锚定;不解析 `/pattern/flags` 语法。同一列表中的空白、带前后空白、无效或重复条目会使服务启动失败。有效 pattern 可以不匹配任何当前已加载包,以使后续加载和 HMR热模块替换保持确定性。
`ctx.invariants.register(packageName, installer)` 为完整 npm 包名保留一个活动注册,即使过滤器使其 installer 保持非活动,并返回 disposer。已启用贡献在专用子 Cordis fiber 中运行。installer 可以通过 `installer.inject` 声明所需服务接口,并收到 `fail(message)`;后者抛出绑定到注册包的 `InvariantError`。在注册成功前,系统会等待同步或异步 installer 完成;失败会原子地 dispose资源释放子级并释放归属。
服务拥有每个注册 fiber返回的 disposer 同时属于配套 fiber。卸载任一侧都会移除监听器、跟踪状态和保留。因此配套入口可以重新加载并注册同一包名而不保留旧状态。由会话支撑的配套入口从持久事件重建 baseline仅实时配套入口观察重新加载后开始的操作。
`InvariantError` 扩展 `Error`,携带稳定 `code: 'INVARIANT'`,并公开所属 `packageName`,而不向服务添加产品依赖。
在每个组合中Session 自身负责不可变且在对外接口层面有效的日志存储:它对每个候选项制作一份无损 JSON 快照,验证引用的源事件是否齐全以及位置替换是否合法,将 `tool/result` 替换限制为一个当前结果的 `content`,深度冻结已接受记录,并通过不可变数组快照公开日志。`dsh-session` 不变量配套入口检查 Session 不负责的其余跨记录规则。
## 包配套入口
发布和注册覆盖全部包但不会为了覆盖全部包而人为编造运行时断言。只有当包拥有可观察事件关系或相关可变数据关系时配套入口才安装检查。确认必需方法、插件名称、注入、effect 或固定纯函数结果属于类型、加载或单元测试关注点,而非运行时不变量。
如果不存在合理的运行时关系,配套入口使用空 installer并以包专用的前置 `No runtime invariant:` 注释说明原因。纯工具、行为已通过其接口包观察的薄实现、仅组合包、二进制程序、需要通过崩溃测试和往返测试验证其约定的持久化适配器和测试支持包通常属于此类。当 owner 获得可变状态或事件协议时,必须重新审视该说明。
当前可执行配套入口保护以下关系:
| 配套入口 | 检查 |
|---|---|
| `dsh-session``dsh-agent``dsh-scope``dsh-agent-loop` | 会话包含关系和调用/结果跟踪、agent智能体状态转换、inbox FIFO 守恒、作用域 subject 和模型请求重建。 |
| `dsh-llm``dsh-llm-retry``dsh-tools``dsh-system-prompt` | 流语法、持久重试位置和边界、工具流水线阶段与冻结结果,以及权威提示词组装数据。 |
| `dsh-compaction``dsh-hook-protocol``dsh-sandbox-policy` | 持久压缩compaction与钩子配对、压缩元数据和沙箱 mode 词汇。 |
| `dsh-fs``dsh-subagent``dsh-workflow` | 文件系统事件身份、提供方/子级配对和工作流/agent 生命周期身份。 |
| `dsh-goal``dsh-goal-round-driver` | 持久 goal 来源/内容一致性、修订和生命周期转换、时间戳、依次获准的 Round 和重建的继续提示词。 |
| `dsh-permission-presets``dsh-user-approval` | 活动 preset 引用和审批询问/决定审计配对。 |
| `dsh-jobs``dsh-tool-todo` | 任务快照生命周期/归属字段和持久整表 todo 结构。 |
| `dsh-time-context` | 持久化时钟读数与会话中正在进行的轮次、下一步骤开始前的位置及已用时间 baseline 一致;渲染时间可解析,且不晚于其事件。 |
每个 owner 的根入口仍独立于诊断。单独加载服务不会安装产品检查;在没有服务时加载配套入口,会等待其声明的 `invariants` 注入。
`pnpm run verify-package-invariants` 发现全部工作区包。它拒绝生成标记、未说明的空 installer、省略或忽略 reporter 的非空 installer、错误注册名称以及不完整的导出、发布、依赖、TypeScript 引用或 bundle 接线。该源码规则是最低归属检查;聚焦测试证明每个可执行配套入口的语义。
## 组合
```ts
import type { Context } from '@deepseek-ai/cordis'
import InvariantRegistry from '@deepseek-ai/dsh-invariants'
import * as SessionInvariant from '@deepseek-ai/dsh-session/invariant'
declare const ctx: Context
ctx.plugin(InvariantRegistry, {
enabled: true,
package_allowlist: ['^@deepseek-ai/dsh-'],
package_blocklist: ['^@deepseek-ai/dsh-agent-loop$'],
})
ctx.plugin(SessionInvariant)
```
标准 agent 组合挂载服务和 4 个核心有状态配套入口。自定义组合为希望检查其约定的其他已加载包显式添加配套入口;过滤器可以在不改变包入口的情况下禁用或选择注册。
每个普通 Vitest 拓扑都挂载显式启用的服务和当前测试包的配套入口。聚焦套件覆盖可执行配套入口的合法与违规观测,一个穷尽拓扑则挂载全部配套入口,以证明注册和 dispose 接线。
## 模型体验
无。服务和配套入口观察运行时事件和可变快照不会更改提示词、消息、schema、流或工具结果。
#### KV Cache 影响
无;不变量检查不组装或发送提供方请求。
## 已知限制与暂缓事项
- 请求重建覆盖 loop 在冻结前显式标记的请求;直接一次性 LLM大语言模型调用即使由调用方冻结或附加会话 id仍不在该标记约定内。
- 仅实时生命周期配套入口无法重建自身重新加载前开始的操作。标准组合和测试组合会在相应操作开始前挂载它们。
- 正则表达式过滤器在服务生命周期内固定;更改它们需要执行普通 Cordis 插件重新加载。

View File

@@ -0,0 +1,43 @@
{
"name": "@deepseek-ai/dsh-invariants",
"description": "Registry service for package-owned DeepSeek Harness runtime invariants",
"version": "0.0.1-rc.2",
"publishConfig": {
"access": "restricted"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
"directory": "packages/runtime-diagnostics/invariants"
},
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
"exports": {
".": {
"types": "./lib/types/index.d.ts",
"default": "./lib/index.js"
},
"./invariant": {
"types": "./lib/types/invariant.d.ts",
"default": "./lib/invariant.js"
},
"./src/*": "./src/*",
"./package.json": "./package.json"
},
"files": [
"lib/index.js",
"lib/invariant.js",
"lib/types/**/*.d.ts"
],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^"
},
"dependencies": {
"@deepseek-ai/schemastery": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^"
}
}

View File

@@ -0,0 +1,200 @@
/**
* Configurable registry for package-owned runtime invariant contributions.
* Every workspace package registers checks from a `./invariant` companion;
* ordinary package entrypoints stay independent of diagnostics.
*
* @module @deepseek-ai/dsh-invariants
*/
import { Context, Service } from '@deepseek-ai/cordis'
import type { Inject } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery'
import type Schema from '@deepseek-ai/schemastery'
/** Runtime invariant selection configured on the service plugin. */
export interface Config {
/** Global switch; defaults to `true`. */
readonly enabled?: boolean
/** Case-sensitive JavaScript regex sources that admit package names; empty admits all. */
readonly package_allowlist?: string[]
/** Case-sensitive JavaScript regex sources that exclude package names after allowlist matching. */
readonly package_blocklist?: string[]
}
/**
* Throw a package-attributed invariant failure.
* @param message - violated package contract without the standard prefix.
* @returns never because reporting a violation throws.
*/
export type InvariantFailure = (message: string) => never
/** 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, or a promise settling after asynchronous checks finish.
*/
(ctx: Context, fail: InvariantFailure): void | Promise<void>
/** Services the child installer fiber may access. */
readonly inject?: Inject
}
/** Internal effect shape used to join child startup before a companion loads. */
interface PendingInvariantRegistration extends PromiseLike<() => void> {
(): void | Promise<void>
}
/** Thrown when a package-owned runtime invariant is violated. */
export class InvariantError extends Error {
/** Stable machine-readable invariant failure code. */
readonly code = 'INVARIANT' as const
/** Full npm package name that owns the violated invariant. */
readonly packageName: string
/**
* Construct a package-attributed invariant failure.
* @param packageName - full npm package name that registered the check.
* @param message - violated contract, without the standard error prefix.
*/
constructor(packageName: string, message: string) {
super(`invariant violated by "${packageName}": ${message}`)
this.name = 'InvariantError'
this.packageName = packageName
}
}
declare module '@deepseek-ai/cordis' {
interface Context {
invariants: InvariantRegistry
}
}
/** Compile and validate one package-filter list. */
function compilePatterns(field: 'package_allowlist' | 'package_blocklist', values: readonly string[]): RegExp[] {
const seen = new Set<string>()
return values.map((value) => {
if (value.length === 0 || value.trim() !== value) {
throw new Error(`invariants: ${field} entries must be non-blank and have no surrounding whitespace`)
}
if (seen.has(value)) {
throw new Error(`invariants: ${field} contains duplicate regex ${JSON.stringify(value)}`)
}
seen.add(value)
try {
return new RegExp(value)
} catch (cause) {
throw new Error(`invariants: ${field} contains invalid regex ${JSON.stringify(value)}`, { cause })
}
})
}
/** Package-owned invariant registry with global and regex-based selection. */
export class InvariantRegistry extends Service {
static Config: Schema<Config> = z.object({
enabled: z.boolean().default(true),
package_allowlist: z.array(z.string()).default([]),
package_blocklist: z.array(z.string()).default([]),
})
private readonly enabled: boolean
private readonly ownerCtx: Context
private readonly packageAllowlist: readonly RegExp[]
private readonly packageBlocklist: readonly RegExp[]
private readonly registrations = new Set<string>()
/**
* Create and install the invariant registry.
* @param ctx - Cordis context that owns the service.
* @param config - global enablement and package-name regex filters.
*/
constructor(ctx: Context, config: Config = {}) {
super(ctx, 'invariants')
this.ownerCtx = ctx
this.enabled = config.enabled ?? true
this.packageAllowlist = compilePatterns('package_allowlist', config.package_allowlist ?? [])
this.packageBlocklist = compilePatterns('package_blocklist', config.package_blocklist ?? [])
}
/** Return whether one full package name passes the configured filters. */
private selected(packageName: string): boolean {
if (!this.enabled) return false
if (this.packageAllowlist.length > 0
&& !this.packageAllowlist.some(pattern => pattern.test(packageName))) return false
return !this.packageBlocklist.some(pattern => pattern.test(packageName))
}
/**
* Register one package's invariant installer. The package name is reserved
* 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 - listener or startup-check installer for the child context.
* @returns an effect-scoped disposer for the registration.
*/
register(packageName: string, installer: InvariantInstaller): () => void {
if (packageName.length === 0 || packageName.trim() !== packageName || /\s/.test(packageName)) {
throw new Error('invariants: packageName must be non-blank and contain no whitespace')
}
if (this.registrations.has(packageName)) {
throw new Error(`invariants: package "${packageName}" is already registered`)
}
// Service method tracing binds `this.ctx` to the caller. This explicit
// origin keeps registrations and their child fibers owned by the service;
// companion disposal is covered independently by the returned disposer.
const ctx = this.ownerCtx
const registrations = this.registrations
registrations.add(packageName)
let registration: PendingInvariantRegistration
try {
registration = ctx.effect(async () => {
if (!this.selected(packageName)) {
return () => {
registrations.delete(packageName)
}
}
const installInvariant = (childCtx: Context) => (
installer(childCtx, (message): never => {
throw new InvariantError(packageName, message)
})
)
try {
const child = ctx.plugin(installer.inject === undefined
? installInvariant
: Object.assign(installInvariant, { inject: installer.inject }))
try {
await child
} catch (error) {
await child.dispose()
throw error
}
return async () => {
try {
await child.dispose()
} finally {
registrations.delete(packageName)
}
}
} catch (error) {
registrations.delete(packageName)
throw error
}
}, `invariants.register(${JSON.stringify(packageName)})`)
} catch (error) {
registrations.delete(packageName)
throw error
}
// Cordis attaches setup thenability and async teardown to this callable;
// the service contract intentionally exposes only the conventional disposer.
// oxlint-disable-next-line typescript/no-misused-promises -- the extra runtime shape stays private.
return registration
}
}
export default InvariantRegistry

View File

@@ -0,0 +1,30 @@
/**
* Package-owned invariant companion for `@deepseek-ai/dsh-invariants`.
* @module @deepseek-ai/dsh-invariants/invariant
*/
/* jscpd:ignore-start */
import type { Context } from '@deepseek-ai/cordis'
import type { InvariantInstaller } from './index.ts'
const PACKAGE_NAME = '@deepseek-ai/dsh-invariants'
/** Cordis companion plugin name. */
export const name = 'invariants-invariant'
/** Service required before the companion can reserve package ownership. */
export const inject = ['invariants']
/**
* No runtime invariant: registration ownership and child lifecycle are the service's mutation
* boundary itself; observing them from the same registry would only duplicate its implementation.
*/
const install: InvariantInstaller = () => {}
/**
* Register this package's invariant companion.
* @param ctx - Cordis context carrying the invariant service.
* @returns the installed registration's disposer after setup succeeds.
*/
export const apply = (ctx: Context): Promise<() => void> =>
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
/* jscpd:ignore-end */

View File

@@ -0,0 +1,310 @@
import { describe, expect, it, vi } from 'vitest'
import { Context, Service } from '@deepseek-ai/cordis'
import InvariantRegistry, {
InvariantError,
type Config,
} from '@deepseek-ai/dsh-invariants'
declare module '@deepseek-ai/cordis' {
interface Context {
invariantProbe: InvariantProbeService
}
interface Events {
'invariants-test/ping'(): void
}
}
class InvariantProbeService extends Service {
constructor(ctx: Context) {
super(ctx, 'invariantProbe')
}
}
interface RuntimeRegistration extends PromiseLike<() => void> {
(): void | Promise<void>
}
interface InstalledRegistration {
dispose(): Promise<void>
}
function runtimeRegistration(registration: () => void): RuntimeRegistration {
return registration as RuntimeRegistration
}
async function setup(config: Config = {}): Promise<{ ctx: Context; fiber: Awaited<ReturnType<Context['plugin']>> }> {
const ctx = new Context()
const fiber = await ctx.plugin(InvariantRegistry, config)
return { ctx, fiber }
}
async function registerProbe(
ctx: Context,
packageName: string,
probe: () => void,
): Promise<InstalledRegistration> {
const registration = runtimeRegistration(ctx.invariants.register(packageName, (child) => {
child.on('invariants-test/ping', probe, { global: true })
}))
await registration
return {
async dispose() { await registration() },
}
}
describe('InvariantRegistry selection', () => {
it('applies defaults when constructed directly without schema normalization', async () => {
const ctx = new Context()
const service = new InvariantRegistry(ctx)
const probe = vi.fn()
const registration = runtimeRegistration(service.register('@deepseek-ai/dsh-session', (child) => {
child.on('invariants-test/ping', probe, { global: true })
}))
await registration
ctx.emit('invariants-test/ping')
expect(probe).toHaveBeenCalledOnce()
await registration()
})
it('enables registrations by default and treats empty lists as admit-all and exclude-none', async () => {
for (const config of [{}, { package_allowlist: [], package_blocklist: [] }]) {
const { ctx } = await setup(config)
const probe = vi.fn()
await registerProbe(ctx, '@deepseek-ai/dsh-session', probe)
ctx.emit('invariants-test/ping')
expect(probe).toHaveBeenCalledOnce()
}
})
it('disables every installer while still reserving package ownership', async () => {
const { ctx } = await setup({ enabled: false })
const probe = vi.fn()
const registration = await registerProbe(ctx, '@deepseek-ai/dsh-session', probe)
expect(() => ctx.invariants.register('@deepseek-ai/dsh-session', () => {}))
.toThrow(/already registered/)
ctx.emit('invariants-test/ping')
expect(probe).not.toHaveBeenCalled()
await registration.dispose()
})
it('uses unanchored, case-sensitive JavaScript regex sources', async () => {
const unanchored = await setup({ package_allowlist: ['session'] })
const unanchoredProbe = vi.fn()
await registerProbe(unanchored.ctx, '@deepseek-ai/dsh-session-extra', unanchoredProbe)
unanchored.ctx.emit('invariants-test/ping')
expect(unanchoredProbe).toHaveBeenCalledOnce()
const anchored = await setup({ package_allowlist: ['^@deepseek-ai/dsh-session$'] })
const anchoredProbe = vi.fn()
await registerProbe(anchored.ctx, '@deepseek-ai/dsh-session-extra', anchoredProbe)
anchored.ctx.emit('invariants-test/ping')
expect(anchoredProbe).not.toHaveBeenCalled()
const caseSensitive = await setup({ package_allowlist: ['Session'] })
const caseProbe = vi.fn()
await registerProbe(caseSensitive.ctx, '@deepseek-ai/dsh-session', caseProbe)
caseSensitive.ctx.emit('invariants-test/ping')
expect(caseProbe).not.toHaveBeenCalled()
})
it('lets the blocklist override an allowlist match', async () => {
const { ctx } = await setup({
package_allowlist: ['^@deepseek-ai/dsh-'],
package_blocklist: ['session'],
})
const sessionProbe = vi.fn()
const agentProbe = vi.fn()
await registerProbe(ctx, '@deepseek-ai/dsh-session', sessionProbe)
await registerProbe(ctx, '@deepseek-ai/dsh-agent', agentProbe)
ctx.emit('invariants-test/ping')
expect(sessionProbe).not.toHaveBeenCalled()
expect(agentProbe).toHaveBeenCalledOnce()
})
it('accepts zero-match patterns for packages registered later', async () => {
const { ctx } = await setup({ package_allowlist: ['^@later/invariants$'] })
const now = vi.fn()
const later = vi.fn()
await registerProbe(ctx, '@deepseek-ai/dsh-session', now)
await registerProbe(ctx, '@later/invariants', later)
ctx.emit('invariants-test/ping')
expect(now).not.toHaveBeenCalled()
expect(later).toHaveBeenCalledOnce()
})
it('allows the same source in both lists and applies blocklist precedence', async () => {
const { ctx } = await setup({ package_allowlist: ['agent'], package_blocklist: ['agent'] })
const probe = vi.fn()
await registerProbe(ctx, '@deepseek-ai/dsh-agent', probe)
ctx.emit('invariants-test/ping')
expect(probe).not.toHaveBeenCalled()
})
})
describe('InvariantRegistry validation', () => {
it.each([
[{ package_allowlist: [''] }, /non-blank/],
[{ package_allowlist: [' '] }, /non-blank/],
[{ package_allowlist: [' session'] }, /surrounding whitespace/],
[{ package_blocklist: ['session '] }, /surrounding whitespace/],
[{ package_allowlist: ['session', 'session'] }, /duplicate regex/],
[{ package_blocklist: ['agent', 'agent'] }, /duplicate regex/],
[{ package_allowlist: ['['] }, /invalid regex/],
[{ package_blocklist: ['('] }, /invalid regex/],
])('rejects malformed filter config %#', async (config, message) => {
await expect((async () => {
const ctx = new Context()
await ctx.plugin(InvariantRegistry, config)
})()).rejects.toThrow(message)
})
it.each(['', ' ', ' package', 'pack age', 'package\n'])('rejects malformed package name %j', async (packageName) => {
const { ctx } = await setup()
expect(() => ctx.invariants.register(packageName, () => {})).toThrow(/packageName/)
})
})
describe('InvariantRegistry lifecycle', () => {
it('honors the installer dependency API in its child fiber', async () => {
const { ctx } = await setup()
await ctx.plugin(InvariantProbeService)
let registration!: RuntimeRegistration
await ctx.plugin({
inject: ['invariants', 'invariantProbe'],
apply(child: Context) {
const installer = Object.assign((installerCtx: Context) => {
expect(Object.keys(installerCtx.fiber.inject)).toContain('invariantProbe')
expect(Object.keys(installerCtx.fiber.store ?? {})).toContain('invariantProbe')
expect(installerCtx.invariantProbe).toBeInstanceOf(InvariantProbeService)
}, { inject: ['invariantProbe'] })
expect(installer.inject).toEqual(['invariantProbe'])
registration = runtimeRegistration(child.invariants.register('@deepseek-ai/dsh-probe', installer))
return Promise.resolve(registration)
},
})
await registration
})
it('attributes failures to the registering package with the stable code', async () => {
const { ctx } = await setup()
const registration = runtimeRegistration(ctx.invariants.register('@deepseek-ai/dsh-session', (child, fail) => {
child.on('invariants-test/ping', () => fail('seq must strictly increase'), { global: true })
}))
await registration
let caught: unknown
try {
ctx.emit('invariants-test/ping')
} catch (error) {
caught = error
}
expect(caught).toBeInstanceOf(InvariantError)
expect(caught).toMatchObject({
name: 'InvariantError',
code: 'INVARIANT',
packageName: '@deepseek-ai/dsh-session',
message: 'invariant violated by "@deepseek-ai/dsh-session": seq must strictly increase',
})
})
it('disposes the child fiber completely and permits HMR re-registration', async () => {
const { ctx } = await setup()
const first = vi.fn()
const firstRegistration = await registerProbe(ctx, '@deepseek-ai/dsh-session', first)
ctx.emit('invariants-test/ping')
await firstRegistration.dispose()
ctx.emit('invariants-test/ping')
expect(first).toHaveBeenCalledOnce()
const second = vi.fn()
await registerProbe(ctx, '@deepseek-ai/dsh-session', second)
ctx.emit('invariants-test/ping')
expect(first).toHaveBeenCalledOnce()
expect(second).toHaveBeenCalledOnce()
})
it('reserves ownership until asynchronous child disposal completes', async () => {
const { ctx } = await setup()
let finishDisposal!: () => void
const disposalBarrier = new Promise<void>((resolve) => { finishDisposal = resolve })
const registration = runtimeRegistration(ctx.invariants.register('@deepseek-ai/dsh-session', (child) => {
child.effect(() => async () => { await disposalBarrier })
}))
await registration
const disposing = registration()
expect(() => ctx.invariants.register('@deepseek-ai/dsh-session', () => {}))
.toThrow(/already registered/)
finishDisposal()
await disposing
const replacement = runtimeRegistration(ctx.invariants.register('@deepseek-ai/dsh-session', () => {}))
await replacement
await replacement()
})
it('rolls back listeners and ownership atomically when an installer fails', async () => {
const { ctx } = await setup()
const leaked = vi.fn()
const failed = runtimeRegistration(ctx.invariants.register('@deepseek-ai/dsh-session', (child) => {
child.on('invariants-test/ping', leaked, { global: true })
throw new Error('installer failed')
}))
await expect(Promise.resolve(failed)).rejects.toThrow('installer failed')
ctx.emit('invariants-test/ping')
expect(leaked).not.toHaveBeenCalled()
const retry = vi.fn()
await registerProbe(ctx, '@deepseek-ai/dsh-session', retry)
ctx.emit('invariants-test/ping')
expect(retry).toHaveBeenCalledOnce()
})
it('rolls back publication effects and ownership when child-fiber publication fails', async () => {
const { ctx } = await setup()
const leaked = vi.fn()
let rejectPublication = true
const stopRejecting = ctx.on('internal/plugin', (fiber) => {
if (!rejectPublication || fiber.uid === null) return
rejectPublication = false
fiber.ctx.on('invariants-test/ping', leaked, { global: true })
throw new Error('publication failed')
})
const failed = runtimeRegistration(ctx.invariants.register('@deepseek-ai/dsh-publication-probe', () => {}))
await expect(Promise.resolve(failed)).rejects.toThrow('publication failed')
ctx.emit('invariants-test/ping')
expect(leaked).not.toHaveBeenCalled()
stopRejecting()
const retry = runtimeRegistration(ctx.invariants.register('@deepseek-ai/dsh-publication-probe', () => {}))
await retry
await retry()
})
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
await fiber.dispose()
expect(() => service.register('@deepseek-ai/dsh-session', () => {})).toThrow(/inactive/i)
})
})

View File

@@ -0,0 +1,21 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib/types"
},
"include": [
"src"
],
"references": [
{
"path": "../../../vendor/cosmokit"
},
{
"path": "../../../vendor/cordis"
},
{
"path": "../../../vendor/schemastery"
}
]
}