Skip to content

fix(file): make write_file create-only reachable by omission - #569

Closed
tietoumiao wants to merge 1 commit into
T-Auto:mainfrom
tietoumiao:fix/419-write-create-only
Closed

tietoumiao wants to merge 1 commit into
T-Auto:mainfrom
tietoumiao:fix/419-write-create-only

Conversation

@tietoumiao

@tietoumiao tietoumiao commented Nov 1, 2026 •

Copy link
Copy Markdown

Closes #419

What was wrong

ops_write_file's create-only path was only reachable if the model literally emitted
expectedRevision: "absent". src/capabilities/file.ts:61 kept expectedRevision required for all
five mutation tools, the descriptions at :14/:132 did not name the literal for the create case,
src/capabilities/arguments.ts did not normalise an omitted value, and the worker has no serde
default. A model that writes {path, content} — the natural create call — got an argument error
instead of a created file.

The change

  • src/capabilities/file.ts — write_file requires only path and content; both descriptions
    name the "absent" sentinel.
  • src/capabilities/arguments.ts — a create-only map: an omitted expectedRevision is
    normalised to "absent" before the request reaches the worker, so the wire contract still carries
    an explicit CAS expectation. A supplied "" / blank / null stays on the reject path, and
    edit/copy/move/remove still require an observed revision.
  • Tests: test/file-args.test.mjs, test/file-sdk-render.test.mjs, test/capabilities.test.mjs,
    test/file-operations.test.mjs (the raw worker must keep refusing the omission), and a
    real-worker host-lane check in test/permissions.test.mjs.
  • docs/troubleshooting.md + docs/常见问题.md + docs/CHANGELOG.md.

Deliberately not done: no Rust change and no worker rebuild — the baseline worker already accepts
the injected wire value, which the end-to-end probe below proves.

Validation

$ node test/run.mjs file-args file-sdk-render file-contract capabilities docs-facts manifest mount worker-boundary
dsh-ops test: 12 suite(s) passed in 16s
  (file-args 10/10 · file-sdk-render 14/14 · capabilities 6/6 · mount 51/51 · worker-boundary 6/6)

$ node test/run.mjs file-operations          # writable-temp host setup, real worker
dsh-ops file-operations: all 15 checks passed

# revert-red A: src/capabilities/file.ts only
FAIL write_file alone makes the CAS baseline optional…            (file-sdk-render 1 of 14)
# revert-red B: src/capabilities/arguments.ts only
FAIL normaliser loses expectedRevision: 'absent'                  (file-args 2 of 10)
     captured wire request: {"path":…,"content":"hello\n"}   (+ undefined / - 'absent')
  • 撤掉本次实现后变红的检查:上面两组
  • docs/CHANGELOG.md ## [Unreleased]:已加(EN+ZH)
  • 面向模型的描述 / 参数:expectedRevision 从 write_file 的 required 集合移出,并在描述中点名
    "absent";schema 字节变化见 file-sdk-render 的 digest 更新(同 PR 内已同步)
  • CI:待维护者批准后由 pull_request 事件触发,届时回填 CI required 的 run URL

Environment

  • OS / 架构:Windows 11 x64
  • Node:24.16.0
  • 基于上游 main 的 commit:065cd7f
  • 安装通道:源码 tarball + npm ci --ignore-scripts

未验证 / 风险披露

  • 本机受限令牌下 worker 子进程写 %TEMP% 会被拒,permissions 的该检查因此在默认口径下红;
    用带用户授权 + Low 标签的工作区临时根时 file-operations 15/15、permissions 的该检查绿。
    这是本机环境,不是 CI 行为。
  • 三处 test/*.mjs 的 pinned digest 随之更新(file-sdk-render、capabilities),请重点核对。

CI

$ gh run view 41338725339 --json url,event,conclusion --jq '"(.url) (.event) (.conclusion)"'
https://github.com/T-Auto/dsh-ops/actions/runs/41338725339 pull_request action_required

expectedRevision leaves the write_file required set, and the argument bridge
now sends the create sentinel when a call omits it, so creating a file no
longer depends on the caller knowing an undocumented literal. The field and
tool descriptions name "absent" and state that omission means create-only.
An explicitly supplied blank or null stays on the reject path, and the other
four mutations still require an observed revision, so the CAS contract is not
relaxed (T-Auto#419, T-Auto#422).
@Haoming9527

Copy link
Copy Markdown
Collaborator

There is an important contract choice to reconcile with #541 before combining these changes. #541 addresses #419 by documenting the required literal "absent" while preserving the current schema; this PR instead makes omission mean create-only in the shared host bridge. Both are within the alternatives raised by the issue, but they are alternative public contracts, not additive fixes: retaining both descriptions/tests would contradict itself.

At head 9168ba8, the normalization shown here preserves an explicit CAS expectation on the worker wire, and supplied blank/null values are not converted into an overwrite. Please retain those controls if this API is selected. The review should also record the actual before/after descriptor and generated-SDK byte counts, rather than treating changed digest values as a byte measurement. No approval or performance claim is inferred from the current action_required run.

This review leaves the contributor branch intact; #541 remains the integration branch's current contract choice until an explicit reconciliation is made.

@Nicholas023 Nicholas023 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查结论:REQUEST_CHANGES — 建议关闭

1. 与 #541 是互斥的公开契约(阻断)

  • 本 PR:expectedRevision 移出 required + host bridge 归一化(省略即 create)
  • #541:保留 required,用文档写明字面量 "absent"

二者改同一文件同一批行,语义相反,不能同时合并。#541 的测试断言了 required.includes('expectedRevision') 与 /literal string "absent"/,并入本 PR 的 schema 后必红。

2. PR body 中有三处可核实的事实错误

(a) 「12 suite(s) passed in 16s」
用同样 9 个 filter 实测只命中 8 个套件,输出 8 suite(s) passed in 8s(dsh-ops 这个 filter 匹配 0 个套件)。

(b) 「三处 pinned digest 随之更新(file-sdk-render、capabilities)」
实际 64-hex 断言只变了 2 处,且都在 test/capabilities.test.mjs;test/file-sdk-render.test.mjs 根本没有 digest。

(c) 用 digest 更新充当「字节测量」
digest 是断言值,不是测量。我实测的真实影响:write_file schema 渲染 +56 字节、工具描述 +81 字节、字段描述 +55 字节、18 个能力的 PTC 声明 113501→113557。

已核实为真的部分

  • 0.5.0 基线的 PARAMETERS.write_file.required 确实是 ["path","content","expectedRevision"](问题真实存在)
  • 归一化只作用于 write_file(CREATE_ONLY 仅一项;edit_file 走 existingRevision)—— 这点做得干净
  • file-args 10/10、file-sdk-render 14/14、capabilities 6/6
  • revert-red → capabilities 2/6、file-args 2/10、file-sdk-render 1/14 红

处置建议

关闭,采纳 #541 的文档方案。若维护者偏好 omission 契约,则必须同时回滚 #541 的 required / docs / tests,并修正上述 body 数字。

@T-Auto T-Auto closed this Nov 2, 2026
@tietoumiao

Copy link
Copy Markdown
Author

Closing — #541 landed the contract in main (#557): expectedRevision stays required and "absent" is documented as the create-only literal. That is the opposite of the omission contract proposed here, and the review's three factual corrections about this PR body (suites counted, digest count, digest != byte measurement) are accepted as written.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants