# Plan: Emacs Integration for napi — Remote File-Notify via TRAMP

## Context

Fénix wants **real-time awareness in Emacs** of student deliveries on zzz (qu3v3d0.tech). The GNU Emacs manual confirms that `file-notify-add-watch` works on **remote machines** via TRAMP — it runs `inotifywait` on the remote host through SSH and streams events back in real-time. No polling.

This means: **no modifications to watcher scripts on zzz**, no reverse SSH tunnels, no Porthole/JSON-RPC. Pure Emacs, using built-in capabilities + a dedicated daemon.

## Architecture

```
emacs --daemon=napi (local)
    │
    ├── TRAMP SSH → zzz:/home/*/python/    (ASIR1 — 21 dirs)
    │   └── inotifywait running on zzz (auto-started by TRAMP)
    │
    ├── TRAMP SSH → zzz:/home/*/html/      (DDAW2 — 19 dirs)
    │   └── inotifywait running on zzz (auto-started by TRAMP)
    │
    └── On file event → forward notification to `water` daemon
        ├── Desktop notification (D-Bus)
        ├── Sound alert (paplay)
        ├── *napi-log* buffer (persistent log)
        └── Minibuffer message

emacsclient --socket-name=napi --eval '(napi-dashboard)'   ← interact
emacsclient --socket-name=water --eval '(napi-log)'        ← view in main UI
```

## Prerequisites

- `inotify-tools` installed on zzz (provides `inotifywait`) — **already present** (watchers use it)
- SSH key access from local → zzz — **already working** (`ssh fenix@qu3v3d0.tech`)
- Emacs 30.1 on local — **confirmed**

## Implementation Steps

### Step 1: Create `~/napi/emacs/napi.el`

The core elisp package. ~200 lines. Key components:

| Component | Purpose |
|:----------|:--------|
| `napi-watch-start` | Sets up TRAMP file-notify watches on all student dirs |
| `napi-watch-stop` | Removes all watches cleanly |
| `napi-notify` | Handles a file event: log + desktop notification + sound |
| `napi-log` | Interactive: open `*napi-log*` buffer |
| `napi-open-student` | Interactive: completing-read → open student dir/notas.md |
| `napi-dashboard` | Interactive: overview of all students + last delivery |
| `C-c n` prefix | Keybindings for all interactive commands |

**Student name maps** (username → full name) embedded in elisp, mirroring watcher scripts.

**TRAMP watch setup** — the core innovation:
```elisp
;; For each ASIR1 student:
(file-notify-add-watch
  "/ssh:fenix@qu3v3d0.tech:/home/jara/python/"
  '(change)
  #'napi--handle-event)

;; 40 watches total (21 ASIR1 + 19 DDAW2)
;; Each spawns a persistent inotifywait on zzz via SSH
```

**Event handler** filters noise (Thumbs.db, __pycache__, .tmp, etc.) and forwards clean notifications to `water` daemon via:
```elisp
(shell-command
  "emacsclient --socket-name=water --eval '(napi--show-notification ...)' &")
```

**Reconnection**: TRAMP sends a `stopped` event when SSH drops. The handler auto-re-establishes the watch after a delay.

### Step 2: Create systemd user unit for `napi` daemon

File: `~/.config/systemd/user/emacs-napi.service`

```ini
[Unit]
Description=Emacs daemon for napi student monitoring
After=network-online.target
Wants=network-online.target

[Service]
Type=forking
ExecStart=/usr/bin/emacs --daemon=napi -l ~/napi/emacs/napi-init.el
ExecStop=/usr/bin/emacsclient --socket-name=napi --eval "(kill-emacs)"
Restart=on-failure
RestartSec=30

[Install]
WantedBy=default.target
```

### Step 3: Create `~/napi/emacs/napi-init.el`

Minimal init file for the `napi` daemon (not the full `~/.emacs.d/init.el`):

```elisp
;; Loaded ONLY by emacs --daemon=napi
(load "~/napi/emacs/napi.el")
(napi-watch-start)
(message "napi: watching %d student directories on zzz" napi--watch-count)
```

### Step 4: Also load `napi.el` in `water` daemon

Add to `~/.emacs.d/init.el` (near line 1478, following `funciones-art.el` pattern):

```elisp
(load "~/napi/emacs/napi.el")
```

This gives the `water` daemon the `napi-log`, `napi-dashboard`, `napi-open-student` commands and the `C-c n` keybindings — but **no watches** (those run in the `napi` daemon only).

### Step 5: Verify `inotifywait` on zzz

```bash
ssh fenix@qu3v3d0.tech "which inotifywait && inotifywait --help | head -1"
```

## Files to Create/Modify

| File | Action | Location |
|:-----|:-------|:---------|
| `emacs/napi.el` | **CREATE** | `~/napi/emacs/napi.el` |
| `emacs/napi-init.el` | **CREATE** | `~/napi/emacs/napi-init.el` |
| `emacs-napi.service` | **CREATE** | `~/.config/systemd/user/emacs-napi.service` |
| `init.el` | **MODIFY** (1 line) | `~/.emacs.d/init.el` |

**No modifications to zzz** — no watcher changes, no new SSH keys, no reverse tunnels.

## Testing

1. **Unit test** — load `napi.el` in `water`, call `(napi-notify ...)` manually
2. **TRAMP watch test** — single watch on one student dir, touch a file on zzz
3. **Full integration** — start `napi` daemon, simulate SFTP upload as student
4. **Stress test** — confirm 40 concurrent TRAMP watches are stable
5. **Reconnection** — kill SSH, verify watch auto-restores
6. **UI test** — `C-c n l` (log), `C-c n s` (student), `C-c n d` (dashboard)

## Risk: 40 Concurrent SSH/inotifywait Processes

Each `file-notify-add-watch` via TRAMP spawns a separate `inotifywait` process on zzz. 40 watches = 40 persistent SSH channels + 40 inotifywait processes.

**Mitigation**: TRAMP reuses SSH connections (ControlMaster). So it's 1 SSH connection + 40 remote processes. The zzz server's `inotify` limit (`fs.inotify.max_user_watches`) is typically 65536 — 40 is nothing.

**Fallback**: If 40 watches proves unstable, consolidate to 2 watches on parent dirs (`/home/` with recursive) + filter events by path. Or switch to the emacsclient-via-SSH approach (see FINDINGS.md).
