docs: correct fs/observed concurrency-safety wording
The read tool's isConcurrencySafe rationale called the fs/observed recorder "commutative" and said concurrent reads "converge to one observed version", overstating the guarantee: the WeakMap record is last-writer-wins. Safety comes from write/edit re-checking the version in their in-lock CAS (a stale observation only forces a later edit to fail closed with FS_STALE_VERSION), as the RFC already states. Align the read comment, the ToolDefinition JSDoc, both READMEs, and the regenerated catalogs.
This commit is contained in:
@@ -46,6 +46,6 @@ The tool passes `exec` (the tool-execution context) as the opaque `actor` on eve
|
||||
|
||||
`fs/observed` fires AFTER the read/write/edit already succeeded, via a plain `ctx.emit`. A listener is contractually a synchronous, side-effect-only recorder (`@deepseek-ai/dsh-fs-policy`'s is a `WeakMap.set`); the tool does not guard the emit, so a listener that throws would surface as the tool's `isError` result — async or fallible observation does not belong on this event.
|
||||
|
||||
This is exactly why `read` declares `isConcurrencySafe: () => true` while `write`/`edit` do not: `read`'s only side effect is that synchronous commutative recorder (same-target concurrent reads converge to one observed version), so the agent loop may run sibling reads in parallel. `write`/`edit` mutate the filesystem and stay exclusive barriers — the provider re-checks the observed version inside its per-target lock before mutating, so a stale read never corrupts (it only forces a re-read). See the [parallel tool-call RFC](../../../docs/rfc/implemented/feature/2026-07-10-parallel-tool-call-execution.md).
|
||||
This is exactly why `read` declares `isConcurrencySafe: () => true` while `write`/`edit` do not: `read`'s only side effect is that synchronous version recorder (same-target concurrent reads race last-writer-wins on the observed version), so the agent loop may run sibling reads in parallel. `write`/`edit` mutate the filesystem and stay exclusive barriers — the provider re-checks the observed version inside its per-target lock before mutating, so a stale read never corrupts (it only forces a re-read via `FS_STALE_VERSION`). See the [parallel tool-call RFC](../../../docs/rfc/implemented/feature/2026-07-10-parallel-tool-call-execution.md).
|
||||
|
||||
The read rendering (line windowing + output formatting) lives in `src/read-render.ts` (Cordis-free, independently unit-tested); `src/read.ts`/`write.ts`/`edit.ts` are the tool executors and `src/index.ts` composes them.
|
||||
|
||||
@@ -92,10 +92,12 @@ export function applyReadTool(ctx: Context, caps: ReadToolCaps): void {
|
||||
offset: { type: 'number', description: '1-based first line to return. Defaults to 1.' },
|
||||
limit: { type: 'number', description: `Maximum number of lines to return. Defaults to ${caps.limit}.` },
|
||||
},
|
||||
// Read-only. Its one side effect is the synchronous, commutative `fs/observed`
|
||||
// version recorder (a WeakMap write; see below and the fs-policy plugin), so
|
||||
// concurrent same-target reads converge to one observed version. write/edit
|
||||
// stay exclusive barriers and re-check versions in-lock before mutating.
|
||||
// Read-only. Its one side effect is the synchronous `fs/observed` version
|
||||
// recorder (a WeakMap write; see below and the fs-policy plugin): concurrent
|
||||
// same-target reads race last-writer-wins on that record, which is safe because
|
||||
// it is NOT the safety boundary — write/edit stay exclusive barriers and
|
||||
// re-check the version in-lock, so a stale observation only makes a later edit
|
||||
// fail closed with FS_STALE_VERSION.
|
||||
isConcurrencySafe: () => true,
|
||||
async execute(args, exec): Promise<ContentBlock[]> {
|
||||
const input = parseReadArgs(args, caps.limit)
|
||||
|
||||
Reference in New Issue
Block a user