mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
docs: trim CoT leakage from post-purge prose
Remove dead design-session citations, change narration, indexical stamps, and review-adjacent justification found by the dsh-trim-cot-leakage recall batteries in prose that landed after the last purge. Bilingual README pairs are re-recorded.
This commit is contained in:
@@ -92,8 +92,8 @@ describe('web e2e: agent-preset authoring is a host-side copy', () => {
|
|||||||
const snapshot = await captureStableAria(page, '[role="dialog"]', scaffold.workspaceCwd)
|
const snapshot = await captureStableAria(page, '[role="dialog"]', scaffold.workspaceCwd)
|
||||||
|
|
||||||
await compareOrRefreshGolden(SECTION_EXPECTED, snapshot, MODE)
|
await compareOrRefreshGolden(SECTION_EXPECTED, snapshot, MODE)
|
||||||
// The intro carries the guidance a create button used to imply, and the
|
// The intro states the copy path directly, and the shipped rows offer
|
||||||
// shipped rows offer view/copy but never delete or a location — their
|
// view/copy but never delete or a location — their
|
||||||
// install is overwritten by upgrades and is not the user's to manage.
|
// install is overwritten by upgrades and is not the user's to manage.
|
||||||
expect(snapshot).toContain('或用「创造模式」让 Agent 帮你创建')
|
expect(snapshot).toContain('或用「创造模式」让 Agent 帮你创建')
|
||||||
expect(snapshot).not.toContain('新建预设')
|
expect(snapshot).not.toContain('新建预设')
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ describe.skipIf(MODE === 'record')('web e2e: another usable provider ends first-
|
|||||||
await page.getByRole('button', { name: '设置', exact: true }).click()
|
await page.getByRole('button', { name: '设置', exact: true }).click()
|
||||||
const settings = page.getByRole('dialog', { name: '设置' })
|
const settings = page.getByRole('dialog', { name: '设置' })
|
||||||
await settings.waitFor({ timeout: 10_000 })
|
await settings.waitFor({ timeout: 10_000 })
|
||||||
// The onboarding step no longer navigates into Settings on dismissal, so
|
// Dismissing the onboarding step leaves Settings closed, so enter the
|
||||||
// enter the Models section explicitly before exercising its normal cards.
|
// Models section explicitly before exercising its normal cards.
|
||||||
await settings.getByRole('button', { name: '模型' }).click()
|
await settings.getByRole('button', { name: '模型' }).click()
|
||||||
const setupKey = settings.getByRole('textbox', { name: 'API 密钥', exact: true })
|
const setupKey = settings.getByRole('textbox', { name: 'API 密钥', exact: true })
|
||||||
await setupKey.waitFor({ timeout: 10_000 })
|
await setupKey.waitFor({ timeout: 10_000 })
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
* published tarball has the same integrity is skipped, and a version whose
|
* published tarball has the same integrity is skipped, and a version whose
|
||||||
* published tarball differs fails the run — that last case means the content
|
* published tarball differs fails the run — that last case means the content
|
||||||
* changed without a version bump. Skipping on identical integrity is what makes
|
* changed without a version bump. Skipping on identical integrity is what makes
|
||||||
* re-running the publish step over the same artifact safe, which matters here
|
* re-running the publish step over the same artifact safe. Without the
|
||||||
* because a partial publication used to leave no way forward: republishing an
|
* integrity skip, a partial publication has no way forward: republishing an
|
||||||
* existing version fails permanently.
|
* existing version fails permanently.
|
||||||
*
|
*
|
||||||
* Usage: `node scripts/publish-release.mjs [packed dir]`.
|
* Usage: `node scripts/publish-release.mjs [packed dir]`.
|
||||||
|
|||||||
@@ -808,8 +808,8 @@ describe('hand-declared providers', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('names the provider as the refreshed directory reports it after a rename', async () => {
|
it('names the provider as the refreshed directory reports it after a rename', async () => {
|
||||||
// The status line used to echo the target captured when the card opened,
|
// A name can change after the card opens, so the saved status reads the
|
||||||
// which never lied while the name could not change. It can now.
|
// refreshed directory name rather than the target captured at open.
|
||||||
const { face } = await mountSection({
|
const { face } = await mountSection({
|
||||||
providers: { 'acme-gateway': { displayName: 'Acme Gateway', api: 'openai-completions' } },
|
providers: { 'acme-gateway': { displayName: 'Acme Gateway', api: 'openai-completions' } },
|
||||||
declaredRoutes: ['acme-gateway'],
|
declaredRoutes: ['acme-gateway'],
|
||||||
|
|||||||
@@ -69,8 +69,9 @@ describe('tool-call-model', () => {
|
|||||||
expect(model.title).toBe('Tool call')
|
expect(model.title).toBe('Tool call')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has dropped the v2 mount verbs that no longer exist', () => {
|
it('renders v2 mount verbs with no current tool as generic calls', () => {
|
||||||
// Keeping them would be a mapping for a tool nothing can call.
|
// No current tool implements these v2 verbs, so a mapping would be
|
||||||
|
// unreachable.
|
||||||
expect(classifyTool('cordis_mount')).toBe('others')
|
expect(classifyTool('cordis_mount')).toBe('others')
|
||||||
expect(toolRowModel('cordis_mount', running({ name: 'cordis_mount', argsRaw: '{}' })).title).toBe('Tool call')
|
expect(toolRowModel('cordis_mount', running({ name: 'cordis_mount', argsRaw: '{}' })).title).toBe('Tool call')
|
||||||
expect(toolRowModel('cordis_unmount', running({ name: 'cordis_unmount', argsRaw: '{}' })).title).toBe('Tool call')
|
expect(toolRowModel('cordis_unmount', running({ name: 'cordis_unmount', argsRaw: '{}' })).title).toBe('Tool call')
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
折叠搜索是视图和添加操作旁的一枚区头按钮。在轨道中,添加和搜索会渲染为沿外壳共用横向进入路径移动的 36px 控件。激活搜索后,输入框会扩展并占据区头;点击外部只会收起经清除首尾空白后为空的查询——但轨道搜索手势仍在进行期间(直至列滑动结束、焦点落入输入框)除外,这样触发展开的那次点击不会收起它刚打开的搜索——而清除控件总会重置并收起搜索。非空白查询会以单一扁平结果列表替代任一浏览模式:不区分大小写的标题和 Workspace 子串匹配项会立即显示,经 250 ms 防抖的 Host 请求则会加入经过排序的当前对话内容匹配项及其摘要片段。英文搜索输入框及其防御性请求路径会移除 NUL,将查询限制在传输 schema 规定的 500 个 UTF-16 代码单元内且不会拆分代理项对,并保留现有的防抖与取消行为。每次新查询都会中止前一个请求;内容搜索失败时,元数据匹配项仍会显示,同时给出警告。列表最多显示 20 条结果,并会在查询过宽时提示用户缩小范围;打开所选 Session 时既不会清除查询,也不会跳转至特定事件。
|
折叠搜索是视图和添加操作旁的一枚区头按钮。在轨道中,添加和搜索会渲染为沿外壳共用横向进入路径移动的 36px 控件。激活搜索后,输入框会扩展并占据区头;点击外部只会收起经清除首尾空白后为空的查询——但轨道搜索手势仍在进行期间(直至列滑动结束、焦点落入输入框)除外,这样触发展开的那次点击不会收起它刚打开的搜索——而清除控件总会重置并收起搜索。非空白查询会以单一扁平结果列表替代任一浏览模式:不区分大小写的标题和 Workspace 子串匹配项会立即显示,经 250 ms 防抖的 Host 请求则会加入经过排序的当前对话内容匹配项及其摘要片段。英文搜索输入框及其防御性请求路径会移除 NUL,将查询限制在传输 schema 规定的 500 个 UTF-16 代码单元内且不会拆分代理项对,并保留现有的防抖与取消行为。每次新查询都会中止前一个请求;内容搜索失败时,元数据匹配项仍会显示,同时给出警告。列表最多显示 20 条结果,并会在查询过宽时提示用户缩小范围;打开所选 Session 时既不会清除查询,也不会跳转至特定事件。
|
||||||
|
|
||||||
该选择器通过全局 `useWorkspaces` hook 列出真实的 Host Workspace 实体。选择 Workspace 会调用 slot owner 的 `onPick` 回调,重新定位前端 Session 对象。不同的规范化路径即使 basename 和显示标题相同,仍会作为由 id 区分的独立 Workspace;侧边栏的悬停详情把 POSIX 家目录及其后代显示为 `~`/`~/…`,Windows 路径保持原样。每个注册各自声明一个**目录流子 slot**(`single` kind:`conversation.hero.workspace.directoryFlow`/`sidebar.workspaces.directoryFlow`),由组合的选择器包 client half 填入其选取交互——标准组合使用 [`-native`](../../host/directory-picker-native/README.zh.md) 后端的无渲染 OS 选择器驱动,`-browse` 组合下则是应用内浏览对话框。平铺显示的 **添加工作区…** 操作仅在当前界面的 slot 被占用时渲染(每次菜单渲染读取占用状态;slot 为空意味着该组合没有目录选择能力——seam 文档化的无流程默认行为,此时侧边栏区头直接不渲染添加按钮,而非留下一个点了没反应的按钮)。本包持有触发与接纳:占用方通过 slot 的属主交互约定(`open`/`busy`/`onPicked`/`onCancel`/`onError`)每次打开上报一个所选路径,owner 通过对象层接纳它,并等待 Workspace 列表投影刷新后才选中已提交的 Workspace;取消操作不会显示提示,错误落入可重试的文件夹对话框,其 **重新选择** 会重新打开流程。添加只有一条路径:占用者自带的新建文件夹能力已经覆盖了全新目录,因此不再单设按名称创建的对话框。菜单只在确有多个目标可选时出现——没有 Workspace 可列时,锚点手势直接拉起流程,而不是弹出只有一行的浮层;在列表基线落地前,空列表不算最终结果。运行时 Session 与 Workspace 服务负责物化。Workspace 行内的 Delete 操作会打开确认框,说明保留边界、阻止重复提交,并在失败时保持打开;成功后,该分组会被移除,其 Session 则留在 Ungrouped 下。Session 行内的 Rename 操作打开同款浏览器持有的对话框,并以该行的显示标题预填:客户端不设名称冲突规则(host 负责规范化,可能以 `title-invalid` 拒绝,错误渲染在对话框告警区);确认未修改的标题是有意允许的——这正是把当前自动标题钉住、不再被重新生成覆盖的手势。Session 行内的 Archive 操作不经确认对话框直接提交(非破坏性:日志和 workspace 记账席位保持不变),通过 `ctx.workspaces.archiveSession` 归档;归档集合回声落地后,该行从所有分组视图——workspace 分组、Ungrouped、内容搜索和平铺列表——中消失,失败只作为控制台诊断输出,树保持不变。空白的「新会话」行只是占位符:不渲染行菜单和时间标签(其中还没有发生任何事),重命名、fork 和归档都从首条提示词落地后才可用。
|
该选择器通过全局 `useWorkspaces` hook 列出真实的 Host Workspace 实体。选择 Workspace 会调用 slot owner 的 `onPick` 回调,重新定位前端 Session 对象。不同的规范化路径即使 basename 和显示标题相同,仍会作为由 id 区分的独立 Workspace;侧边栏的悬停详情把 POSIX 家目录及其后代显示为 `~`/`~/…`,Windows 路径保持原样。每个注册各自声明一个**目录流子 slot**(`single` kind:`conversation.hero.workspace.directoryFlow`/`sidebar.workspaces.directoryFlow`),由组合的选择器包 client half 填入其选取交互——标准组合使用 [`-native`](../../host/directory-picker-native/README.zh.md) 后端的无渲染 OS 选择器驱动,`-browse` 组合下则是应用内浏览对话框。平铺显示的 **添加工作区…** 操作仅在当前界面的 slot 被占用时渲染(每次菜单渲染读取占用状态;slot 为空意味着该组合没有目录选择能力——seam 文档化的无流程默认行为,此时侧边栏区头直接不渲染添加按钮,而非留下一个点了没反应的按钮)。本包持有触发与接纳:占用方通过 slot 的属主交互约定(`open`/`busy`/`onPicked`/`onCancel`/`onError`)每次打开上报一个所选路径,owner 通过对象层接纳它,并等待 Workspace 列表投影刷新后才选中已提交的 Workspace;取消操作不会显示提示,错误落入可重试的文件夹对话框,其 **重新选择** 会重新打开流程。添加只有一条路径:占用者自带的新建文件夹能力已经覆盖了全新目录,因此不设独立的按名称创建对话框。菜单只在确有多个目标可选时出现——没有 Workspace 可列时,锚点手势直接拉起流程,而不是弹出只有一行的浮层;在列表基线落地前,空列表不算最终结果。运行时 Session 与 Workspace 服务负责物化。Workspace 行内的 Delete 操作会打开确认框,说明保留边界、阻止重复提交,并在失败时保持打开;成功后,该分组会被移除,其 Session 则留在 Ungrouped 下。Session 行内的 Rename 操作打开同款浏览器持有的对话框,并以该行的显示标题预填:客户端不设名称冲突规则(host 负责规范化,可能以 `title-invalid` 拒绝,错误渲染在对话框告警区);确认未修改的标题是有意允许的——这正是把当前自动标题钉住、使其不被重新生成覆盖的手势。Session 行内的 Archive 操作不经确认对话框直接提交(非破坏性:日志和 workspace 记账席位保持不变),通过 `ctx.workspaces.archiveSession` 归档;归档集合回声落地后,该行从所有分组视图——workspace 分组、Ungrouped、内容搜索和平铺列表——中消失,失败只作为控制台诊断输出,树保持不变。空白的「新会话」行只是占位符:不渲染行菜单和时间标签(其中还没有发生任何事),重命名、fork 和归档都从首条提示词落地后才可用。 (docs: trim CoT leakage from post-purge prose)
|
||||||
|
|
||||||
Workspace 和 Session 悬浮卡片会复制对应行被截断的值:激活 Workspace 卡片会写入其完整目录路径,激活非空白 Session 卡片则会写入其完整显示标题。临时的空白「新会话」卡片保持只读,因为其本地化标签是占位文案,并非会话内容。只有浏览器接受剪贴板写入后,卡片才会显示由字典提供的已复制状态。
|
Workspace 和 Session 悬浮卡片会复制对应行被截断的值:激活 Workspace 卡片会写入其完整目录路径,激活非空白 Session 卡片则会写入其完整显示标题。临时的空白「新会话」卡片保持只读,因为其本地化标签是占位文案,并非会话内容。只有浏览器接受剪贴板写入后,卡片才会显示由字典提供的已复制状态。
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,9 @@
|
|||||||
* product.
|
* product.
|
||||||
*
|
*
|
||||||
* pnpm creates a workspace package's bin link only when the link target exists
|
* pnpm creates a workspace package's bin link only when the link target exists
|
||||||
* at install time. Pointing the bin straight at `lib/bin.js` — a build product —
|
* at install time. `lib/bin.js` is a build product and is absent on a clean
|
||||||
* left the link uncreated on every clean checkout, so the command was missing
|
* checkout, so this committed file is the link target; it forwards to the build
|
||||||
* from `node_modules/.bin` even after a build produced the file, and only an
|
* product when the command runs.
|
||||||
* install that happened to follow a build brought it back. This file is
|
|
||||||
* committed, so the link is always created; the build product is resolved when
|
|
||||||
* the command actually runs.
|
|
||||||
* @module @deepseek-ai/dsh-experimental-webworker-packer/bin
|
* @module @deepseek-ai/dsh-experimental-webworker-packer/bin
|
||||||
*/
|
*/
|
||||||
import { existsSync } from 'node:fs'
|
import { existsSync } from 'node:fs'
|
||||||
|
|||||||
@@ -8,8 +8,9 @@
|
|||||||
* answers `/`, the directory that actually holds the entry. Node's three are
|
* answers `/`, the directory that actually holds the entry. Node's three are
|
||||||
* purely lexical and answer `/a/b`. A `node:path` proxy owes callers Node's
|
* purely lexical and answer `/a/b`. A `node:path` proxy owes callers Node's
|
||||||
* literal answers, so it needs its own port of Node's implementation rather than
|
* literal answers, so it needs its own port of Node's implementation rather than
|
||||||
* a facade over this module (`apps/web-preview` keeps one; the divergence covers
|
* a facade over this module; the divergence covers 45 of ~200 cases, all in
|
||||||
* 45 of ~200 cases, all in these three functions).
|
* these three functions, and `../../tests/node/path-diff.spec.ts` enumerates
|
||||||
|
* them.
|
||||||
* @module @deepseek-ai/dsh-experimental-webworker-runtime/src/module-system/posix-path
|
* @module @deepseek-ai/dsh-experimental-webworker-runtime/src/module-system/posix-path
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -2,7 +2,8 @@
|
|||||||
* `node:path` for the worker: the POSIX algorithm, transliterated from Node's
|
* `node:path` for the worker: the POSIX algorithm, transliterated from Node's
|
||||||
* implementation. It is NOT a face over the worker host's `posixPath`: that helper
|
* implementation. It is NOT a face over the worker host's `posixPath`: that helper
|
||||||
* normalizes before splitting, so `dirname('/a/b/..')` answers `/` where Node
|
* normalizes before splitting, so `dirname('/a/b/..')` answers `/` where Node
|
||||||
* answers `/a/b` (45 cases diverge — `.artifacts/p2/path-diff.ts` enumerates them).
|
* answers `/a/b` (45 cases diverge; `../../../../tests/node/path-diff.spec.ts`
|
||||||
|
* enumerates them).
|
||||||
* A `node:` proxy has to answer what Node answers, since VFS paths were built with
|
* A `node:` proxy has to answer what Node answers, since VFS paths were built with
|
||||||
* Node semantics. `win32` members throw: the worker host reports
|
* Node semantics. `win32` members throw: the worker host reports
|
||||||
* `process.platform === 'linux'`, so a Windows branch means a bug.
|
* `process.platform === 'linux'`, so a Windows branch means a bug.
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* `node:worker_threads` stub. Nested workers are out of scope for v1, so the
|
* `node:worker_threads` stub. Nested workers are unsupported, so the workflow
|
||||||
* workflow and code-runtime plugin bodies mount and fail on use. The
|
* and code-runtime plugin bodies mount and fail on use. The
|
||||||
* thread-identity values are real: they say "this is the main thread", which is
|
* thread-identity values are real: they say "this is the main thread", which is
|
||||||
* what the worker host is from the tree's point of view.
|
* what the worker host is from the tree's point of view.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -9,30 +9,10 @@
|
|||||||
* emits, so a rolldown upgrade that starts emitting an unseen module form shows
|
* emits, so a rolldown upgrade that starts emitting an unseen module form shows
|
||||||
* up here first.
|
* up here first.
|
||||||
*
|
*
|
||||||
* Consolidated from `.artifacts/w0-lexer-probe.ts` (part 2). Two deliberate
|
* Module-syntax statistics are counted from the acorn AST, so the check has no
|
||||||
* changes for the terminal form:
|
* separate lexer dependency. Baseline exemptions are a pinned list, not a count:
|
||||||
*
|
* four files cannot be imported by Node in this repository for reasons unrelated
|
||||||
* 1. **No `es-module-lexer`.** The lexer was retired as a runtime dependency
|
* to the transform, and an unexpected member fails the run.
|
||||||
* when the single acorn pass replaced the two-pass pipeline, so the
|
|
||||||
* statistics it used to contribute are counted from the acorn AST instead.
|
|
||||||
* The probe's part 1 (lexer field semantics over 20 sample forms) is dropped
|
|
||||||
* entirely: it documented the behaviour of a component that no longer runs.
|
|
||||||
* The forms themselves are covered as emitted-code assertions in
|
|
||||||
* `transform-check.ts`.
|
|
||||||
* 2. **The baseline exemptions are a pinned list, not a count.** Four files
|
|
||||||
* cannot be imported by Node in this repository for reasons unrelated to the
|
|
||||||
* transform; the probe merely counted them, so a fifth would have gone
|
|
||||||
* unnoticed. Here they are named, and an unexpected member fails the run.
|
|
||||||
*
|
|
||||||
* Not consolidated: `.artifacts/v3-oracle.ts`, the byte-for-byte comparison
|
|
||||||
* against the retired lexer pipeline. It was a **retirement gate** and it has
|
|
||||||
* been through (`files=228 residualDifferences=0 lineDrift=0`). Keeping it as a
|
|
||||||
* standing check would mean keeping two abandoned implementations alive
|
|
||||||
* (`.artifacts/oracle-esm-to-cjs.ts`, `.artifacts/oracle-rewrite-await.ts`)
|
|
||||||
* forever to compare against. The one real defect it caught that no other signal
|
|
||||||
* could — `new.target` is also a `MetaProperty` — is preserved as a direct
|
|
||||||
* assertion (`transform-check.ts`, trap 8), which is where that knowledge
|
|
||||||
* belongs now.
|
|
||||||
*
|
*
|
||||||
* Cost: this walks the whole build output and imports every bundle, so it takes
|
* Cost: this walks the whole build output and imports every bundle, so it takes
|
||||||
* tens of seconds and needs `pnpm run build:lib:host` to have run. It is a
|
* tens of seconds and needs `pnpm run build:lib:host` to have run. It is a
|
||||||
@@ -275,7 +255,7 @@ async function runTransformed(code: string, path: string): Promise<Record<string
|
|||||||
return exports
|
return exports
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Module-syntax counts read from the AST, replacing what the retired lexer reported. */
|
/** Module-syntax counts read from the AST. */
|
||||||
interface Counts {
|
interface Counts {
|
||||||
staticImports: number
|
staticImports: number
|
||||||
dynamicImports: number
|
dynamicImports: number
|
||||||
@@ -369,7 +349,7 @@ if (files.length === 0) {
|
|||||||
|
|
||||||
// The debugging contract, over the whole corpus: a transformed body has the
|
// The debugging contract, over the whole corpus: a transformed body has the
|
||||||
// same line count as its source, so a stack frame still points at the right
|
// same line count as its source, so a stack frame still points at the right
|
||||||
// line. This is the property the retired oracle measured as "line drift".
|
// line.
|
||||||
const sourceLines = source.split('\n').length
|
const sourceLines = source.split('\n').length
|
||||||
const codeLines = code.split('\n').length
|
const codeLines = code.split('\n').length
|
||||||
if (sourceLines !== codeLines) {
|
if (sourceLines !== codeLines) {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Semantic check of the worker module transform (`src/compile/transform.ts`): what the
|
* Semantic check of the worker module transform (`src/compile/transform.ts`): what the
|
||||||
* emitted CommonJS body looks like for each module form, how suspension points
|
* emitted CommonJS body looks like for each module form, how suspension points
|
||||||
* are rewritten, that line numbers survive, which forms are refused, and that
|
* are rewritten, that line numbers survive, which forms are refused, and that
|
||||||
* every trap the retired lexer pipeline hit stays fixed.
|
* every covered trap form stays fixed.
|
||||||
*
|
*
|
||||||
* Scope boundary: this file checks the transform itself; the image collector's
|
* Scope boundary: this file checks the transform itself; the image collector's
|
||||||
* loop around it is covered by the packer's `transform-image.spec.ts`.
|
* loop around it is covered by the packer's `transform-image.spec.ts`.
|
||||||
@@ -12,9 +12,10 @@
|
|||||||
* one — the code parses as script, publishes the right bindings, keeps line
|
* one — the code parses as script, publishes the right bindings, keeps line
|
||||||
* count, and routes suspension through `__als`.
|
* count, and routes suspension through `__als`.
|
||||||
*
|
*
|
||||||
* The trap cases are the module forms that broke real boots under the retired
|
* The trap cases are module forms that break a boot when the transform
|
||||||
* lexer pipeline. Five cannot recur under an AST pass, but retaining them pins
|
* mishandles them. Five traps cannot recur while the AST pass is the parser,
|
||||||
* the behavior against a future parser swap.
|
* but they stay checked because a future parser swap would reintroduce exactly
|
||||||
|
* them.
|
||||||
*/
|
*/
|
||||||
import { expect, test } from 'vitest'
|
import { expect, test } from 'vitest'
|
||||||
import { parse } from 'acorn'
|
import { parse } from 'acorn'
|
||||||
@@ -208,7 +209,7 @@ check(
|
|||||||
|
|
||||||
{
|
{
|
||||||
// Local exports are getters, so a later assignment is observable through
|
// Local exports are getters, so a later assignment is observable through
|
||||||
// `exports` — the ESM live-binding property the report calls out explicitly.
|
// `exports` — the ESM live-binding property.
|
||||||
const code = transformModule('export let counter = 0\nexport function bump() { counter += 1 }\n', 'probe.js')
|
const code = transformModule('export let counter = 0\nexport function bump() { counter += 1 }\n', 'probe.js')
|
||||||
parsesAsScript('live binding', code)
|
parsesAsScript('live binding', code)
|
||||||
const exports = runBody(code)
|
const exports = runBody(code)
|
||||||
@@ -224,9 +225,7 @@ check(
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Trap 4 in the lexer report: `export const a = 1, b = 2` reported only the
|
// Trap 4: a multi-declarator export publishes every binding.
|
||||||
// first declarator, so the AST pass upgraded this from "loud refusal" to
|
|
||||||
// "correctly supported". Both bindings must appear.
|
|
||||||
const code = transformModule('export const a = 1, b = 2\n', 'probe.js')
|
const code = transformModule('export const a = 1, b = 2\n', 'probe.js')
|
||||||
parsesAsScript('multi-declarator export', code)
|
parsesAsScript('multi-declarator export', code)
|
||||||
const exports = runBody(code)
|
const exports = runBody(code)
|
||||||
@@ -415,7 +414,7 @@ function recordingAls(): { als: Record<string, unknown>; calls: string[] } {
|
|||||||
|
|
||||||
{
|
{
|
||||||
// for-await desugars to an explicit loop; `return()` must run only on abrupt
|
// for-await desugars to an explicit loop; `return()` must run only on abrupt
|
||||||
// completion, which is the language rule the report calls out. The two
|
// completion, which is the language rule. The two
|
||||||
// completion paths need two different loop bodies, so they are separate cases.
|
// completion paths need two different loop bodies, so they are separate cases.
|
||||||
const plain = 'export const run = async (src) => { const seen = []\n'
|
const plain = 'export const run = async (src) => { const seen = []\n'
|
||||||
+ 'for await (const item of src) { seen.push(item) }\n'
|
+ 'for await (const item of src) { seen.push(item) }\n'
|
||||||
@@ -594,24 +593,24 @@ refuses('unparseable source is refused', 'export const = \n', 'parse failed')
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// 9. Trap regressions. Each case broke a real boot under the retired lexer
|
// 9. Trap regressions. Each case is a module form that breaks a boot when the
|
||||||
// pipeline; the AST pass must keep them fixed.
|
// transform mishandles it; the AST pass must keep them fixed.
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
{
|
{
|
||||||
// Trap 1: a file with no module syntax can still contain a dynamic import.
|
// Trap 1: a file with no module syntax can still contain a dynamic import. A
|
||||||
// Early-returning on "no module syntax" left it unrewritten and it escaped to
|
// transform that skips such files would leave it unrewritten, and it would
|
||||||
// the host engine's parser.
|
// escape to the host engine's parser.
|
||||||
const code = transformModule("module.exports = () => import('./x.js')\n", 'probe.js')
|
const code = transformModule("module.exports = () => import('./x.js')\n", 'probe.js')
|
||||||
contains('trap 1: dynamic import in a CommonJS file is still rewritten', code, '__dsh$dynImport')
|
contains('trap 1: dynamic import in a CommonJS file is still rewritten', code, '__dsh$dynImport')
|
||||||
parsesAsScript('trap 1', code)
|
parsesAsScript('trap 1', code)
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Trap 2: `export {}` is a bundler module marker. The lexer reported nothing
|
// Trap 2: `export {}` is a bundler module marker and must be removed before
|
||||||
// for it, so it survived into `new Function` as `Unexpected token 'export'`.
|
// `new Function` parses the body. The needle is the keyword in statement
|
||||||
// The needle is the keyword in statement position, since `exports.` in the
|
// position, since `exports.` in the prologue legitimately contains the same
|
||||||
// prologue legitimately contains the same letters.
|
// letters.
|
||||||
const code = transformModule('export {};\n', 'probe.js')
|
const code = transformModule('export {};\n', 'probe.js')
|
||||||
lacks('trap 2: bare export {} is removed', code, 'export {')
|
lacks('trap 2: bare export {} is removed', code, 'export {')
|
||||||
lacks('trap 2: no export keyword survives', code, 'export;')
|
lacks('trap 2: no export keyword survives', code, 'export;')
|
||||||
@@ -630,10 +629,10 @@ refuses('unparseable source is refused', 'export const = \n', 'parse failed')
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Trap 6, the most costly one: a block comment before a class member named
|
// Trap 6: a block comment before a class member named `import` must not be
|
||||||
// `import` made the lexer report a dynamic import, renaming
|
// treated as a dynamic import. Renaming `EntryTree.prototype.import` breaks
|
||||||
// `EntryTree.prototype.import` and breaking the loading chain at
|
// the loading chain at `Entry._init` with
|
||||||
// `Entry._init` with "this.parent.tree.import is not a function".
|
// "this.parent.tree.import is not a function".
|
||||||
const source = 'export class A {\n /** doc */ import(name) { return name }\n}\n'
|
const source = 'export class A {\n /** doc */ import(name) { return name }\n}\n'
|
||||||
const code = transformModule(source, 'probe.js')
|
const code = transformModule(source, 'probe.js')
|
||||||
lacks('trap 6: a method named import is not rewritten', code, '__dsh$dynImport')
|
lacks('trap 6: a method named import is not rewritten', code, '__dsh$dynImport')
|
||||||
@@ -643,19 +642,19 @@ refuses('unparseable source is refused', 'export const = \n', 'parse failed')
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Trap 7: a comment between `export` and the declaration keyword made the
|
// Trap 7: a comment between `export` and the declaration keyword must not
|
||||||
// gap-matching regex miss, refusing zod's `export /*@__NO_SIDE_EFFECTS__*/ function`
|
// hide the declaration; refusing zod's
|
||||||
// and taking 30-odd roster rows down with it.
|
// `export /*@__NO_SIDE_EFFECTS__*/ function` takes 30-odd roster rows down
|
||||||
|
// with it.
|
||||||
const code = transformModule('export /*@__NO_SIDE_EFFECTS__*/ function $constructor(x) { return x }\n', 'probe.js')
|
const code = transformModule('export /*@__NO_SIDE_EFFECTS__*/ function $constructor(x) { return x }\n', 'probe.js')
|
||||||
parsesAsScript('trap 7', code)
|
parsesAsScript('trap 7', code)
|
||||||
check('trap 7: export with an interposed comment still publishes', typeof runBody(code).$constructor, 'function')
|
check('trap 7: export with an interposed comment still publishes', typeof runBody(code).$constructor, 'function')
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// The trap the AST pass introduced and the byte-level oracle caught:
|
// `new.target` is also a MetaProperty. Replacing every MetaProperty would
|
||||||
// `new.target` is also a MetaProperty. Replacing every MetaProperty made
|
// make `new.target === Cls` permanently false, silently disabling
|
||||||
// `new.target === Cls` permanently false, silently disabling abstract-seam
|
// abstract-seam guards in `jobs` and `llm`.
|
||||||
// guards in `jobs` and `llm`.
|
|
||||||
const source = 'export class Base {\n constructor() { this.direct = new.target === Base }\n}\n'
|
const source = 'export class Base {\n constructor() { this.direct = new.target === Base }\n}\n'
|
||||||
const code = transformModule(source, 'probe.js')
|
const code = transformModule(source, 'probe.js')
|
||||||
contains('trap 8: new.target survives verbatim', code, 'new.target')
|
contains('trap 8: new.target survives verbatim', code, 'new.target')
|
||||||
@@ -668,9 +667,9 @@ refuses('unparseable source is refused', 'export const = \n', 'parse failed')
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Shebang handling (found while packing `yaml/bin.mjs`): `#!` is only legal at
|
// Shebang handling: `#!` is only legal at offset 0, which the prologue
|
||||||
// offset 0, which the prologue occupies. It is commented out in place so both
|
// occupies. It is commented out in place so both offsets and the line count
|
||||||
// offsets and the line count stay put.
|
// stay put.
|
||||||
const source = '#!/usr/bin/env node\nexport const main = 1\n'
|
const source = '#!/usr/bin/env node\nexport const main = 1\n'
|
||||||
const code = transformModule(source, 'probe.js')
|
const code = transformModule(source, 'probe.js')
|
||||||
lacks('shebang is not left in the emitted body', code, '#!')
|
lacks('shebang is not left in the emitted body', code, '#!')
|
||||||
|
|||||||
@@ -4,10 +4,9 @@
|
|||||||
* Cordis's `LoggerService` accepts every message and, with no exporter mounted,
|
* Cordis's `LoggerService` accepts every message and, with no exporter mounted,
|
||||||
* only fills a ring buffer. No profile in this repository mounts one, so a
|
* only fills a ring buffer. No profile in this repository mounts one, so a
|
||||||
* provider that fails and is skipped — the skill registry logs exactly that —
|
* provider that fails and is skipped — the skill registry logs exactly that —
|
||||||
* used to look identical to one that found nothing. That is how an empty skill
|
* is indistinguishable from one that found nothing. The sink is exercised here
|
||||||
* catalog hid a filesystem fault through two rounds of diagnosis, so the sink is
|
* rather than trusted: a diagnostic that runs nothing is a diagnostic that
|
||||||
* exercised here rather than trusted: a diagnostic nothing runs is a diagnostic
|
* silently stops working.
|
||||||
* that silently stops working.
|
|
||||||
*/
|
*/
|
||||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||||
import { installLogSink, type LogExporter, type LogMessage } from '../src/worker-host.ts'
|
import { installLogSink, type LogExporter, type LogMessage } from '../src/worker-host.ts'
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
/**
|
/**
|
||||||
* The `node:child_process` face over the in-worker shell, and the ladder above
|
* The `node:child_process` face over the in-worker shell, and the ladder above
|
||||||
* it: the REAL local subprocess service, running unmodified against this
|
* it: the REAL local subprocess service, running unmodified against this
|
||||||
* module instead of a host kernel. That ladder is what the bash tool walks in
|
* module instead of a host kernel. The bash tool walks this same ladder in the
|
||||||
* the browser, so proving it here is what makes the browser probe a
|
* browser.
|
||||||
* confirmation rather than the only evidence.
|
|
||||||
*
|
*
|
||||||
* A Node test host has no DOM `Worker`, so the commands here run through the
|
* A Node test host has no DOM `Worker`, so the commands here run through the
|
||||||
* inline strategy; the worker strategy and its frames are proven in
|
* inline strategy; the worker strategy and its frames are proven in
|
||||||
* `../shell/shell-process.spec.ts`, and both meet again in the preview probe.
|
* `../shell/shell-process.spec.ts`.
|
||||||
*
|
*
|
||||||
* `process.kill` is redirected to the worker's process table for the same
|
* `process.kill` is redirected to the worker's process table for the same
|
||||||
* reason the worker does it: the subprocess service polls process-group
|
* reason the worker does it: the subprocess service polls process-group
|
||||||
|
|||||||
@@ -6,12 +6,11 @@
|
|||||||
* `node:path/posix`", so Node itself is the oracle and every case is compared
|
* `node:path/posix`", so Node itself is the oracle and every case is compared
|
||||||
* rather than asserted against a hand-written expectation. The corpus is the
|
* rather than asserted against a hand-written expectation. The corpus is the
|
||||||
* shapes a VFS path actually takes (absolute image paths, `node_modules`
|
* shapes a VFS path actually takes (absolute image paths, `node_modules`
|
||||||
* specifiers, `.bin` entries) plus the edge forms that historically diverge
|
* specifiers, `.bin` entries) plus the edge forms that diverge between the two
|
||||||
* (repeated slashes, trailing dots, `..` past the root).
|
* implementations (repeated slashes, trailing dots, `..` past the root).
|
||||||
*
|
*
|
||||||
* Migrated from apps/web-preview/scripts/checks/path-diff.ts. Imports go through
|
* Imports go through the package name so the harness and the shim resolve to one
|
||||||
* the package name so the harness and the shim resolve to one module instance
|
* module instance (see `../polyfill/als-shim.spec.ts` for why that matters).
|
||||||
* (see `../polyfill/als-shim.spec.ts` for why that matters).
|
|
||||||
*/
|
*/
|
||||||
import { expect, test } from 'vitest'
|
import { expect, test } from 'vitest'
|
||||||
import { posix as nodePosix } from 'node:path'
|
import { posix as nodePosix } from 'node:path'
|
||||||
|
|||||||
@@ -16,8 +16,7 @@
|
|||||||
* - both completion paths do this, which is why the token always fulfills.
|
* - both completion paths do this, which is why the token always fulfills.
|
||||||
*
|
*
|
||||||
* The shim-backed end of the same contract (does a real AsyncLocalStorage
|
* The shim-backed end of the same contract (does a real AsyncLocalStorage
|
||||||
* actually fold, do the hooks cover timers) is `als-shim.spec.ts`, and the
|
* actually fold, do the hooks cover timers) is `als-shim.spec.ts`. This file is
|
||||||
* cross-session behavioural proof is the browser concurrency probe. This file is
|
|
||||||
* the middle layer: the protocol, in isolation.
|
* the middle layer: the protocol, in isolation.
|
||||||
*/
|
*/
|
||||||
import { expect, test } from 'vitest'
|
import { expect, test } from 'vitest'
|
||||||
@@ -121,8 +120,8 @@ function recordingCausality(): {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// The rejection path restores too, and only then rethrows: a catch clause must
|
// The rejection path restores too, and only then rethrows: a catch clause
|
||||||
// observe the caller's store, which is the case the browser probe pinned.
|
// must observe the caller's store.
|
||||||
const state = recordingCausality()
|
const state = recordingCausality()
|
||||||
const als = createAlsRuntime(state.causality)
|
const als = createAlsRuntime(state.causality)
|
||||||
state.current = 'session-C'
|
state.current = 'session-C'
|
||||||
@@ -310,9 +309,9 @@ function recordingCausality(): {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// 6. The inert runtime. `?als=inert` is the browser probe's control arm: the
|
// 6. The inert runtime. Without a causality face, the rewrite still runs and
|
||||||
// rewrite still runs and still hops a microtask, but no state moves. That
|
// still hops a microtask, but no state moves. A comparison arm built on this
|
||||||
// control must be genuinely inert, or the probe loses its discriminating power.
|
// mode must be genuinely inert, or the comparison proves nothing.
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,15 +17,11 @@
|
|||||||
*
|
*
|
||||||
* Scope boundary: this file owns the shim (the state). `als-runtime.spec.ts`
|
* Scope boundary: this file owns the shim (the state). `als-runtime.spec.ts`
|
||||||
* owns the protocol that moves snapshots around, with the causality face stubbed.
|
* owns the protocol that moves snapshots around, with the causality face stubbed.
|
||||||
* The cross-session end-to-end proof is the browser concurrency probe, whose
|
|
||||||
* control arm (`?als=inert`) relies on the protocol being genuinely inert.
|
|
||||||
*
|
*
|
||||||
* Migrated from apps/web-preview/scripts/checks/als-check.ts after the Node
|
* Every import goes through the **package name**, not a relative path: a check
|
||||||
* compatibility layer was reorganized into implemented/mock/globals. Every import
|
* that reaches built `lib/` while the shim resolves by package name to `src/`
|
||||||
* goes through the **package name**, not a relative path: a check that reached
|
* gets two module instances and a shim mounted in the wrong world (the failure
|
||||||
* built `lib/` while the shim resolved by package name to `src/` produced two
|
* mode asserted in `../node/fs.spec.ts`). One resolution path per module.
|
||||||
* module instances and a shim mounted in the wrong world (the `fs-check`
|
|
||||||
* incident — "no filesystem is mounted"). One resolution path per module.
|
|
||||||
*/
|
*/
|
||||||
import { expect, test } from 'vitest'
|
import { expect, test } from 'vitest'
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
* (`runShellProcess`) against the REAL host half, so the frames, the
|
* (`runShellProcess`) against the REAL host half, so the frames, the
|
||||||
* filesystem service, and the termination ladder are the shipped ones — only
|
* filesystem service, and the termination ladder are the shipped ones — only
|
||||||
* the thread boundary is simulated, because a Node test host has no DOM
|
* the thread boundary is simulated, because a Node test host has no DOM
|
||||||
* `Worker` to cross. That a browser worker really can start a nested worker
|
* `Worker` to cross. The real browser Worker boundary is not exercised here.
|
||||||
* and terminate it mid-burn is measured separately, in the preview probe.
|
|
||||||
*/
|
*/
|
||||||
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
|
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
|
||||||
import { MemoryVfs } from '@deepseek-ai/dsh-experimental-webworker-runtime/src/storage/memory.ts'
|
import { MemoryVfs } from '@deepseek-ai/dsh-experimental-webworker-runtime/src/storage/memory.ts'
|
||||||
|
|||||||
@@ -1032,8 +1032,8 @@ describe('compat switches', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('refuses a compat key no wire protocol declares instead of dropping it', () => {
|
it('refuses a compat key no wire protocol declares instead of dropping it', () => {
|
||||||
// The silent drop is what let an unreadable switch look applied: schemastery
|
// Schemastery passes unknown keys through, so silently dropping one would
|
||||||
// passes unknown keys through, and resolution used to read only two fields.
|
// make an unreadable switch look applied; the resolver must refuse it.
|
||||||
expect(() => resolveProfiles({
|
expect(() => resolveProfiles({
|
||||||
'acme-gateway': {
|
'acme-gateway': {
|
||||||
api: 'openai-completions',
|
api: 'openai-completions',
|
||||||
|
|||||||
@@ -331,9 +331,9 @@ describe.skipIf(!isWin32 || !pwshAvailable())('windows-acl runner', () => {
|
|||||||
// workspace-write keeps the ACE standing for the server lifetime. After
|
// workspace-write keeps the ACE standing for the server lifetime. After
|
||||||
// switching to read-only, the restricted token's read-only list must carry NO
|
// switching to read-only, the restricted token's read-only list must carry NO
|
||||||
// capability SID — the standing ACE stays but the pass-2 check cannot use
|
// capability SID — the standing ACE stays but the pass-2 check cannot use
|
||||||
// it, so the workspace write is denied (previously it LEAKED). The
|
// it, so the workspace write is denied instead of leaking through the
|
||||||
// switch back reuses the SAME standing ACE: the re-upgrade write lands
|
// standing ACE. The switch back reuses the SAME standing ACE: the
|
||||||
// without any re-grant.
|
// re-upgrade write lands without any re-grant.
|
||||||
const writeSid = workspaceWriteSid(writableDir)
|
const writeSid = workspaceWriteSid(writableDir)
|
||||||
const privateTemp = join(isolatedTemp, 'mode-switch-temp')
|
const privateTemp = join(isolatedTemp, 'mode-switch-temp')
|
||||||
mkdirSync(privateTemp)
|
mkdirSync(privateTemp)
|
||||||
|
|||||||
@@ -152,9 +152,9 @@ describe('SessionProjectionRegistry drive', () => {
|
|||||||
|
|
||||||
first()
|
first()
|
||||||
|
|
||||||
// The regression this counts against: one session ending used to strip
|
// The regression this counts against: without last-release semantics, one
|
||||||
// the projection from every other live session, because the first
|
// session ending strips the projection from every other live session,
|
||||||
// registrant owned the only disposer.
|
// because the first registrant owns the only disposer.
|
||||||
expect(ctx.sessionProjections.snapshot(session).values['test/marks']).toEqual({ marks: ['kept'] })
|
expect(ctx.sessionProjections.snapshot(session).values['test/marks']).toEqual({ marks: ['kept'] })
|
||||||
second()
|
second()
|
||||||
expect(ctx.sessionProjections.snapshot(session).values).toEqual({})
|
expect(ctx.sessionProjections.snapshot(session).values).toEqual({})
|
||||||
|
|||||||
Reference in New Issue
Block a user