# PLAN — Hermes Pipeline Tool

## Goal

Build a compact Vite + TypeScript single-page visual dataflow editor with a pure DAG library at `src/lib/dag.ts` for hidden acceptance tests. Optimize for correctness, clean separation, small bundle size, and usable interactions rather than heavyweight dependencies.

## Architecture

### 1. Pure graph core (`src/lib/dag.ts`)

- Export the exact required `NodeDef` interface and functions: `hasCycle`, `topoSort`, `wouldCreateCycle`, `evaluate`.
- Treat edges as `input -> dependent` because each node lists its dependency ids in `inputs`.
- Implement Kahn topological sort with maps/sets for O(V + E).
- `wouldCreateCycle(nodes, from, to)` simulates adding `from` to `to.inputs` and delegates to cycle detection.
- `evaluate` walks topological order and supports `const`, `add`, `mul`, `sub`, `neg`, plus UI-only aliases such as `output`, `slider`, `clamp`, `min`, and `max` without weakening the acceptance contract.
- Missing inputs evaluate to 0; cycles throw defensively.

### 2. Editor model (`src/lib/editorGraph.ts`, `src/lib/geometry.ts`)

- Keep UI graph state separate from the pure acceptance graph.
- Editor nodes store position, type/op, title, value, and port metadata.
- Connections store source node/port and target node/port.
- Conversion to `NodeDef[]` derives ordered dependency ids from incoming wires.
- Geometry helpers handle canvas/world transforms, port positions, wire paths, and hit testing.

### 3. App/view (`src/main.ts`, `src/styles.css`)

- No framework dependency: use vanilla TypeScript + Vite for a small bundle and full control.
- Render fixed chrome (palette/sidebar/toolbar) plus a transformed canvas layer.
- Nodes are absolutely positioned; wires render in SVG below nodes.
- Pointer interactions:
  - Add nodes from palette.
  - Drag nodes by header/body.
  - Connect output port to input port with snapping/preview.
  - Pan empty canvas by dragging; zoom with wheel around cursor.
  - Select node/wire; delete via Delete/Backspace.
- Inputs update node values and immediately re-evaluate downstream results.
- Output/display node shows its incoming computed value live.
- Save/load JSON with localStorage and import/export textarea for stretch value.

### 4. Performance hook (`src/lib/perf.ts` + registration in app)

- `window.__buildoff.warmup()` builds a roughly 300-node chain/mesh graph in memory.
- `window.__buildoff.workload()` mutates seed constants and evaluates 100 times, returning `{ nodes, evals, avgEvalMs }`.
- Use the pure DAG evaluator so the probe measures graph algorithm throughput.

## File layout

- `package.json`, `package-lock.json`: npm contract and reproducible installs.
- Copied shared configs: `eslint.config.js`, `tsconfig.json`, `.prettierrc.json`.
- `vite.config.ts`, `index.html`.
- `src/lib/dag.ts`: hidden-test acceptance API.
- `src/lib/editorGraph.ts`: editor node definitions, serialization, sample graph, conversion to DAG.
- `src/lib/geometry.ts`: transforms, SVG paths, port/wire hit testing.
- `src/lib/perf.ts`: buildoff warmup/workload hook.
- `src/main.ts`: app state, rendering, interactions.
- `src/styles.css`: polished, legible editor styling.
- `tests/dag.test.ts`: public-contract tests written before implementation.
- `tests/editorGraph.test.ts`: conversion/cycle guard tests.
- `REASONING.md`: running development log.
- `SELF.md`: final self-report after verification.

## Libraries

- Runtime: none beyond Vite browser platform. Avoid React/diagram libraries to keep bundle small and demonstrate graph architecture.
- Dev-only: Vite, TypeScript, Vitest, ESLint, Prettier, shared config dependencies.

## Test and verification strategy

1. Write local Vitest tests importing exactly `src/lib/dag.ts` and verify RED before implementation.
2. Implement DAG core and run targeted tests.
3. Add editor model tests for conversion and cycle refusal.
4. Build UI and run full contract:
   - `npm ci`
   - `npm run build`
   - `npm test`
   - `npm run lint`
   - verify `dist/` exists.

## Risks and mitigations

- Hidden tests may check exact edge semantics: define `from -> to` as dependency to dependent and test it explicitly.
- UI cycles can arise through multi-input operator wires: simulate the `NodeDef` addition before accepting any input connection.
- Bundle-size scoring: use vanilla TS and CSS, no canvas/graph dependencies.
- Complex pointer interactions can get brittle: centralize transform math and keep state transitions small.
- Strict TypeScript and lint warnings can hurt cleanliness: keep typed discriminated helper data and avoid non-null assertions/`any`.
