IPC
Ontology
IPC (Inter-Process Communication) in agent-doc is a System for delivering document patches from the agent-doc binary to an IDE Plugin without triggering external file change detection. It derives from Context (the IDE's document editing environment) and Resolution (applying changes at the component level rather than the whole-file level).
IPC is the bridge between the CLI Domain (agent-doc binary) and the IDE Domain (JetBrains/VS Code plugin), enabling conflict-free document updates that preserve user Focus — cursor position, selection, and editing flow.
Axiology
External file writes (tempfile + rename) trigger IDE reload behaviors:
- Cursor displacement — IDE moves caret to the changed region during reload
- "File changed externally" dialog — blocks user flow, risks data loss on Esc
- Undo history disruption — external writes break the IDE's undo chain
IPC eliminates all three by routing patches through the IDE's native Document API, where changes are applied in-process with full cursor preservation and undo batching.
Epistemology
Architecture
.agent-doc/patches/
┌──────────────────┐
agent-doc write --ipc ──┤ <hash>.json │
│ │
│ { file, patches,│
│ unmatched, │
│ baseline } │
└────────┬─────────┘
│
NIO WatchService (inotify/FSEvents)
│
┌────────▼─────────┐
│ PatchWatcher.kt │
│ │
│ 1. Read JSON │
│ 2. Find Document│
│ 3. Apply patches│
│ 4. Save to disk │
│ 5. Delete JSON │
│ (ACK) │
└────────┬─────────┘
│
WriteCommandAction.runWriteCommandAction
(holds doc lock, batches undo, preserves cursor)
│
┌────────▼─────────┐
│ IntelliJ Doc │
│ (in-memory) │
│ │
│ <!-- agent:X -->│
│ patched content │
│ <!-- /agent:X-->│
└──────────────────┘
Sequence
Binary Filesystem Plugin
│ │ │
│ write <hash>.json │ │
├─────────────────────────>│ │
│ │ ENTRY_CREATE event │
│ ├─────────────────────>│
│ │ │ read JSON
│ │ │ find Document
│ │ │ apply patches
│ │ │ save document
│ │ delete <hash>.json │
│ │<─────────────────────┤
│ poll: file gone (ACK) │ │
│<─────────────────────────│ │
│ │ │
│ read file, save │ │
│ snapshot + CRDT state │ │
│ │ │
Patch JSON Format
{
"file": "/absolute/path/to/document.md",
"patches": [
{
"component": "exchange",
"content": "Response content for the exchange component."
},
{
"component": "status",
"content": "**Version:** v0.17.0 | **Tests:** 303 passing"
}
],
"unmatched": "Content not targeting a specific component.",
"baseline": "Document content at response generation time."
}
Each patch targets a <!-- agent:name -->...<!-- /agent:name --> component. The plugin replaces the content between markers with the patch content.
Fallback
If the patch file is not consumed within 2 seconds (plugin not installed or IDE not running), the binary:
- Deletes the unconsumed patch file
- Falls back to direct CRDT stream write (
run_stream()) - Logs
[write] IPC timeout — falling back to direct write
This makes --ipc safe to use unconditionally in the SKILL.md workflow.
Component Mapping
| Binary | Plugin | Purpose |
|---|---|---|
write.rs:run_ipc() | PatchWatcher.kt | End-to-end IPC flow |
template::parse_patches() | applyComponentPatch() | Patch extraction/application |
snapshot::save() | FileDocumentManager.saveDocument() | Persistence |
atomic_write() (patch JSON) | NIO WatchService | File-based IPC transport |
Pattern Expression
IDE Scope
The IPC pattern maps to IntelliJ's threading model:
- EDT (Event Dispatch Thread):
invokeLaterschedules patch application on the EDT - WriteCommandAction: Acquires the document write lock, groups changes as a single undo unit
- FileDocumentManager: Flushes the in-memory Document to disk after patching
This is the same mechanism IntelliJ uses for its own refactoring operations — the cursor and selection are preserved because the change originates from within the IDE process, not from an external file modification.
CLI Scope
The binary side is deliberately minimal:
- Parse patches from stdin (reuses existing
template::parse_patches()) - Serialize to JSON (serde_json)
- Atomic write of patch file (same
atomic_write()used everywhere) - Poll for deletion with timeout
- Update snapshot from the file the plugin saved
No new dependencies, no new IPC protocol, no sockets — just a JSON file in a watched directory.