Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ Runtime Host owns Session and Turn identity, agent lifecycle, continuation, tool
3. Agent Graph schedules dependent work using child Sessions and sends every activation back through the same Runtime.
4. Storage owns interactive Runtime state. It has no Eval-specific root, TaskRun ledger, or experiment result authority.

## Tool resource admission

Tool execution uses three separate layers. `ToolScheduler` orders conflicting
claims inside one provider batch. A process-owned shared/exclusive coordinator
provides cross-batch correctness: `all()` holds its exclusive side, while each
participating non-empty resource authority holds the shared side. Domain owners
then acquire their own leases (for example filesystem exact/tree read-write
leases) and revalidate identity before the effect.

The fixed acquisition order is process admission, then domain lease. Explicit
`none()` operations bypass both the batch conflict graph and the process
barrier; `all()` does not mean "every asynchronous operation in the process."

## Eval boundary

```text
Expand Down
11 changes: 11 additions & 0 deletions ARCHITECTURE.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ Runtime Host 拥有 Session 和 Turn identity、agent lifecycle、continuation
3. Agent Graph 通过 child Session 调度依赖工作,并把每次 activation 送回同一 Runtime。
4. Storage 只拥有交互 Runtime 状态,不再有 Eval 专用 root、TaskRun ledger 或实验结果 authority。

## 工具资源准入

工具执行分为三个彼此独立的层次。`ToolScheduler` 只在单个 provider batch
内按冲突 claim 排序;进程级 shared/exclusive coordinator 提供跨 batch
correctness:`all()` 获取 exclusive,所有参与建模的非空资源 authority 获取
shared;随后领域 owner 再获取自己的 lease(例如 filesystem exact/tree
读写 lease),并在真实 effect 前重新校验 identity。

固定获取顺序是 process admission,再 domain lease。显式 `none()` 同时绕过
batch 冲突图和 process barrier;`all()` 并不表示“阻塞进程中的一切异步操作”。

## Eval 边界

```text
Expand Down
162 changes: 162 additions & 0 deletions docs/filesystem-read-tree-lease-test-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

# Filesystem Read/Tree Lease 测试报告

日期:2026-09-04

平台:Windows,Node.js v22.23.2

工作区:`D:\harness learning\maka-agent`

## 1. 结论

Filesystem Read/Tree Lease 的实现和定向回归通过。最终扩展矩阵为 **62 pass、0 fail、0 skip**。

本轮补齐了以下关键证据:

- 独立 `settleToolCallBatch()` 之间的 Read/Edit 与 Grep/Write 冲突;
- 不依赖 Scheduler 的 prepared Read/direct Edit owner 互斥;
- structured single-operation patch 与 Read/Grep 的直接 overlap;
- root/child tool composition 共享同一个 filesystem coordinator;
- multi-key 原子准入、writer fairness、abort/reject release;
- Windows junction 与 POSIX symlink 的 canonical alias、越界和“不跟随删除”语义;
- unknown tool fallback、`all`/`none` 真值表和 provider-order 结果槽稳定性。

原先的 State Root ownership namespace 权限阻塞在 unrestricted 环境下不再出现。Runtime Host 全量测试仍不能宣称全绿,但剩余失败已确认是独立的 Windows SQLite teardown、默认测试并发资源压力和 real-model terminal timeout,不是本次 filesystem lease 行为失败。

## 2. 实现范围

实现包含:

- process-owned `FilesystemLeaseCoordinator`;
- exact/tree read-write 冲突判断;
- writer-fair、abort-aware 等待队列;
- `acquireMany` 风格的 multi-key 原子准入,禁止部分持有;
- canonical、Windows case-folded lease key;
- Read/Write/Edit/Grep/Glob/apply_patch 的 owner-level lease;
- freeform multi-file patch 的整组 lease interval;
- Runtime Host root/child composition 共享 coordinator;
- patch unknown-outcome 与现有 authority contract 对齐。

## 3. 测试矩阵

| 类别 | 主要场景 | 结果 |
| --- | --- | --- |
| Lease key | POSIX canonical、Windows case fold、`src`/`src2` separator boundary | 通过 |
| Coordinator | exact/tree RW、并发 read、独立路径 fan-out、writer fairness | 通过 |
| Abort/release | queued abort、pre-abort、active abort、effect reject 后释放 | 通过 |
| Multi-key admission | reversed keys、dedupe、全量原子准入、禁止 partial admission | 通过 |
| 独立 batch | Read/Edit、Grep tree/child Write | 通过 |
| Owner correctness | prepared/direct 路径绕过 Scheduler 后仍互斥 | 通过 |
| Patch | structured patch overlap、multi-file interval、exact-write claims | 通过 |
| Root/child | 两个 composition 共享 coordinator | 通过 |
| Alias | prepared junction/symlink canonical lease identity | 通过 |
| Boundary | junction/symlink 越界拒绝、bypass 可访问 | 通过 |
| Delete link | 删除 reparse/link entry,不跟随删除目标 | 通过 |
| Kimi semantics | unknown→`all`、`all`/`none`、fairness、provider order | 通过 |

## 4. 执行结果

### 4.1 类型检查与构建

以下命令通过:

```text
npm --workspace @maka/runtime run typecheck
npm --workspace @maka/runtime-host run typecheck
npm --workspace @maka/runtime run build
npm --workspace @maka/runtime-host run build
```

### 4.2 最终扩展矩阵

运行:

```text
node --test \
packages/runtime/dist/__tests__/filesystem-apply-patch.test.js \
packages/runtime/dist/__tests__/filesystem-authority-contract.test.js \
packages/runtime/dist/__tests__/filesystem-authority-leases.test.js \
packages/runtime/dist/__tests__/filesystem-authority.test.js \
packages/runtime/dist/__tests__/filesystem-lease-coordinator.test.js \
packages/runtime/dist/__tests__/filesystem-lease-key.test.js \
packages/runtime/dist/__tests__/filesystem-tool-call-batch-scenarios.test.js \
packages/runtime/dist/__tests__/tool-authority-kimi-semantics-batch.test.js \
packages/runtime-host/dist/__tests__/filesystem-lease-composition.test.js
```

结果:

```text
tests 62
pass 62
fail 0
skipped 0
```

### 4.3 Windows link 定向矩阵

Windows 当前进程令牌没有 `SeCreateSymbolicLinkPrivilege`。测试采用平台等价策略:Windows 使用无需提权的 directory junction,POSIX 保留 symlink。该策略实际验证 reparse entry 的 canonicalization、越界拒绝和不跟随删除,而不是简单跳过。

结果:

```text
tests 23
pass 23
fail 0
skipped 0
```

### 4.4 静态质量检查

以下检查通过:

```text
biome format
biome lint
git diff --check
```

## 5. Runtime Host 全量测试记录

使用 unrestricted filesystem 权限后,Host 测试不再出现 `StorageRootAuthorityError` 或 State Root ownership namespace 解析失败。

全量测试进行了两种运行:

1. 默认 Node 文件并发:多个 Host/child 进程出现 JavaScript heap OOM,随后残留句柄导致测试不退出。
2. `--test-concurrency=1` 串行:消除了 OOM,但在 Windows SQLite 临时库清理阶段稳定出现 `EBUSY`,并在后续大型 composition 文件中出现长时间不退出。

独立复现结果:

- `canonical-session-projection.test.js`:8 个用例均在清理 `runtime.sqlite`、`runtime.sqlite-wal` 或 `runtime.sqlite-shm` 时因 `EBUSY` 失败;
- `execution-model-composition.test.js`:存在既有 real-model Turn terminal timeout、SQLite `EBUSY`,随后测试进程不退出;
- 本次新增的 `filesystem-lease-composition.test.js` 通过;
- Host production composition、State Root startup、root/child filesystem coordinator 路径均可运行。

因此,本报告不将 Runtime Host 全量 suite 标记为全绿。剩余问题应作为独立的 Windows SQLite close/cleanup、测试并发上限及 real-model timeout 工作处理。

## 6. Review disposition

测试已经证明 filesystem correctness 不依赖单一 batch Scheduler。仍需在 PR 文案中明确:

- unknown real tool 默认 `all` 是安全优先的吞吐回退;
- provider order 只用于冲突调度和结果槽稳定性,不表达数据依赖;
- subagent fan-out 继续由既有 capacity limiter 控制,capacity 尚未统一表达为本 authority contract;
- global `all`、dynamic MCP policy 和 Bash workspace-scoped coarse authority 不属于本次 filesystem lease 实现范围。
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import { type BackendFactoryContext } from '@maka/runtime/session-manager';
import { type AiSdkBackendInput, type RunTraceEvent } from '@maka/runtime/ai-sdk-backend';
import { type FilesystemWorkerExecuteInput } from '@maka/runtime/filesystem-worker';
import { type MakaTool, type MakaToolContext } from '@maka/runtime/tool-runtime';
import { ToolAuthorityRegistry, ToolPreparationService } from '@maka/runtime/tool-preparation';
import {
type ProxiedFetchProxy,
type ProxiedFetchTransport,
Expand Down Expand Up @@ -147,6 +148,33 @@ const HEADLESS_CODING_V1_PROMPT_HASH =
const HEADLESS_CODING_V1_TOOLS_HASH =
'sha256:aa3ab56a7b67dde133fffe885f4def81735c93015202e31ecb339a84863f6d03';
const execFileAsync = promisify(execFile);

test('injects one caller-owned preparation service into every backend', async () => {
const preparationService = new ToolPreparationService(new ToolAuthorityRegistry());
const createBackend = () =>
createHostAiSdkBackend(
backendCreationFixture({
abortSignal: new AbortController().signal,
resolveExecutionConnection: async () => readyExecutionConnection(),
readPricing: async () => ({ revision: 0, overrides: [] }),
preparationService,
}),
);
const [first, second] = await Promise.all([createBackend(), createBackend()]);
try {
assert.equal(
(first as unknown as { preparationService: ToolPreparationService }).preparationService,
preparationService,
);
assert.equal(
(second as unknown as { preparationService: ToolPreparationService }).preparationService,
preparationService,
);
} finally {
await Promise.all([first.dispose(), second.dispose()]);
}
});

test('backend creation resolves a bound Session by immutable Connection identity', async () => {
let observedRef: unknown;
await createHostAiSdkBackend(
Expand Down Expand Up @@ -3962,6 +3990,7 @@ function backendCreationFixture(input: {
createFetchTransport?: HostAiSdkBackendInput['createFetchTransport'];
createRunComposer?: HostAiSdkBackendInput['createRunComposer'];
artifacts?: HostAiSdkBackendInput['artifacts'];
preparationService?: HostAiSdkBackendInput['preparationService'];
}): HostAiSdkBackendInput {
const runtimePolicy =
input.runtimePolicy ??
Expand Down Expand Up @@ -4035,6 +4064,8 @@ function backendCreationFixture(input: {
runtimePolicy,
...(input.oauthCredentials ? { oauthCredentials: input.oauthCredentials } : {}),
createRunComposer,
preparationService:
input.preparationService ?? new ToolPreparationService(new ToolAuthorityRegistry()),
artifacts: input.artifacts ?? {},
executionArtifacts: {
recordToolArtifacts: async () => undefined,
Expand Down
Loading