Skip to content

Latest commit

 

History

History
120 lines (93 loc) · 5 KB

File metadata and controls

120 lines (93 loc) · 5 KB

Interactive Patch Flow

Interactive HMD editing is a host-mediated loop:

HMD source -> semantic HTML -> PatchIntent -> HmdPatch -> validated HMD source -> rerender

The browser or webview is UI glue. It should read semantic attributes, build a PatchIntent, and hand that intent to Rust through WASM, CLI, or a local/editor host. It must not reimplement HMD parsing, validation, or patch semantics.

Quickstart

cp fixtures/valid/decision-basic.hmd /tmp/decision.hmd
cargo run -p hmd-cli -- action /tmp/decision.hmd fixtures/intents/decision-select-rust.json --print-patch > /tmp/select-rust.patch.json
cargo run -p hmd-cli -- patch /tmp/decision.hmd /tmp/select-rust.patch.json --write
cargo run -p hmd-cli -- validate /tmp/decision.hmd
cargo run -p hmd-cli -- render /tmp/decision.hmd -o /tmp/decision.updated.html
rg 'CH-D-runtime-rust|selected|choice' /tmp/decision.updated.html

For a browser preview:

cargo run -p hmd-cli -- render examples/decision.hmd --interactive -o examples/decision.interactive.html
python3 -m http.server --directory examples 8000

Open http://127.0.0.1:8000/decision.interactive.html. The static page can build and preview patch data. It does not write files.

For a write-capable local host:

cargo run -p hmd-cli -- preview examples/decision.hmd --write --port 0

The preview server binds only to 127.0.0.1, injects a per-run token, and rejects writes without that token.

PatchIntent

{
  "intentVersion": "0.1",
  "action": "decision.select_option",
  "target": "/blocks/rust",
  "context": {
    "documentProfile": "decision@0.1",
    "sourceHash": "sha256-...",
    "decisionTarget": "/blocks/D-runtime"
  },
  "params": {
    "choiceId": "CH-D-runtime-rust",
    "choiceStatus": "selected",
    "markdown": "Selected Rust."
  }
}

decision.select_option appends a choice block. It does not modify decision status. Use meta.set for field updates and body.replace for body edits.

Host Matrix

Host Can write files Transport Safety gate
Static HTML no copy intent/patch, optional WASM preview no file write
WASM no createPatch, applyPatch, applyIntent hash and validation errors are thrown
CLI yes with --write or -o files or stdin stale check, duplicate ID check, validate before write
Local preview yes with --write localhost POST /api/intent or /api/patch localhost, token, stale check, validate, atomic write
VS Code/webview extension host only postMessage intent/patch document version/hash before WorkspaceEdit
npm CLI yes through native binary npm exec ... hmd action same CLI gates

Editor Webview Protocol

Webviews never access the filesystem. They send versioned messages to the extension host:

type HmdWebviewMessage =
  | { version: "0.1"; type: "hmd.intent"; intent: PatchIntent }
  | { version: "0.1"; type: "hmd.patch"; patch: HmdPatch }
  | { version: "0.1"; type: "hmd.requestSource" }
  | { version: "0.1"; type: "hmd.source"; source: string; documentVersion: number }
  | { version: "0.1"; type: "hmd.applyResult"; ok: boolean; diagnostics?: unknown[] };

The extension host should call WASM or CLI to create/apply patches, compare the active document version or source hash, use WorkspaceEdit to update the buffer, and rerender the preview after success. Unsaved editor changes must not be overwritten blindly.

Prototype adapter: editors/vscode-hmd/host-prototype.mjs.

Minimal documented adapter:

webview.onDidReceiveMessage(async (message: HmdWebviewMessage) => {
  if (message.version !== "0.1") throw new Error("Unsupported HMD webview message");
  if (message.type !== "hmd.intent" && message.type !== "hmd.patch") return;

  const document = vscode.window.activeTextEditor?.document;
  const version = document?.version;
  const source = document?.getText() ?? "";
  const patch =
    message.type === "hmd.intent"
      ? JSON.parse(await wasm.createPatch(source, JSON.stringify(message.intent)))
      : message.patch;

  if (version !== document?.version) throw new Error("Document changed while patch was prepared");
  const patched = await wasm.applyPatch(source, JSON.stringify(patch));
  const edit = new vscode.WorkspaceEdit();
  edit.replace(document.uri, fullDocumentRange(document), patched);
  await vscode.workspace.applyEdit(edit);
  webview.postMessage({ version: "0.1", type: "hmd.applyResult", ok: true });
});

Failure Rules

  • Stale sourceHash or targetHash: return conflict and do not write.
  • Write-capable CLI and local preview paths require context.sourceHash on intents or targetHash on patches.
  • Missing or ambiguous target: return non-zero CLI status or HTTP 400/409.
  • Duplicate ID append: fail before source replacement.
  • Invalid patched HMD: return validation error and do not write unless the CLI user passes --allow-invalid.
  • Static pages must say that patch data is generated or previewed; they must not imply a local file was saved.