F3 SLICE-3: pending-edit registry + sidecar section-merge update (INV-9 groundwork) (#6)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* PendingEditRegistry — the seam's bookkeeping (spec §6.2, INV-9). Before
|
||||
* applyAgentEdit applies a WorkspaceEdit, it registers the exact expected
|
||||
* change; the controller's change handler consumes a matching registration to
|
||||
* attribute that change to the agent. Anything unmatched is human typing.
|
||||
* vscode-free and unit-testable.
|
||||
*/
|
||||
import type { Provenance } from "./model";
|
||||
|
||||
export interface PendingEdit {
|
||||
docPath: string;
|
||||
/** half-open [start, end) offsets of the replaced range. */
|
||||
start: number;
|
||||
end: number;
|
||||
newText: string;
|
||||
provenance: Provenance;
|
||||
turnId?: string;
|
||||
}
|
||||
|
||||
export class PendingEditRegistry {
|
||||
private pending: PendingEdit[] = [];
|
||||
|
||||
register(edit: PendingEdit): void {
|
||||
this.pending.push(edit);
|
||||
}
|
||||
|
||||
/** Remove a registration that failed to apply. */
|
||||
unregister(edit: PendingEdit): void {
|
||||
this.pending = this.pending.filter((p) => p !== edit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find-and-consume the registration exactly matching a change event
|
||||
* (same doc, same replaced range, same inserted text). Null → human edit.
|
||||
*/
|
||||
match(docPath: string, change: { start: number; end: number; text: string }): PendingEdit | null {
|
||||
const i = this.pending.findIndex(
|
||||
(p) =>
|
||||
p.docPath === docPath &&
|
||||
p.start === change.start &&
|
||||
p.end === change.end &&
|
||||
p.newText === change.text,
|
||||
);
|
||||
if (i === -1) return null;
|
||||
const [hit] = this.pending.splice(i, 1);
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user