|
| 1 | +# OPX v1: Overwrite Patch XML — Product Requirements Document (PRD) |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +OPX is an original XML-based change protocol for Overwrite that LLMs produce and the extension applies to the workspace. |
| 6 | + |
| 7 | +Operations supported: |
| 8 | + |
| 9 | +- Create files |
| 10 | +- Patch specific regions of files (search-and-replace) |
| 11 | +- Replace entire files |
| 12 | +- Remove files |
| 13 | +- Move/rename files |
| 14 | + |
| 15 | +## Goals |
| 16 | + |
| 17 | +- Replace the existing immediately with a new, original protocol. |
| 18 | +- Keep the format minimal and highly reliable for LLMs to generate. |
| 19 | +- Map OPX operations directly onto our existing apply engine with no behavior changes beyond parsing. |
| 20 | + |
| 21 | +## Non‑Goals |
| 22 | + |
| 23 | +- No phased rollout or legacy compatibility. |
| 24 | +- No telemetry changes. |
| 25 | +- No new runtime dependencies. |
| 26 | + |
| 27 | +--- |
| 28 | + |
| 29 | +## Protocol Specification |
| 30 | + |
| 31 | +### Top-level |
| 32 | + |
| 33 | +- One or more `<edit>` elements may appear at the top level. |
| 34 | +- Optionally, edits may be wrapped in a single `<opx>...</opx>` container. The container is ignored by the parser. |
| 35 | + |
| 36 | +### Edit element |
| 37 | + |
| 38 | +Element: `<edit>` |
| 39 | +- Required attributes: |
| 40 | + - `file`: path to the target file (prefer workspace-relative) |
| 41 | + - `op`: operation type (see below) |
| 42 | +- Optional attributes: |
| 43 | + - `root`: VS Code workspace root folder name for multi-root workspaces |
| 44 | + |
| 45 | +Supported `op` values and semantics: |
| 46 | +- `new` — create a new file. Requires a `<put>` child. |
| 47 | +- `patch` — search-and-replace a region. Requires both `<find>` and `<put>` children. |
| 48 | +- `replace` — replace entire file contents. Requires a `<put>` child. |
| 49 | +- `remove` — delete file. No children required. |
| 50 | +- `move` — rename/move file. Requires a `<to file="..."/>` child. |
| 51 | + |
| 52 | +### Children of `<edit>` |
| 53 | + |
| 54 | +- `<why>` (optional): A brief sentence describing the change for this edit. |
| 55 | +- `<find>` (for `op="patch"`): |
| 56 | + - Optional attribute: `occurrence="first|last|N"` to disambiguate repeated matches. |
| 57 | + - Contains a literal block delimited by `<<<` (start) and `>>>` (end) on their own lines. |
| 58 | +- `<put>` (for `op="new"`, `op="patch"`, `op="replace"`): |
| 59 | + - Contains a literal block delimited by `<<<` and `>>>` on their own lines. |
| 60 | +- `<to file="..."/>` (for `op="move"`): |
| 61 | + - Self-closing element with a required `file` attribute specifying destination path. |
| 62 | + |
| 63 | +### Literal content blocks |
| 64 | + |
| 65 | +- Inside `<find>` and `<put>`, the payload must be wrapped between lines containing only `<<<` and `>>>`. |
| 66 | +- The parser takes all text strictly between those markers. Surrounding whitespace/newlines around markers are trimmed. |
| 67 | +- Parser auto-heal: to mitigate Markdown/chat copy issues, if a line inside `<find>`/`<put>` contains only `<` or `<<`, it is treated as `<<<`; if it contains only `>` or `>>`, it is treated as `>>>`. This repair happens before extraction and affects only full-line markers. |
| 68 | + |
| 69 | +### Path rules |
| 70 | + |
| 71 | +- Prefer workspace-relative paths (e.g., `src/lib/logger.ts`). |
| 72 | +- `file://` URIs and absolute paths are tolerated but not required. |
| 73 | +- Do not reference paths outside the workspace. |
| 74 | + |
| 75 | +### Examples |
| 76 | + |
| 77 | +New file |
| 78 | +```xml |
| 79 | +<edit file="src/utils/strings.ts" op="new"> |
| 80 | + <why>Create utility module</why> |
| 81 | + <put> |
| 82 | +<<< |
| 83 | +export function titleCase(s: string): string { |
| 84 | + return s.split(/\s+/).map(w => w ? w[0]!.toUpperCase() + w.slice(1) : w).join(' ') |
| 85 | +} |
| 86 | +>>> |
| 87 | + </put> |
| 88 | +</edit> |
| 89 | +``` |
| 90 | + |
| 91 | +Patch region |
| 92 | +```xml |
| 93 | +<edit file="src/api/users.ts" op="patch"> |
| 94 | + <why>Add timeout and error logging</why> |
| 95 | + <find occurrence="first"> |
| 96 | +<<< |
| 97 | +export async function fetchUser(id: string) { |
| 98 | + const res = await fetch(`/api/users/${id}`); |
| 99 | + if (!res.ok) throw new Error(`Request failed: ${res.status}`); |
| 100 | + return res.json(); |
| 101 | +} |
| 102 | +>>> |
| 103 | + </find> |
| 104 | + <put> |
| 105 | +<<< |
| 106 | +async function withTimeout<T>(p: Promise<T>, ms = 10000): Promise<T> { |
| 107 | + const t = new Promise<never>((_, r) => setTimeout(() => r(new Error('Request timed out')), ms)); |
| 108 | + return Promise.race([p, t]); |
| 109 | +} |
| 110 | + |
| 111 | +export async function fetchUser(id: string) { |
| 112 | + try { |
| 113 | + const res = await withTimeout(fetch(`/api/users/${id}`), 10000); |
| 114 | + if (!res.ok) throw new Error(`Request failed: ${res.status}`); |
| 115 | + return res.json(); |
| 116 | + } catch (err) { |
| 117 | + console.error('[api] fetchUser error', err); |
| 118 | + throw err; |
| 119 | + } |
| 120 | +} |
| 121 | +>>> |
| 122 | + </put> |
| 123 | +</edit> |
| 124 | +``` |
| 125 | + |
| 126 | +Replace entire file |
| 127 | +```xml |
| 128 | +<edit file="src/config/index.ts" op="replace"> |
| 129 | + <put> |
| 130 | +<<< |
| 131 | +export interface AppConfig { |
| 132 | + apiBaseUrl: string; |
| 133 | + enableTelemetry: boolean; |
| 134 | + maxConcurrentJobs: number; |
| 135 | +} |
| 136 | + |
| 137 | +export const config: AppConfig = { |
| 138 | + apiBaseUrl: process.env.API_BASE_URL || 'http://localhost:3000', |
| 139 | + enableTelemetry: process.env.TELEMETRY === '1', |
| 140 | + maxConcurrentJobs: Number(process.env.MAX_JOBS || 4), |
| 141 | +}; |
| 142 | +>>> |
| 143 | + </put> |
| 144 | +</edit> |
| 145 | +``` |
| 146 | + |
| 147 | +Remove file |
| 148 | +```xml |
| 149 | +<edit file="tests/legacy/user-auth.spec.ts" op="remove" /> |
| 150 | +``` |
| 151 | + |
| 152 | +Move / rename file |
| 153 | +```xml |
| 154 | +<edit file="src/lib/flags.ts" op="move"> |
| 155 | + <to file="src/lib/feature-flags.ts" /> |
| 156 | +</edit> |
| 157 | +``` |
| 158 | + |
| 159 | +--- |
| 160 | + |
| 161 | +## Architecture Changes (Immediate) |
| 162 | + |
| 163 | +All changes below are applied at once to switch the system fully to OPX v1. |
| 164 | + |
| 165 | +### Parser — OPX only |
| 166 | + |
| 167 | +File: [src/utils/xml-parser.ts](file:///Users/minhthanh/Work/Side/overwrite/src/utils/xml-parser.ts) |
| 168 | + |
| 169 | +- Remove legacy parsing and markers; accept only OPX tags. |
| 170 | +- New parse path: |
| 171 | + - Accept either top-level `<edit>` elements or a single `<opx>` wrapper containing `<edit>` elements. |
| 172 | + - For each `<edit>`: |
| 173 | + - Read attributes `file`, `op`, optional `root`. |
| 174 | + - `op -> action` mapping: |
| 175 | + - `new` → `create` |
| 176 | + - `patch` → `modify` |
| 177 | + - `replace` → `rewrite` |
| 178 | + - `remove` → `delete` |
| 179 | + - `move` → `rename` |
| 180 | + - For `patch`: extract `<find>` (with optional `occurrence`) and `<put>` blocks. |
| 181 | + - For `new|replace`: extract `<put>` block. |
| 182 | + - For `move`: read `<to file="..."/>` into `newPath`. |
| 183 | + - Literal extraction uses `<<<`/`>>>` only. |
| 184 | +- Return the same `ParseResult` and `FileAction[]` types used by the apply engine. |
| 185 | + |
| 186 | +### Preprocessor (Apply tab) |
| 187 | + |
| 188 | +File: [src/webview-ui/src/components/apply-tab/preprocess.ts](file:///Users/minhthanh/Work/Side/overwrite/src/webview-ui/src/components/apply-tab/preprocess.ts) |
| 189 | + |
| 190 | +- Extend normalization to `<edit ...>` and `<to .../>` tags (lowercase keys, normalize quotes). |
| 191 | +- Remove legacy tag handling (`<file>`, `<new/>`, etc.). |
| 192 | +- Lint rules: |
| 193 | + - `<edit>` must include `file` and `op`. |
| 194 | + - `op="patch"` requires both `<find>` and `<put>`. |
| 195 | + - `op="move"` requires `<to file="..."/>`. |
| 196 | + |
| 197 | +### Apply Pipeline (no changes) |
| 198 | + |
| 199 | +File: [src/providers/file-explorer/file-action-handler.ts](file:///Users/minhthanh/Work/Side/overwrite/src/providers/file-explorer/file-action-handler.ts) |
| 200 | + |
| 201 | +- No behavior changes required. The parser still emits `action` in `{create|modify|rewrite|delete|rename}` and the same `changes`/`newPath` fields. |
| 202 | + |
| 203 | +### Prompt Text |
| 204 | + |
| 205 | +File: [src/prompts/xml-instruction.ts](file:///Users/minhthanh/Work/Side/overwrite/src/prompts/xml-instruction.ts) |
| 206 | + |
| 207 | +- Replace the entire instruction constant with OPX-only wording and examples. |
| 208 | +- Remove references to legacy format. |
| 209 | + |
| 210 | +--- |
| 211 | + |
| 212 | +## DX |
| 213 | + |
| 214 | +- OPX is simpler to read and write: |
| 215 | + - One element per edit (`<edit>`), with an explicit `op` attribute. |
| 216 | + - Clear, distinct delimiters `<<<` / `>>>` for literal content. |
| 217 | + - Optional `<why>` enables short, per-edit intent |
| 218 | + |
| 219 | +--- |
| 220 | + |
| 221 | +## Testing Plan (Immediate) |
| 222 | + |
| 223 | +Parser unit tests (targeted): |
| 224 | +- Valid cases: `new`, `patch`, `replace`, `remove`, `move`. |
| 225 | +- Occurrence handling: `first`, `last`, numeric `N`, ambiguous without `occurrence` (error). |
| 226 | +- Marker parsing: mixed whitespace around `<<<`/`>>>`. |
| 227 | +- Error cases: missing attributes, missing required children, multiple `<put>`/`<find>`. |
| 228 | + |
| 229 | +Webview tests (Apply tab): |
| 230 | +- Lint/normalization for `<edit>`/`<to>` tags. |
| 231 | +- Preview renders correct summary and per-row apply functions. |
| 232 | + |
| 233 | +Manual smoke: |
| 234 | +- Dev playground at http://localhost:5173/. |
| 235 | +- Paste each example from this document and verify preview/apply success. |
| 236 | + |
| 237 | +--- |
| 238 | + |
| 239 | +## Risks & Mitigations |
| 240 | + |
| 241 | +- LLM adherence to the new format: |
| 242 | + - Keep instruction text concise and provide 3–4 OPX examples. |
| 243 | + - Use stable tags/attributes and simple markers. |
| 244 | +- Content delimiter collision: |
| 245 | + - `<<<`/`>>>` are uncommon; if conflicts appear, OPX v1.1 can add an optional custom `marker` attribute. |
| 246 | +- Markdown copy truncation of markers: |
| 247 | + - The parser auto-heals full-line `<`/`<<`/`>`/`>>` into `<<<`/`>>>` inside `<find>`/`<put>` blocks. |
| 248 | + - Guidance: ask models to emit OPX inside a single fenced block (e.g., ```xml … ```), and copy from within the fence. |
| 249 | + |
| 250 | +--- |
| 251 | + |
| 252 | +## Acceptance Criteria |
| 253 | + |
| 254 | +- Legacy input is no longer accepted; OPX-only responses are parsed and applied. |
| 255 | +- All five operations (`new`, `patch`, `replace`, `remove`, `move`) work end-to-end via Apply tab. |
| 256 | +- Instruction text is replaced with OPX-only content. |
| 257 | +- Unit tests for OPX pass; Apply tab previews and applies OPX responses successfully. |
| 258 | + |
| 259 | +--- |
| 260 | + |
| 261 | +## Implementation Checklist |
| 262 | + |
| 263 | +1) Parser |
| 264 | +- [x] Remove legacy parsing path and `===` marker support. |
| 265 | +- [x] Implement OPX-only parsing with `<<<`/`>>>` markers. |
| 266 | +- [x] Map `op` → internal actions. |
| 267 | + |
| 268 | +2) Preprocessor |
| 269 | +- [x] Normalize/lint `<edit>` and `<to>`. |
| 270 | +- [x] Remove legacy tag handling and lints. |
| 271 | + |
| 272 | +3) Prompt text |
| 273 | +- [x] Replace instruction in [src/prompts/xml-instruction.ts](file:///Users/minhthanh/Work/Side/overwrite/src/prompts/xml-instruction.ts) with OPX. |
| 274 | + |
| 275 | +4) Tests |
| 276 | +- [x] Add OPX parser tests. |
| 277 | +- [x] Update/Add webview tests for OPX samples. |
| 278 | + |
| 279 | +5) Manual smoke |
| 280 | +- [ ] Validate examples from this PRD in the dev playground. |
| 281 | + |
| 282 | +--- |
| 283 | + |
| 284 | +## Release Checklist (OPX v1) |
| 285 | + |
| 286 | +- [x] Replace prompt instructions with OPX-only (src/prompts/xml-instruction.ts) |
| 287 | +- [x] Parser OPX-only (src/utils/xml-parser.ts) and preprocessor normalization (src/webview-ui/.../preprocess.ts) |
| 288 | +- [x] Backend tests green: `pnpm test` |
| 289 | +- [x] Webview tests green: `pnpm -C src/webview-ui test --run` |
| 290 | +- [ ] Optional manual smoke in dev playground (http://localhost:5173/) |
| 291 | +- [ ] Bump version in package.json if releasing |
| 292 | +- [ ] Package: `pnpm package` (or `pnpm vscode:package`) |
| 293 | + |
0 commit comments