# Data Model: Screenshot Analysis Skill

**Feature**: 001-screenshot-analysis  
**Date**: 2025-11-08

## Overview

This skill has a minimal data model focused on configuration and file metadata. There are no persistent data structures or databases - all data is ephemeral (file system state) or configuration (JSON file).

## Entities

### Screenshot File

**Description**: A screenshot image file discovered in the configured directory

**Attributes**:
- `path` (string, absolute): Full filesystem path to the screenshot file
  - Example: `/home/user/Pictures/Screenshots/screenshot-2025-11-08-143022.png`
  - Validation: Must be absolute path, must exist, must be readable
- `modification_time` (timestamp, Unix epoch): File modification time in seconds since epoch
  - Example: `1731078622` (2025-11-08 14:30:22 UTC)
  - Used for: Sorting files by recency
  - Source: `stat -c '%Y'`
- `format` (enum): Image file format
  - Allowed values: `PNG`, `JPG`, `JPEG`
  - Validation: Case-insensitive match on file extension
  - Used for: Filtering non-screenshot files

**Lifecycle**: Ephemeral (discovered on each invocation, not persisted)

**Relationships**: None (standalone file, no references)

**State Transitions**: N/A (stateless)

---

### Screenshot Directory

**Description**: Location where screenshots are stored

**Attributes**:
- `path` (string, absolute): Full filesystem path to the directory
  - Default: `~/Pictures/Screenshots` (expanded to absolute)
  - Custom: Loaded from config file `screenshot_dir` field
  - Validation: Must be absolute, must exist, must be readable
- `exists` (boolean, derived): Whether the directory exists
  - Computed: `[[ -d "$path" ]]`
- `readable` (boolean, derived): Whether the directory is readable
  - Computed: `[[ -r "$path" ]]`

**Lifecycle**: Checked on each skill invocation

**Relationships**: Contains zero or more Screenshot Files

---

### Skill Configuration

**Description**: Optional user configuration stored in JSON file

**Storage Location** (precedence order):
1. `~/.config/opencode/skills/screenshot-analysis/config.json` (OpenCode)
2. `~/.claude/skills/screenshot-analysis/config.json` (Claude Code)

**Schema**:
```json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "screenshot_dir": {
      "type": "string",
      "description": "Custom screenshot directory path (absolute or ~-expanded)",
      "examples": [
        "/home/user/Pictures/Screenshots",
        "~/Screenshots",
        "/mnt/storage/screenshots"
      ]
    }
  },
  "additionalProperties": false
}
```

**Attributes**:
- `screenshot_dir` (string, optional): Custom directory path
  - Default: `~/Pictures/Screenshots` if omitted or file doesn't exist
  - Validation: Must resolve to valid directory path after tilde expansion

**Lifecycle**: Loaded once per skill invocation

**Validation Rules**:
- If config file missing → Use default, no error
- If config file malformed JSON → Log warning, use default
- If `screenshot_dir` present but invalid path → Error with actionable message
- If `screenshot_dir` omitted → Use default

**Example Valid Configs**:
```json
// Minimal (use all defaults)
{}

// Custom directory
{
  "screenshot_dir": "/home/user/Documents/Screenshots"
}

// Tilde expansion supported
{
  "screenshot_dir": "~/Pictures/MyScreenshots"
}
```

**Example Invalid Configs**:
```json
// Invalid: not an object
"~/Pictures"

// Invalid: wrong type
{
  "screenshot_dir": 12345
}

// Invalid: extra fields (allowed but ignored per additionalProperties: false)
{
  "screenshot_dir": "~/Pictures/Screenshots",
  "unknown_field": "value"
}
```

---

## Data Flow

```
1. Skill Invoked
   ↓
2. Load Configuration
   - Check ~/.config/opencode/skills/screenshot-analysis/config.json
   - Check ~/.claude/skills/screenshot-analysis/config.json
   - Parse JSON (jq)
   - Extract screenshot_dir or use default
   ↓
3. Validate Directory
   - Expand tilde (~) to absolute path
   - Check directory exists ([[ -d ]])
   - Check directory readable ([[ -r ]])
   ↓
4. Discover Screenshot Files
   - Find regular files (exclude symlinks)
   - Filter by extension (.png, .jpg, .jpeg)
   - Get modification times (stat -c '%Y %n')
   ↓
5. Sort by Recency
   - Primary: modification time (newest first)
   - Tiebreaker: lexicographic filename (Z > A)
   ↓
6. Select File(s)
   - Latest: First result (head -1)
   - Nth: Nth result (sed -n "${N}p")
   - Time-filtered: All matching time constraint
   ↓
7. Return File Path(s)
   - Absolute path(s) to agent
   - Agent passes to image analysis
```

---

## Validation Rules Summary

| Entity | Field | Validation | Error Behavior |
|--------|-------|------------|----------------|
| Config File | JSON format | Valid JSON | Warn + use default |
| Config File | screenshot_dir | Absolute/expandable path | Error if invalid |
| Screenshot Dir | path | Exists + readable | Error with message |
| Screenshot File | path | Regular file (not symlink) | Skip (filter) |
| Screenshot File | format | PNG/JPG/JPEG extension | Skip (filter) |
| Screenshot File | modification_time | Valid Unix timestamp | Skip (malformed) |

---

## Edge Cases

### Empty Directory
- **Scenario**: Screenshot directory exists but contains no screenshot files
- **Behavior**: Return empty result (not an error)
- **Message**: "No screenshots found in {directory}"

### Permission Denied
- **Scenario**: Screenshot directory exists but is not readable
- **Behavior**: Error with actionable message
- **Message**: "Directory not readable (permission denied): {directory}"

### Symlinks Present
- **Scenario**: Directory contains symlinks to screenshot files
- **Behavior**: Symlinks are filtered out (skip)
- **Validation**: `find -type f` excludes symlinks automatically

### Same Timestamp
- **Scenario**: Multiple files have identical modification times
- **Behavior**: Use lexicographic filename ordering as tiebreaker
- **Example**: Given `a.png` and `z.png` both at timestamp 1731078622, `z.png` is selected (Z > A)

### Malformed Config
- **Scenario**: config.json exists but contains invalid JSON
- **Behavior**: Log warning, fall back to default directory
- **Message**: "Warning: Failed to parse config.json, using default directory"

### Missing jq
- **Scenario**: `jq` command not found on system
- **Behavior**: Log warning, fall back to default directory
- **Message**: "Warning: jq not found, using default directory"

---

## Constraints

**Performance**:
- File discovery must complete in <1 second for 1000 files (SC-002)
- Config loading must be negligible (<10ms)

**Storage**:
- No persistent storage (stateless skill)
- Config file size limited to 1KB (reasonable for JSON with single path field)

**Scalability**:
- Tested up to 1000 files (requirement)
- Degrades gracefully beyond 10,000 files (acceptable for screenshot directories)

---

## Assumptions

1. Screenshot files are regular files (not symlinks, special files, or directories)
2. Modification time accurately reflects screenshot recency (OS maintains mtime correctly)
3. File extensions (.png, .jpg, .jpeg) reliably indicate image format (case-insensitive)
4. Config file is user-managed (skill doesn't create or modify it)
5. Users won't have more than ~10,000 screenshots in a single directory

---

## Future Enhancements (Out of Scope for v1)

- **Screenshot Metadata Database**: Index screenshots for faster queries (>10k files)
- **Tag/Label Support**: Add metadata field for user-defined tags
- **Multi-Directory Search**: Support searching multiple directories
- **Format Conversion**: Support additional formats (WebP, BMP, TIFF)
- **Content-Based Search**: OCR text extraction, visual similarity
