mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-11 04:00:38 +00:00
docs(code-runtime-python): drop the placebo dispose test and finish the remaining doc drift
The review showed the added dispose case was a placebo (dispose in the same tick as run means SIGTERM hits the group before the program body runs; the group-emptied arm is already deterministically covered by the same-group survivor case, which this removes the v8 ignore for). The test is deleted; the stale silently-discards comment in the boundary test now says rejects; the README Known Limitations gains the late-log-frame-drop and host-side binding-value-memory entries. Pairing re-recorded.
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# 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/code-runtime/code-runtime-python/README.md
|
||||
README.md: 3d2b7631393a08d1980fc0a3fdf9c89e73077593
|
||||
README.zh.md: f91d712b947dcd6b57651f7744c5f49754190019
|
||||
README.md: 557ff7f58b70fcbc5cd9d7901c0c6b66d123c0eb
|
||||
README.zh.md: d98fdcc52a05541c66e050445ebc04264686451b
|
||||
|
||||
@@ -112,6 +112,8 @@ These limits define what the package does and does not cover; they are current p
|
||||
|
||||
- **The cross-language guard covers the executed surfaces and the frame field shapes, not the field types** — the mirror e2e compares required/optional field sets, not that `cpuSeconds` is an `int` on both sides; a type-level drift is caught by review plus the backend's real-subprocess suite.
|
||||
- **A descendant that escapes the child's process group with `setsid()` is not reaped by the group teardown** — `kill(-pid)` cannot reach it; the run still settles on the value the done frame decided, and the close-deadline backstop forces settlement if the orphan holds the pipes open, but the orphan itself outlives the fiber until it exits on its own.
|
||||
- **A `log` frame that arrives after settlement is dropped** — once the run has settled, host-side capture is closed; a late fd-3 `log` frame (from a thread that outlived the done frame) is discarded rather than appended to `logs`.
|
||||
- **The host-side memory bound for a binding VALUE is the frame parse cap, not a seam budget** — a binding reply's value is rebuilt host-side and billed against `maxValueBytes`; an intermediate binding value has no seam-level byte cap and is bounded by the lossless-JSON serialization cost and process memory (see the binding-argument entry).
|
||||
- **`run()` is one-shot** — `logs` become available only after `CodeRunResult` resolves; there is no streaming-log or progress interface for output produced by a running program.
|
||||
- **No state persists across runs** — every request executes in a fresh subprocess; a persistent REPL-style kernel stays deferred until a backend brings its own logging scheme.
|
||||
- **An fd-3 frame whose raw length exceeds 64 MiB settles the run as a worker-exit** — `maxLogBytes`/`maxValueBytes` are load-bounded to the same parser cap so an honest child's frames always fit; a model-constructed binding ARGUMENT above 64 MiB (a value with no seam-level budget) trips the same cap — an accepted residual of the OOM guard.
|
||||
|
||||
@@ -112,6 +112,8 @@ kind: "package-reference"
|
||||
|
||||
- **跨语言 guard 覆盖执行的表面与帧字段形状,而非字段类型**——mirror e2e 比较必填/可选字段集,而非 `cpuSeconds` 在两侧是否都是 `int`;类型级漂移由评审加后端的真实子进程套件捕获。
|
||||
- **以 `setsid()` 逃出子进程组后代不被组拆卸回收**——`kill(-pid)` 够不到它;运行仍按 done 帧决定的值结算,若该孤儿持有管道,close 截止兜底会强制结算,但孤儿本身在自行退出前一直存活到 fiber 之外。
|
||||
- **结算后到达的 `log` 帧被丢弃**——运行一旦结算,宿主侧捕获即关闭;迟到的 fd-3 `log` 帧(来自比 done 帧存活更久的线程)会被丢弃,而不是追加到 `logs`。
|
||||
- **binding 值的宿主侧内存界是帧解析上限,而非 seam 预算**——binding 回复的值在宿主侧重建并按 `maxValueBytes` 计费;中间 binding 值没有 seam 级字节上限,受无损 JSON 序列化成本与进程内存约束(见 binding 实参条目)。
|
||||
- **`run()` 是一次性的**——`logs` 只有在 `CodeRunResult` resolve 后才能获得;没有为运行中程序产生的输出提供流式日志或进度接口。
|
||||
- **运行之间不保留状态**——每次请求都在全新子进程中执行;持久 REPL 风格内核在某个后端带来自己的日志方案之前保持延期。
|
||||
- **原始长度超过 64 MiB 的 fd-3 帧会让本次运行以 worker-exit 结算**——`maxLogBytes`/`maxValueBytes` 在加载期被限制到同一解析器上限,因此诚实子进程的帧总能放得下;模型构造的超过 64 MiB 的 binding 实参(一个在 seam 层没有预算的值)会触发同一上限——这是该 OOM 防护的已接受残余。
|
||||
|
||||
@@ -110,9 +110,9 @@ describe('PythonCodeRuntime — seam descriptors and misuse', () => {
|
||||
// bytes, so a payload occupies at most `cap + envelope` on the wire; the
|
||||
// bound is `parse-cap - envelope`, not `(ceiling - envelope) / 6` (that
|
||||
// divided in escape expansion the charge already counts). The receive path
|
||||
// drops raw frames past the 64 MiB parse cap before decoding, so a budget
|
||||
// above it would admit a config whose honest child frames the host then
|
||||
// silently discards.
|
||||
// rejects raw frames past the 64 MiB parse cap (the run settles as a
|
||||
// worker-exit), so a budget above it would admit a config whose honest
|
||||
// child frames the host then rejects.
|
||||
const admissible = 64 * 1024 * 1024 - 64
|
||||
const ctx = new Context()
|
||||
await expect(ctx.plugin(PythonCodeRuntime, { maxLogBytes: admissible + 1 }))
|
||||
@@ -2952,30 +2952,6 @@ describe('PythonCodeRuntime — budgets, termination, disposal', () => {
|
||||
expect(still).toBe(true)
|
||||
}, 20_000)
|
||||
|
||||
it('dispose resolves promptly when the kill empties the group and an orphan holds the pipes', async () => {
|
||||
// The group-emptied arm of the reap poll: dispose() drives settle, kill()
|
||||
// kills the still-running child, and a setsid orphan holds the pipes open
|
||||
// so close never fires — the poll must run, see the group empty (the
|
||||
// orphan escaped into its own session), cancel the pending grace SIGKILL,
|
||||
// and finalize immediately. A prompt resolve proves the arm ran
|
||||
// (fail-before: dropping clearTimeout/finalize from that arm leaves dispose
|
||||
// waiting for the never-firing grace escalation and blows the bound).
|
||||
const { runtime, fiber } = await setup({ maxWallMs: 60_000, graceMs: 60_000 })
|
||||
const start = Date.now()
|
||||
const runPromise = runtime.run({
|
||||
program: [
|
||||
'import subprocess, sys, time',
|
||||
'subprocess.Popen([sys.executable, "-c", "import time; time.sleep(30)"],',
|
||||
' start_new_session=True)',
|
||||
'time.sleep(30)',
|
||||
].join('\n'),
|
||||
bindings: [],
|
||||
})
|
||||
await fiber.dispose()
|
||||
const result = await runPromise
|
||||
expect(result.error?.kind).toBe('abort')
|
||||
expect(Date.now() - start).toBeLessThan(5_000)
|
||||
}, 20_000)
|
||||
|
||||
it('dispose awaits reaping of a same-group survivor from a completed run', async () => {
|
||||
// The quiescence contract also holds for a run that ALREADY resolved: the run
|
||||
|
||||
Reference in New Issue
Block a user