# Chip Mechanism Design Decisions

## 1. Motivation

In the MCP (Model Context Protocol) call chain, tool parameters generated by LLMs often fail due to JSON escaping issues, missing fields, or malformed formatting. Once a failure occurs, the original parameter content may only appear in a one-off error response, making it difficult for the model to reproduce the exact same content in subsequent rounds — especially for large code writes (`be-write`) or multi-line replacements.

The core goal of the Chip mechanism is to temporarily preserve the "failure scene" or "deleted content" as a reliable data source for subsequent model recovery operations. It acts as a lightweight FIFO cache queue maintained within the process, allowing the LLM to reinsert previously lost or deleted content into target files via the dedicated `be-insert-chip` tool, thereby reducing the probability of an entire task being interrupted due to a single call failure.

## 2. When a Chip Is Created

There are currently two scenarios that trigger chip recording:

### 2.1 Automatic Parameter Saving on Tool Call Failure

When a tool call returns an error and the serialized JSON length of the input parameters exceeds 50 bytes, the server calls `SaveChip(tool, args, errMsg)` to save the original parameters as a chip.

```go
func SaveChip(tool string, args map[string]any, errMsg string) string
```

- Parameters with JSON length ≤ 50 bytes are not saved, to avoid recording too many meaningless short parameters.
- Returns a chip ID (e.g., `a3f7b2`) on success; returns an empty string if recovery is not needed.

### 2.2 Saving Deleted Content on Delete Operations

`be-delete` saves non-empty deleted content as a chip via `SaveContentChip` before actually writing to disk, and prompts the model in the returned `warnings`:

```go
deletedContent := strings.Join(fileLines[start-1:end], "")
if deletedContent != "" {
    chipID, chipWarn := SaveContentChip("be-delete", deletedContent)
    warnings = append(warnings, fmt.Sprintf("deleted content saved as chip://%s", chipID))
}
```

This allows reinserting accidentally deleted content back into the file via `be-insert-chip`.

## 3. What Is Stored in a Chip

The chip data structure is defined in `pkg/betools/chip.go`:

```go
type ChipRecord struct {
    ID        string         `json:"id"`        // chip unique identifier
    Tool      string         `json:"tool"`      // source tool name, e.g. be-write / be-delete
    Args      map[string]any `json:"args"`      // original parameters or deleted content
    ErrMsg    string         `json:"err_msg,omitempty"` // error message on failure
    CreatedAt int64          `json:"created_at"`        // creation timestamp (Unix seconds)
}
```

- **Chip from failed calls**: `Args` stores the original tool parameters, `ErrMsg` stores the error text.
- **Chip from delete operations**: `Args` is `{"_content": "<deleted text>"}`, `ErrMsg` is empty.

The chip ID is generated by `newShortID`, defaulting to a 6-character hex random string (3 bytes of entropy). On collision, it retries up to 5 times, and falls back to a 12-character hex string if conflicts persist.

## 4. How to List, Read, and Use Chips

### 4.1 Listing Chips

When calling `be-insert-chip` without `from` and `to`, the server returns all chip IDs currently in memory:

```json
{
  "status": "ok",
  "chips": ["a3f7b2", "c8e101", "d245aa"]
}
```

Internally implemented via `ListChips()`, returning in **FIFO order** (oldest first).

### 4.2 Reading a Single Chip

A chip can be read by ID via `GetChip(id)`. It searches the in-memory queue first; if not found in memory (e.g., after a process restart), it falls back to reading `chip-{id}.json` from disk.

### 4.3 Replaying Chip Content

`be-insert-chip` supports two sources:

- `file:///absolute/path`: reads content from a specified file.
- `chip://{id}`: reads content from a chip cache.

Target location format:

- `to`: `file:///absolute/path:line_number`

When the source is `chip://`, the server re-serializes the chip's `Args` as JSON, prepends a comment header, and inserts the content into the target file via `betools.Insert`:

```go
content = fmt.Sprintf("// Chip %s from tool %q\n// Original arguments:\n%s", rec.ID, rec.Tool, string(argsJSON))
```

This allows the model to clearly see in the diff which failed call's content is being replayed.

## 5. Queue Capacity, Eviction Strategy, and Persistence

### 5.1 Capacity and Eviction

```go
const maxChips = 30
```

- The chip queue is globally unique, protected by `sync.Mutex`.
- When the queue exceeds 30, the **oldest chip** is evicted (FIFO).
- Evicted chips are removed from the in-memory `chipStore` and `chipIDSet`, and the corresponding disk file is deleted.
- `SaveContentChip` returns a warning text on eviction, for example:
  ```
  oldest chip a3f7b2 was evicted (queue max 30)
  ```

### 5.2 Disk Persistence

Each chip is written independently to a JSON file:

```go
path := filepath.Join(ChipDir(), fmt.Sprintf("chip-%s.json", record.ID))
```

The cache directory is platform-dependent:

- Windows: `%LOCALAPPDATA%/better-edit-tools-mcp/chips`
- Linux/macOS: `$XDG_CACHE_HOME/better-edit-tools-mcp/chips` or `~/.cache/better-edit-tools-mcp/chips`
- Fallback: `/tmp/better-edit-tools-mcp-chips`

On process startup, `loadChipsFromDisk()` restores chips from this directory:

1. Read all `.json` files;
2. Sort by `CreatedAt`;
3. Evict old files if exceeding `maxChips`;
4. Load into the in-memory queue.

Both writing and deletion are best-effort — disk IO failures do not interrupt the main flow.

## 6. Current Limitations and Future Directions

### 6.1 Current Limitations

1. **Fixed capacity**: `maxChips` is a compile-time constant of 30, cannot be dynamically adjusted per session or disk space.
2. **No TTL**: Only FIFO eviction, no time-based expiration mechanism.
3. **Text only**: Chip content is stored as strings, not suitable for binary file content.
4. **Small parameters not saved**: Parameters with JSON ≤ 50 bytes do not generate a chip; some short but critical parameters may be lost.
5. **Unreliable disk persistence**: Write failures are silently ignored; process crashes may leave residual or missing chips.
6. **Limited cross-process consistency**: Although chips are read from disk, multiple server instances running concurrently may overwrite each other's files.
7. **Fixed replay format**: `chip://` sources always prepend a comment header, which may be undesirable in some scenarios.

### 6.2 Future Directions

- **Configurable capacity**: Adjust `maxChips` via startup parameters or environment variables.
- **TTL / expiration strategy**: Add expiration times to chips, automatically clean up stale records.
- **Categorization and search**: Group by tool type, file path, or error keywords, making it easier for the model to quickly locate chips that need recovery.
- **Snapshot integration**: Combine chips with transaction snapshots, supporting "rollback to pre-delete state" with automatic chip attachment.
- **Direct parameter replay**: In addition to inserting `Args` as text, provide a recovery mode that "re-calls a tool with the original parameters".
- **Encryption or signing**: Encrypt or checksum sensitive file content persisted to shared cache directories, preventing information leakage or tampering.
- **Friendlier list view**: Return metadata such as tool name, creation time, and content summary in `ListChips`, rather than just IDs.

---

**Related source files**: `pkg/betools/chip.go`, `pkg/betools/id.go`, `pkg/betools/ops.go`, `internal/server/server.go`
