You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up from the CodeRabbit review of #1408 (S4b, epic #1375 file-safety series). The review finding (comment on src/core/tools/ReadFileTool.ts:238) points out that read completeness is not tracked:
processTextFile() is slice-based by default: even a read_file without explicit offset/limit returns at most DEFAULT_LINE_LIMIT lines (truncated, with a warning), and indentation mode returns only a semantic block.
The observation registered after such a read is file-level only — FileObservation = { version, observedAt } (src/core/task/observationRegistry.ts), with the version token derived from on-disk fs.stat.
So fs.readFile can complete successfully, the version tokens can match, and a later write_to_file full-file replacement still passes the guardedWrite()update path — even though the model only ever saw a slice of the file.
Registration records the scope (ReadFileTool): a read that delivered the whole file (or an explicit full-file read) marks the observation complete; slice/indentation/truncated reads mark it partial with the delivered range.
guardedWrite() requires a complete observation for the update kind (full-file replacement): a partial observation rejects the update with the standard stale/guard rejection (re-read remediation), while the edit kind keeps working on any prior observation (optionally, a later step could also scope edit to the observed range).
Re-reading the file extends/refreshes the observation as today; a complete read supersedes a partial one.
Acceptance criteria
Regression test: file read through a truncated slice → write_to_file full replacement → rejected with the standard guard error; after a complete read → allowed.
Regression test: edit kind still permitted after a slice read (no behavior change for scoped edits in this step).
FileObservation scope fields round-trip through the registry semantics (re-observe replaces the entry).
Scope
Follow-up from the CodeRabbit review of #1408 (S4b, epic #1375 file-safety series). The review finding (comment on
src/core/tools/ReadFileTool.ts:238) points out that read completeness is not tracked:processTextFile()is slice-based by default: even aread_filewithout explicitoffset/limitreturns at mostDEFAULT_LINE_LIMITlines (truncated, with a warning), andindentationmode returns only a semantic block.FileObservation = { version, observedAt }(src/core/task/observationRegistry.ts), with the version token derived from on-diskfs.stat.fs.readFilecan complete successfully, the version tokens can match, and a laterwrite_to_filefull-file replacement still passes theguardedWrite()updatepath — even though the model only ever saw a slice of the file.Proposed behavior
FileObservationwith read scope (S2 registry, feat(task): per-task file observation registry (A2, #1375) Zoo-Code-Org/Zoo-Code#1394): e.g.complete: booleanplus the observed line range / total line count (fromprocessTextFile's result:returnedLines,totalLines,includedRanges).guardedWrite()requires a complete observation for theupdatekind (full-file replacement): a partial observation rejects the update with the standard stale/guard rejection (re-read remediation), while theeditkind keeps working on any prior observation (optionally, a later step could also scopeeditto the observed range).Acceptance criteria
write_to_filefull replacement → rejected with the standard guard error; after a complete read → allowed.editkind still permitted after a slice read (no behavior change for scoped edits in this step).FileObservationscope fields round-trip through the registry semantics (re-observe replaces the entry).References
ReadFileTool.ts:238.guardedWrite()core; feat(tools): wire guarded writes into the diff-view save paths (S4b, #1375) Zoo-Code-Org/Zoo-Code#1408 (S4b) — tool wiring.