Skip to content
Back to skills

2498 Hashline Subsystem 921790b7

ASecurity

The hashline edit subsystem provides cryptographically-validated file editing for agent operations. It implements a **read-side annotation + write-side validation** pattern that prevents stale edits and ensures edits are always applied to the correct line content.

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
testingpythongotestingdebuggingapidocumentation

Works with

  • api

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 2498-hashline-subsystem_921790b7 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 2498 Hashline Subsystem 921790b7?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 2498 Hashline Subsystem 921790b7
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-2498-hashline-subsystem-921790b7/badge)](https://www.skillsdirectory.com/skills/tools-only-2498-hashline-subsystem-921790b7)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
---
title: Hashline Edit Subsystem
summary: Cryptographically-validated file editing with content-hash line tagging and in-memory cache validation
read_when:
  - You need to understand how file edits are validated
  - You're implementing a new file editing tool
  - Debugging hash mismatch errors in file edits
  - Working on the read_file → hashline_edit workflow
depends_on:
  - docs/modules/tools/index.md
  - docs/modules/core/ui_api.md
feeds_into:
  - docs/modules/tools/read_file.md
  - docs/modules/tools/file_editing.md
---

# Hashline Edit Subsystem

## What

The hashline edit subsystem provides cryptographically-validated file editing for agent operations. It implements a **read-side annotation + write-side validation** pattern that prevents stale edits and ensures edits are always applied to the correct line content.

Each line read by the agent is tagged with a short MD5 content hash (2 hex characters = 256 buckets). When the agent requests an edit, the hash is validated against the cached file state. If the hash doesn't match, the edit is rejected with a clear error message instructing the model to re-read the file.

### Key Features

- **Content-hash validation**: Every line is validated before editing
- **Three edit operations**: `replace`, `replace_range`, `insert_after`
- **Side-by-side diff visualization**: NeXTSTEP-style panel with before/after view
- **Automatic cache updates**: Cache stays synchronized after every edit
- **Unified diff output**: Standard diff format for review and verification

### Workflow Flow Diagram

```
┌─────────────┐     ┌──────────────┐     ┌─────────────┐     ┌──────────────┐
│             │     │              │     │             │     │              │
│  read_file  │────▶│  hashline.py │────▶│ line_cache  │────▶│ hashline_edit│
│             │     │  (tag lines) │     │  (store)    │     │  (validate)  │
└─────────────┘     └──────────────┘     └─────────────┘     └──────┬───────┘
                                                                    │
                    ┌──────────────┐                               │
                    │              │◄──────────────────────────────┘
                    │    File      │       (write changes)
                    │              │
                    └──────────────┘
                            │
                            ▼
                    ┌──────────────┐
                    │    Diff      │────▶ UI Renderer
                    │   Output     │     (side-by-side)
                    └──────────────┘
```

## Key Files

### Core Components

| File | Purpose |
|------|---------|
| `src/tunacode/tools/hashline.py` | Content-hash line tagging and validation primitives |
| `src/tunacode/tools/line_cache.py` | In-memory cache for hashed lines keyed by file path |
| `src/tunacode/tools/hashline_edit.py` | Main edit tool with three operations (replace, replace_range, insert_after) |
| `src/tunacode/tools/read_file.py` | File reading that populates cache with hash-tagged output |
| `src/tunacode/ui/renderers/tools/hashline_edit.py` | Side-by-side diff display with NeXTSTEP panel styling |

### File Relationships

```
read_file.py
    │
    ├──▶ hashline.py
    │       ├── tag_lines()
    │       ├── content_hash()
    │       └── format_hashline()
    │
    └──▶ line_cache.py (store)
            │
            │    ▲
            │    │ (validate & update)
            ▼    │
    hashline_edit.py
            │
            ├── _validate_ref() ──▶ line_cache.py (get_line, validate_ref)
            ├── _apply_replace()
            ├── _apply_replace_range()
            ├── _apply_insert_after()
            │
            └──▶ UI renderer ────▶ hashline_edit_renderer.py
```

## How

### 1. Content Hashing (`hashline.py`)

Each line is hashed using MD5 (2 hex characters = 256 buckets), giving enough entropy to detect stale references without bloating the context window.

```python
HASH_LENGTH = 2
LINE_HASH_SEPARATOR = ":"
HASH_SEPARATOR = "|"

# Example output format
# 1:a3|function hello() {
# 2:f1|    console.log("world");
# 3:0e|}
```

**Key Functions:**

| Function | Purpose |
|----------|---------|
| `content_hash(line)` | Compute MD5 hash of line content (2 hex chars) |
| `tag_lines(content, offset)` | Split content and tag each line with hash |
| `format_hashline(HashedLine)` | Format as `line:hash|content` string |
| `format_hashlines(content)` | Tag all lines in content for display |
| `parse_line_ref(ref)` | Parse `line:hash` reference, e.g., `"2:f1"` |

### 2. Line Cache (`line_cache.py`)

Stores `{path: {line_number: HashedLine}}` as a module-level singleton. The cache is replaced (not merged) on each `read_file` call.

**Cache Operations:**

| Function | Purpose |
|----------|---------|
| `store(filepath, lines)` | Cache hashed lines for a file |
| `get(filepath)` | Return read-only view of cached lines |
| `get_line(filepath, line_number)` | Return single cached line or None |
| `validate_ref(filepath, line, expected_hash)` | Check if line hash matches cached state |
| `update_lines(filepath, updates)` | Update cached lines after an edit |
| `replace_range(filepath, start, end, new_lines)` | Replace a range and re-number subsequent lines |
| `invalidate(filepath)` | Remove cached state for a file |
| `clear()` | Clear entire cache (testing) |

### 3. Edit Tool (`hashline_edit.py`)

Three edit operations validate against the cache before applying changes.

**Validation Flow:**

```
┌─────────────────┐
│  hashline_edit  │
│   (operation)   │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  _validate_ref  │──── Parse "line:hash" reference
│                 │──── Check file is cached
│                 │──── Verify line exists in cache
│                 │──── Compare expected vs actual hash
└────────┬────────┘
         │
    ┌────┴────┐
    │         │
    ▼         ▼
┌──────┐  ┌──────┐
│ Pass │  │ Fail │
└──┬───┘  └──┬───┘
   │         │
   ▼         ▼
┌──────┐  ┌─────────────┐
│ Apply│  │ ToolRetryError│
│ Edit │  │ "hash mismatch│
└──┬───┘  │  Re-read..." │
   │      └─────────────┘
   ▼
┌──────────┐
│ Update   │
│ Cache    │
└──────────┘
```

**Three Operations:**

| Operation | Parameters | Description |
|-----------|------------|-------------|
| `replace` | `line`, `new` | Replace a single line identified by hash |
| `replace_range` | `start`, `end`, `new` | Replace contiguous range of lines |
| `insert_after` | `after`, `new` | Insert new lines after a referenced line |

**Error Messages:**

```python
STALE_REF_MESSAGE = (
    "File has changed since last read — line {line} hash mismatch "
    "(expected '{expected}', cached '{actual}'). Re-read the file first."
)

UNCACHED_FILE_MESSAGE = "File '{filepath}' has no cached state. Read the file first with read_file."

LINE_NOT_CACHED_MESSAGE = (
    "Line {line} is not in the cached state for '{filepath}'. "
    "The file may have been read with an offset that skipped this line. "
    "Re-read the file to include this line."
)
```

### 4. Read Integration (`read_file.py`)

`read_file` populates the line cache with every read, enabling `hashline_edit` to validate references.

```python
# Format: line:hash|content
# Example output:
1:a3|def main():
2:f1|    pass
3:0e|
```

**Key Points:**

- Each `read_file` call **replaces** the cache for that file
- Paginated reads do not merge prior cache windows
- `hashline_edit` can only edit lines present in the current cache
- Missing lines cause `ToolRetryError` with offset hints

### 5. UI Renderer (`hashline_edit_renderer.py`)

NeXTSTEP-style panel with four zones:

```
┌─────────────────────────────────────────────┐
│ filename.js   +3 -2          ← Zone 1: Header│
│                                             │
│ /path/to/filename.js         ← Zone 2: Path│
│ ─────────────────────────────              │
│   1│function old() {      │ 1│function new() {     ← Zone 3: Side-by-side
│ - 2│  return old;        │+2│  return new;       │     diff viewport
│   3│}                    │ 3│}                   │
│ ─────────────────────────────                │
│                                             │
│ 1 hunk  [45ms]               ← Zone 4: Status  │
└─────────────────────────────────────────────┘
```

**Rendering Pipeline:**

```
hashline_edit result (unified diff text)
         │
         ▼
    parse_result()
         │
         ├──▶ Extract filepath
         ├──▶ Count additions (+)
         ├──▶ Count deletions (-)
         └──▶ Count hunks (@@)
         │
         ▼
   _parse_side_by_side_rows()
         │
         ├──▶ Parse hunk headers (@@ -1,3 +1,3 @@)
         ├──▶ Categorize lines: context, insert, delete, meta
         └──▶ Build DiffSideBySideLine objects
         │
         ▼
   _build_side_by_side_viewport()
         │
         ├──▶ Calculate column widths
         ├──▶ Apply syntax highlighting styles
         └──▶ Build Rich Table with gutters
```

## Why

### Prevents Stale Edits

Without hash validation, an agent might reference a line that was:

- Deleted by another process
- Modified by a previous edit
- Never actually existed at that line number

The hash acts as a **content-addressed pointer**. If the content changes, the hash changes, and the edit is rejected before touching the filesystem.

### Provides Clear Edit Visualization

Side-by-side diff makes changes explicit:

```
Before                    │ After
──────────────────────────┼──────────────────────────
  1│old_function() {       │  1│new_function() {
- 2│  return old_value;   │+ 2│  return new_value;
  3│}                     │  3│}
```

### Enables Safe Concurrent Editing

The cache is session-local and updated immediately after each edit. This allows:

1. Multiple edits to the same file in sequence
2. Cache stays synchronized with filesystem
3. Next edit validates against updated hashes

### Minimal Overhead

- 2 hex characters = 256 buckets (0.4% collision probability)
- Cache is in-memory only (no disk I/O)
- Unified diff output is standard and reviewable

## Usage Example

```python
# 1. Read file (populates cache)
result = await read_file("/path/to/file.py")
# Output: 1:a3|def main():
#         2:f1|    pass

# 2. Edit a line using hash reference
result = await hashline_edit(
    filepath="/path/to/file.py",
    operation="replace",
    line="2:f1",  # Line 2, hash f1
    new="    print('hello')"
)

# 3. If hash mismatches (file changed)
# Raises: ToolRetryError("File has changed since last read...")
```

## Related Documentation

- `docs/modules/tools/read_file.md` - File reading and cache population
- `docs/modules/tools/file_editing.md` - General file editing patterns
- `docs/modules/core/ui_api.md` - Panel rendering and UI constants

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.