Repository navigation
feat(editor): route the diff-view save through the guard (U8, #1375) #1916
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
easonLiangWorldedtech
wants to merge
111
commits into
Zoo-Code-Org:main
Choose a base branch
from
easonLiangWorldedtech:fws/u8-diffview-guarded-save
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+13,862
−338
Open
Changes from all commits
Commits
Show all changes
111 commits
Select commit
Hold shift + click to select a range
7505c8e
split unit U1 of PR 1833 (issue 1375)
1b07409
fix(file-safety): close the pre-merge findings on the publish primiti…
8735c49
fix(file-safety): propagate a non-ENOENT lstat failure in resolvePubl…
1fca597
fix(file-safety): keep the rollback pair typed and the mock stand-ins…
4e74e82
rebuild unit u2 on the fixed chain
815b5e0
chore(lint): prune the safeWriteJson suppression this unit earns
ffa10b1
rebuild unit u3 on the fixed chain
4653b34
fix(task): declare the observation registry on Task in this unit
dfac2c3
rebuild unit u4 on the fixed chain
fed1242
rebuild unit u5 on the fixed chain
27d98af
rebuild unit U8 on U5 (corrected merge order)
44dc7e1
chore(ci): rerun the e2e-mock lane
91bd6c7
fix(file-safety): inherit unit 1 committed guard
0a7fcff
test: rerun mocked e2e - no source change, previous run failed in the…
716b5d9
fix(file-safety): keep the publish error message in RollbackFailureError
c478fdc
test(file-safety): assert the exact staging directory removed after a…
02ce2e6
test: re-trigger required checks - the queued runs were cancelled by …
0aaf125
test: re-run mocked e2e and the ubuntu lane - no source change since …
930c88d
test: re-run mocked e2e - the suite passed on the previous head of th…
f6b3e3d
fix(file-safety): give the Windows DACL dump a per-write name
f4e03eb
test: third mocked-e2e attempt on this unit - the suite is green on t…
a5eaa7a
chore(ci): re-run mocked E2E (apply_diff timeouts on the previous run…
eb93e30
chore(ci): second re-run of the mocked E2E suite. The apply_diff fixt…
1140140
fix(tools): apply_diff must publish with edit-guard semantics, not cr…
0d3697d
fix(file-safety): durable-copy publish and confined writes for the di…
bb15ea9
fix(file-safety): do not read a failed target lstat as a missing target
8a86a91
fix(file-safety): compare staging and target identity with bigint stats
732bfff
test(file-safety): pin the bigint options in the staging-identity tests
ebfc7fb
fix(file-safety): report a Windows replacement whose DACL was not pre…
5b8fa7b
fix(diff-view): undo a partial open() instead of leaving an unapprove…
d87e1d4
fix(file-safety): route the DACL restore failure through onWarning too
90e4b22
fix(file-safety): keep DACL warning delivery from failing the save
6f69ff6
fix(utils): check confinement before taking the advisory lock
0028a1b
fix(integrations): the preview observation must not authorize an appr…
da01ba8
fix(file-safety): handle async warning sinks and confine before mkdir
1c6e5d3
fix(tools): ApplyDiffTool must observe the version its diff was built on
51137bf
test(tools): type the apply_diff observation doubles for check-types
26be948
fix(integrations): never adopt an autosaved match for an unauthorized…
2316165
fix(tools): never refresh a model observation the tool read on a diff…
c508da8
fix(editor): attribute an adopted publish to the version the preview saw
0f31b2d
fix(u8): track placeholder ownership apart from the observation token
c868022
fix(file-safety): one lock key whether the parent directory exists yet
abf03c5
test(file-safety): a backup copy that fails part-way must not survive…
9d2be1e
fix(diff-view): put the save's post-publish cleanup inside the same t…
3a3473e
fix(diff-view): let only the teardown that started a pass finalize th…
c075439
test(file-safety): pin that an uncanonicalizable confined scope stops…
56fb9cc
fix(diff-view): restore preview tabs for the pass that owns a rejecte…
8a29285
test(apply-diff): cover the two bracketing stats that can fail on the…
a8df293
fix(read-file): stop indenting the read content inside the notice tem…
d1ca165
test(safe-write): a warning sink that throws must not fail the commit…
d564f61
fix(diff-view): finalize the session when a cancellation lands during…
db5ef0a
test(diff-view): put the rejected-publish comment back above the test…
fdc1ace
fix(file-safety): never abandon the backup seed descriptor when its c…
34a88e4
fix(diff-view): one caller owns the session's finalization, by claim …
bc8aeee
fix(safe-write): a write that declares no scope replaces the link ins…
facbc4c
fix(diff-view): the rejected-save pass owns the session through previ…
f34215c
fix(safe-write): an existing Windows target is not published over und…
7b1a20d
fix(test): teach main's unicode-reader spec about hasClippedLines
0b12594
fix(safe-write): every descriptor this call owns is released through …
e0fbde1
fix(safe-write): a failed Windows ACL step never publishes content, a…
95480b7
fix(safe-write-json): an unscoped write locks and reads the file it a…
dc4a78a
fix(safe-write): match a verified ACE against the whole principal, no…
0942fd1
fix(diffview): put the finalization guard on the public reset(), not …
87ac7b1
fix(safe-write-json): keep the referent lock beside the link-path loc…
51257de
style: format the ten files the formatting gate flags on this PR
516496a
test: scope the icacls assertion to the platform that issues the command
fc6ef60
fix: fold a lock key from the deepest existing ancestor, not the way …
625d412
fix(safe-write-json): take the lock on the canonical spelling of the …
56c45d6
style: reflow the lock-key spec to the repo's prettier output
d2ce125
fix(u8): refuse adoption when the rejection was about completeness, a…
05a5e1d
fix(editor): reconcile the observation when a guarded publish lands w…
413fa01
fix(utils): decide confinement once and reject a declared-empty root
81d16f3
fix: order cancellation against the guarded publish and session restart
easonliang28 2ba2936
test(diffview): keep the open() serialization test inside the lint su…
easonliang28 716bff8
fix(diffview): gate the rollback against the rejected save's full lif…
easonLiangWorldedtech 0a76af3
fix(u8): align the unit with main's lint re-enable and format gate
easonLiangWorldedtech 5a04426
fix(integrations,file-safety): clear the re-review threads on typed t…
easonLiangWorldedtech 9980ac7
chore: retrigger CI (windows timing flake in main's TaskHistoryStore.…
easonLiangWorldedtech 3624e4f
fix(file-safety): refuse a captured-DACL publish over a symbolic link…
easonLiangWorldedtech f23e49f
test(utils): follow the DACL-over-link refusal in the lock-key integr…
easonLiangWorldedtech c6b3e86
test(task): prove per-Task observation registry ownership with real T…
easonLiangWorldedtech 82cfbf2
fix(editor): withdraw the preview observation on every exit path (port)
easonLiangWorldedtech 5622f24
fix(editor): narrow the preview task before the withdrawal forget
easonLiangWorldedtech a00c5b7
fix(editor): withdraw the preview entry by ownership, not content (port)
easonLiangWorldedtech e76c44a
fix(guarded-write): bind the publish to a fixed target and lock the r…
easonLiangWorldedtech 3ea7348
test(tools): pass toolCallId in guarded-write spec callbacks
easonLiangWorldedtech 1c34cec
fix(diffview): surface the publish token so the rollback baseline is …
easonLiangWorldedtech 4e4cf7f
fix(diff-view): roll back through a compare-and-swap bound to the che…
easonLiangWorldedtech 096abc2
fix(diff-view): bind every rollback publish to a version pair
easonLiangWorldedtech bf09481
fix(guarded-write): bind the allowlist decision to the retained publi…
easonLiangWorldedtech 1d77916
fix(diffview): encode the CAS rollback with the document codec
950752e
fix(diffview): discard the create buffer before the delete, reload in…
98ba389
chore(guarded-write): mark the test-only chain reset internal
24b6797
fix(file-safety): never clean up a staging name the commit already co…
c055251
fix(guarded-write): canonicalize the workspace root for containment; …
5cd9420
Merge upstream/main into fws/u8-diffview-guarded-save
574f794
fix(merge): restore the upstream hunks dropped by the main merge
easonLiangWorldedtech e77d20e
test(apply-diff): stub the stat-observation calls in the failure-repo…
easonLiangWorldedtech 72f0d89
Merge remote-tracking branch 'upstream/main' into fws/u8-diffview-gua…
easonLiangWorldedtech 66c320f
fix(file-safety): keep the claim, the publish target and the staged f…
easonLiangWorldedtech 676dea9
fix(fws-u8): own-tab path equality, link-aware backup, identity-owned…
easonliang28 04d6a6d
fix(fws-u8): publish the target safeWriteJson verified, and prove the…
easonliang28 9f5bf59
fix(fws-u8): canonicalize an absent publish target through its deepes…
easonliang28 48bb73e
fix(fws-u8): decide the adoption refusals before the baseline branche…
easonliang28 f95c7b4
fix(file-safety,tools,diff-view): close the three follow-up rows on #…
easonliang28 e36a779
fix(tools,diff-view): decide adoption from the owned placeholder, and…
easonliang28 4a6d0f2
fix(diff-view,tools): give a direct save the observation its publish …
easonliang28 61b31b1
fix(diff-view): reserve the session transition before open() first aw…
easonliang28 caabad3
fix(diff-view,tools,fs): scope the direct-save basis to full-file writes
easonliang28 2e00da1
fix(diff-view,tools): recognise the session's own Auto Save, gate re-…
easonliang28 680bdc2
Merge upstream main (73931fe8b) into fws/u8-diffview-guarded-save
easonLiangWorldedtech File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| import { type ProviderSettings } from "@roo-code/types" | ||
|
|
||
| import { Task } from "../Task" | ||
| import { ObservationRegistry } from "../observationRegistry" | ||
| import type { ClineProvider } from "../../webview/ClineProvider" | ||
| import { providerIdentifiers } from "@roo-code/types/provider-identifiers" | ||
|
|
||
| // Real-Task isolation proof for the observationRegistry field this PR adds to | ||
| // Task. The unit spec covers the registry's own semantics; this one pins the | ||
| // ownership contract the field exists for: every Task owns its registry, so a | ||
| // parent's observations never authorize a subtask's writes (and vice versa). | ||
| // The construction boilerplate mirrors Task.dispose.test.ts. | ||
| vi.mock("../../webview/ClineProvider") | ||
| vi.mock("../../../integrations/terminal/TerminalRegistry", () => ({ | ||
| TerminalRegistry: { | ||
| releaseTerminalsForTask: vi.fn(), | ||
| }, | ||
| })) | ||
| vi.mock("../../../utils/storage", () => ({ | ||
| getTaskDirectoryPath: vi.fn().mockResolvedValue("/test/path/tasks/test-task"), | ||
| })) | ||
| vi.mock("../../../integrations/terminal/OutputInterceptor", () => ({ | ||
| OutputInterceptor: { | ||
| cleanup: vi.fn().mockResolvedValue(undefined), | ||
| }, | ||
| })) | ||
| vi.mock("../../ignore/RooIgnoreController") | ||
| vi.mock("../../protect/RooProtectedController") | ||
| vi.mock("../../context-tracking/FileContextTracker") | ||
| vi.mock("../../../integrations/editor/DiffViewProvider") | ||
| vi.mock("../../tools/ToolRepetitionDetector") | ||
| vi.mock("../../../api", () => ({ | ||
| buildApiHandler: vi.fn(() => ({ | ||
| getModel: () => ({ info: {}, id: "test-model" }), | ||
| })), | ||
| })) | ||
| vi.mock("@roo-code/telemetry", () => ({ | ||
| TelemetryService: { | ||
| instance: { | ||
| captureTaskCreated: vi.fn(), | ||
| captureTaskRestarted: vi.fn(), | ||
| }, | ||
| }, | ||
| })) | ||
|
|
||
| describe("Task observation registry ownership", () => { | ||
| const makeProvider = () => ({ | ||
| context: { globalStorageUri: { fsPath: "/test/path" } }, | ||
| getState: vi.fn().mockResolvedValue({ mode: "code" }), | ||
| log: vi.fn(), | ||
| flushPostStateToWebviewThrottled: vi.fn().mockResolvedValue(undefined), | ||
| }) | ||
| const apiConfiguration = { | ||
| apiProvider: providerIdentifiers.anthropic, | ||
| apiKey: "test-key", | ||
| } as ProviderSettings | ||
|
|
||
| it("gives every real Task its own registry, isolated from every other", async () => { | ||
| const taskA = new Task({ | ||
| provider: makeProvider() as unknown as ClineProvider, | ||
| apiConfiguration, | ||
| startTask: false, | ||
| }) | ||
| const taskB = new Task({ | ||
| provider: makeProvider() as unknown as ClineProvider, | ||
| apiConfiguration, | ||
| startTask: false, | ||
| }) | ||
|
|
||
| try { | ||
| expect(taskA.observationRegistry).toBeInstanceOf(ObservationRegistry) | ||
| expect(taskB.observationRegistry).toBeInstanceOf(ObservationRegistry) | ||
| // Two real Tasks, two registries: a shared or static field fails here. | ||
| expect(taskA.observationRegistry).not.toBe(taskB.observationRegistry) | ||
|
|
||
| taskA.observationRegistry.observe("/tmp/observed-by-a.json", "version-a") | ||
|
|
||
| expect(taskA.observationRegistry.get("/tmp/observed-by-a.json")?.version).toBe("version-a") | ||
| // The other task's registry must not contain the observation: an | ||
| // observation earned by one task cannot authorize the other's writes. | ||
| expect(taskB.observationRegistry.get("/tmp/observed-by-a.json")).toBeUndefined() | ||
| expect(taskB.observationRegistry.has("/tmp/observed-by-a.json")).toBe(false) | ||
| expect(taskB.observationRegistry.size).toBe(0) | ||
| } finally { | ||
| await taskA.dispose().catch(() => {}) | ||
| await taskB.dispose().catch(() => {}) | ||
| } | ||
| }) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| import { describe, it, expect, vi } from "vitest" | ||
|
|
||
| import { ObservationRegistry } from "../observationRegistry" | ||
|
|
||
| describe("ObservationRegistry", () => { | ||
| it("observe → get returns the recorded version and observedAt", () => { | ||
| const reg = new ObservationRegistry() | ||
| reg.observe("/a/b/c.ts", "1:2:300:4000000000:5000000000") | ||
|
|
||
| const obs = reg.get("/a/b/c.ts") | ||
| expect(obs).toBeDefined() | ||
| expect(obs!.version).toBe("1:2:300:4000000000:5000000000") | ||
| expect(typeof obs!.observedAt).toBe("number") | ||
| }) | ||
|
|
||
| it("re-observe replaces the entry with a fresh observedAt", () => { | ||
| vi.useFakeTimers() | ||
| const reg = new ObservationRegistry() | ||
| reg.observe("/a/b/c.ts", "v1") | ||
| const first = reg.get("/a/b/c.ts")! | ||
| expect(first.version).toBe("v1") | ||
|
|
||
| vi.advanceTimersByTime(50) | ||
| reg.observe("/a/b/c.ts", "v2") | ||
| const second = reg.get("/a/b/c.ts")! | ||
| expect(second.version).toBe("v2") | ||
| expect(second.observedAt).toBeGreaterThan(first.observedAt) | ||
|
|
||
| vi.useRealTimers() | ||
| }) | ||
|
|
||
| it("has returns true for observed paths, false otherwise", () => { | ||
| const reg = new ObservationRegistry() | ||
| reg.observe("/x.ts", "t1") | ||
| expect(reg.has("/x.ts")).toBe(true) | ||
| expect(reg.has("/y.ts")).toBe(false) | ||
| }) | ||
|
|
||
| it("size reflects the number of observed entries", () => { | ||
| const reg = new ObservationRegistry() | ||
| expect(reg.size).toBe(0) | ||
| reg.observe("/a.ts", "t1") | ||
| reg.observe("/b.ts", "t2") | ||
| expect(reg.size).toBe(2) | ||
| }) | ||
|
|
||
| it("clear removes all entries and resets size to 0", () => { | ||
| const reg = new ObservationRegistry() | ||
| reg.observe("/a.ts", "t1") | ||
| reg.observe("/b.ts", "t2") | ||
| reg.clear() | ||
| expect(reg.size).toBe(0) | ||
| expect(reg.get("/a.ts")).toBeUndefined() | ||
| expect(reg.has("/b.ts")).toBe(false) | ||
| }) | ||
|
|
||
| it("get on empty registry returns undefined", () => { | ||
| const reg = new ObservationRegistry() | ||
| expect(reg.get("/any.ts")).toBeUndefined() | ||
| }) | ||
|
|
||
| it("separate instances are independent — observing in one does not appear in the other", () => { | ||
| const regA = new ObservationRegistry() | ||
| const regB = new ObservationRegistry() | ||
| regA.observe("/shared.ts", "v1") | ||
| expect(regA.get("/shared.ts")).toBeDefined() | ||
| expect(regB.get("/shared.ts")).toBeUndefined() | ||
| regB.observe("/shared.ts", "v2") | ||
| expect(regA.get("/shared.ts")!.version).toBe("v1") | ||
| expect(regB.get("/shared.ts")!.version).toBe("v2") | ||
| }) | ||
|
|
||
| describe("completeness scope (S4b follow-up #46)", () => { | ||
| it("defaults to a complete observation when the read scope is not given", () => { | ||
| const reg = new ObservationRegistry() | ||
| reg.observe("/a/b/c.ts", "v1") | ||
|
|
||
| expect(reg.get("/a/b/c.ts")!.complete).toBe(true) | ||
| }) | ||
|
|
||
| it("records a partial observation when the read only returned a view of the file", () => { | ||
| const reg = new ObservationRegistry() | ||
| reg.observe("/a/b/c.ts", "v1", false) | ||
|
|
||
| expect(reg.get("/a/b/c.ts")!.complete).toBe(false) | ||
| }) | ||
|
|
||
| it("re-observing replaces the entry's completeness with the new read's scope", () => { | ||
| const reg = new ObservationRegistry() | ||
| reg.observe("/a/b/c.ts", "v1", false) | ||
| reg.observe("/a/b/c.ts", "v2") | ||
|
|
||
| const obs = reg.get("/a/b/c.ts")! | ||
| expect(obs.version).toBe("v2") | ||
| expect(obs.complete).toBe(true) | ||
| }) | ||
|
|
||
| it("re-observing with a partial scope downgrades a previously complete entry", () => { | ||
| const reg = new ObservationRegistry() | ||
| reg.observe("/a/b/c.ts", "v1") | ||
| reg.observe("/a/b/c.ts", "v2", false) | ||
|
|
||
| const obs = reg.get("/a/b/c.ts")! | ||
| expect(obs.version).toBe("v2") | ||
| expect(obs.complete).toBe(false) | ||
| }) | ||
| }) | ||
|
|
||
| it("forget() removes exactly the given path and reports it", () => { | ||
| // DiffViewProvider revokes the observation for the preview path it published. Removing | ||
| // a neighbour's entry would make that neighbour's later write look untouched. | ||
| const reg = new ObservationRegistry() | ||
| reg.observe("/a/b/c.ts", "v1") | ||
| reg.observe("/a/b/d.ts", "v2") | ||
|
|
||
| expect(reg.forget("/a/b/c.ts")).toBe(true) | ||
|
|
||
| expect(reg.get("/a/b/c.ts")).toBeUndefined() | ||
| expect(reg.get("/a/b/d.ts")?.version).toBe("v2") | ||
| expect(reg.size).toBe(1) | ||
| }) | ||
|
|
||
| it("forget() reports false and changes nothing for a path that was never observed", () => { | ||
| // A cancellation can forget a path whose observation was already dropped. The caller | ||
| // distinguishes the two cases through this return value, so it must not throw and must | ||
| // leave the other entries alone. | ||
| const reg = new ObservationRegistry() | ||
| reg.observe("/a/b/c.ts", "v1") | ||
|
|
||
| expect(reg.forget("/a/b/absent.ts")).toBe(false) | ||
|
|
||
| expect(reg.get("/a/b/c.ts")?.version).toBe("v1") | ||
| expect(reg.size).toBe(1) | ||
| }) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| /** | ||
| * Per-task file observation registry (upstream epic #1375, phase A2). | ||
| * | ||
| * Each Task owns its own instance so parent and subtask observations are | ||
| * independent. The S4 guarded-write will compare these versions against the | ||
| * token recomputed pre-write to detect stale reads or file replacement. | ||
| * | ||
| * Pure in-memory — zero I/O, no dependencies. The S4 guarded-write consults | ||
| * these observations for the version check and for the completeness check that | ||
| * gates a full-file replacement. | ||
| */ | ||
|
|
||
| export interface FileObservation { | ||
| /** Version token derived from on-disk fs.stat (bigint mode). */ | ||
| version: string | ||
| /** Millisecond timestamp when the observation was recorded. */ | ||
| observedAt: number | ||
| /** | ||
| * Whether the read that produced this observation returned the complete | ||
| * file. A slice, line-range, truncated, or indentation-block read returns | ||
| * only a view of the file; such an observation authorizes targeted edits | ||
| * on the view the model saw, but never a full-file replacement. | ||
| */ | ||
| complete: boolean | ||
| } | ||
|
|
||
| export class ObservationRegistry { | ||
| private readonly entries = new Map<string, FileObservation>() | ||
|
|
||
| /** | ||
| * Record an observation for a file at its absolute path. | ||
| * | ||
| * Re-observing replaces the entry with a fresh observedAt timestamp, the | ||
| * new version token, and the read's completeness. `complete` defaults to | ||
| * true for callers that read the whole file themselves (spec doubles, | ||
| * WriteToFileTool). A caller whose read is internal to a targeted edit must | ||
| * carry the model's prior completeness instead, so the tool's own read cannot | ||
| * upgrade a partial read into authority for a full-file replacement. | ||
| */ | ||
| observe(absolutePath: string, version: string, complete: boolean = true): void { | ||
| this.entries.set(absolutePath, { version, observedAt: Date.now(), complete }) | ||
| } | ||
|
|
||
| get(absolutePath: string): FileObservation | undefined { | ||
| return this.entries.get(absolutePath) | ||
| } | ||
|
|
||
| has(absolutePath: string): boolean { | ||
| return this.entries.has(absolutePath) | ||
| } | ||
|
|
||
| /** | ||
| * Drop the observation for one path. A caller that must revoke an | ||
| * authorization it did not earn - a preview that observed a version the model | ||
| * never read - needs this instead of clear(), which would also discard the | ||
| * observations other reads of the same task still rely on. | ||
| */ | ||
| forget(absolutePath: string): boolean { | ||
| return this.entries.delete(absolutePath) | ||
| } | ||
|
|
||
| clear(): void { | ||
| this.entries.clear() | ||
| } | ||
|
|
||
| get size(): number { | ||
| return this.entries.size | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
Repository: Zoo-Code-Org/Zoo-Code
Length of output: 12629
Script executed:
Repository: Zoo-Code-Org/Zoo-Code
Length of output: 42641
Script executed:
Repository: Zoo-Code-Org/Zoo-Code
Length of output: 42561
Script executed:
Repository: Zoo-Code-Org/Zoo-Code
Length of output: 44083
Script executed:
Repository: Zoo-Code-Org/Zoo-Code
Length of output: 41427
Script executed:
Repository: Zoo-Code-Org/Zoo-Code
Length of output: 18019
Record observations for file mentions.
When a user mentions a file, the model receives its content without a corresponding observation. A subsequent
write_to_filecan therefore reject an unchanged file as unread or partially read. Record a stable version and accurate completeness for mention reads before sending their content to the model.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Measured: the registry is keyed on the observed on-disk version token — its declaration comment says 'the observed on-disk version of each file this task has read ... a write guard later compares a token against this registry' (Task.ts:300-302). A mention insertion changes the editor buffer, not the disk token, and the next publish re-observes under the guard; reconciling a buffer edit into the disk-version registry would record a version that does not exist on disk. Advisory-by-design, same disposition family as the S680 batch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rate Limit Exceeded
@easonLiangWorldedtechhave exceeded the limit for the number of chat messages per hour. Please wait 6 minutes and 20 seconds before sending another message.