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:

  1. Deletes the unconsumed patch file
  2. Falls back to direct CRDT stream write (run_stream())
  3. Logs [write] IPC timeout — falling back to direct write

This makes --ipc safe to use unconditionally in the SKILL.md workflow.

Component Mapping

BinaryPluginPurpose
write.rs:run_ipc()PatchWatcher.ktEnd-to-end IPC flow
template::parse_patches()applyComponentPatch()Patch extraction/application
snapshot::save()FileDocumentManager.saveDocument()Persistence
atomic_write() (patch JSON)NIO WatchServiceFile-based IPC transport

Pattern Expression

IDE Scope

The IPC pattern maps to IntelliJ's threading model:

  • EDT (Event Dispatch Thread): invokeLater schedules 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.