Files
CableGUI/CLAUDE.md
mAi c690113ea1 docs: design v3 — global cable_types, device UNIQUE, delete guardrail
Tight pass on round-4 answers (single commit per head's request):

- cable_types is GLOBAL — drop project_id, UNIQUE(name). Migration 001
  seeds the 5 defaults once; POST /api/projects no longer seeds them.
  API moves to top-level /api/cable-types. Renaming/recolouring affects
  every project. CASCADE from projects does not touch cable_types.
- devices: UNIQUE (project_id, name) added.
- projects: drawing_name defaults to "<name>.excalidraw" server-side
  on POST when omitted; editable via PATCH.
- DELETE /api/projects/:pid requires ?confirm=<name>; server checks
  name match, returns 400 if missing or mismatched.
- io_markers: no type_id (Power-by-convention, UI soft-warn). Confirmed
  v0 stance.
- Bundles ignored on export — carries over from v2.
- §0 changelog rewritten as "what changed in v3 / what carried over".
- §2 schema rewritten; FK-shape paragraph updated to call out the one
  global table.
- §3 endpoints: cable-types moved to top level; POST/DELETE projects
  show new defaults + guardrail semantics.
- §4 export table notes cable_types pulled from global.
- §7 "edit cable type" flow gains the cross-project-effect banner +
  ON DELETE RESTRICT inline-error UX.
- §8 slice 1 rewritten: no per-project seeding; legend reads global.
- §9 all six v2 questions marked resolved with the v3 answer per item.
- Trailer changes to "DESIGN v3 READY — coder shift gated".
- CLAUDE.md mirrors: global cable_types, device UNIQUE per project,
  drawing_name default, delete guardrail.
2026-05-15 16:32:20 +02:00

149 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# mCables — Project Instructions
## Project Overview
Cable-management **framework** for m's setup. Each cable-managed environment
(LOFT, OFFICE, …) is a separate **mCables project**, and each project is
backed by exactly one Excalidraw drawing. The framework provides a visual
web interface backed by a Go HTTP API and SQLite, plus an export pipeline
that writes `.excalidraw` files via mExDraw.
**Memory group_id:** `mcables`
**No CLI.** Frontend-first — every interaction is through the visual
interface. The backend serves the UI and the API; there is no
`mcables` shell binary intended for humans.
## Goal
- A reusable framework for tracking devices, ports, cables, cable types,
bundles, frames — **scoped per project** (LOFT and OFFICE are separate
projects, each a separate drawing).
- A visual editor in the browser: switch projects, add frames/devices/ports,
click ports to wire up cables, pick cable types from a per-project legend.
- A one-way export from the DB to the corresponding `.excalidraw` drawing
on `mxdrw.msbls.de` whenever m clicks Export — DB is authoritative,
Excalidraw is the projection.
- Bundle detection: parallel cables along the same path within a project
get grouped + colour-bundled in the diagram.
## Architecture
| Layer | Tech | Notes |
|---|---|---|
| DB | SQLite | `./data/mcables.db` (project-local, gitignored). Driver: `modernc.org/sqlite` (cgo-free). |
| Backend | Go | `net/http` HTTP API + static frontend via `embed.FS`. Standard library + minimal deps. Single binary. |
| Frontend | Vanilla JS modules + SVG, no build step | TypeScript types via JSDoc, optional `tsc --noEmit` in CI. Preact-via-CDN-ESM is the documented fallback if vanilla state gets painful — no build step either way. |
| Diagram I/O | mExDraw HTTP API | `PUT https://mxdrw.msbls.de/api/drawings/<name>.excalidraw` with `Authorization: Bearer $MEXDRAW_TOKEN`. (The `mcp__mexdraw__*` MCP tools are not currently configured for this project — workers use the raw HTTP API.) |
## Hierarchy
- **Project** (`projects` table) is the top-level concept. LOFT, OFFICE,
HOMELAB, … are separate projects. One project ↔ one `.excalidraw`
drawing in mExDraw. `projects.drawing_name` defaults to
`<name>.excalidraw` server-side when omitted on create; editable later.
- **Frames** sub-divide a project (LOFT has `desk`, `rack`, `media`;
OFFICE has `desk`, `server`). Frames are not projects — they're zones
within one drawing.
- Every device, port, cable, IO marker, and bundle is **project-scoped**
(`project_id` denormalised onto every row, with `ON DELETE CASCADE` from
`projects`). `UNIQUE (project_id, devices.name)` — no two devices in
one project share a name.
- **Cable types are global.** A single shared `cable_types` table —
no `project_id`. The five defaults (Power/USB/HDMI/DP/RJ45) are seeded
by migration 001 once, not per project. Renaming or recolouring a type
affects every project's legend immediately.
- **Project deletion guardrail.** `DELETE /api/projects/:pid` requires
`?confirm=<name>` matching the project's current name. 400 otherwise.
## Branch Strategy
- `main` = production-deployable.
- `mai/<worker>/<slug>` = worker branches via the mai workflow.
- No `dev` branch — too small a project for staging.
- Merge with `--no-ff` to main, delete branches after merge.
## Tech Stack
- **Go** for the backend (matches m's other tools: `m`, `mai`,
youpcms, mExDraw).
- **SQLite** via `modernc.org/sqlite` (cgo-free → clean `scratch`/distroless
container, no toolchain pain).
- **mExDraw** via HTTP for diagram export. Never edit raw `.excalidraw`
files directly outside the mExDraw API.
- **Vanilla JS + SVG** for the frontend — no build step. JSDoc-typed.
## Deployment — mDock, raw docker (NOT Dokploy)
mCables runs on **mDock** (`192.168.178.131` on the LAN, Tailscale `mdock`)
as a **plain docker-compose service**. Dokploy is for public mlake/mRiver
stuff; mDock uses raw `docker compose` per the conventions of the existing
mDock services (mgreen, mgeo, msports-garmin, paperless, …).
- Repo layout on mDock: `/home/m/stacks/mcables/` with `docker-compose.yml`,
`data/` bind-mount, secrets in `/home/m/secrets/mcables/.env`.
- Image: `mgit.msbls.de/m/mcables:latest` (built and pushed by a Gitea
Actions workflow on push to `main`, runs on the self-hosted runner on
mDock with label `self-hosted:host`).
- Port mapping: `7777:7777`, exposed on the LAN — no reverse proxy.
- Restart policy: `unless-stopped`.
- LAN URL: `http://mdock:7777`.
- No auth — LAN-trusted.
Local dev (no Docker): `go run ./cmd/mcables` against `./data/mcables.db`.
## Seed drawing — visual grammar reference, **not** a runtime importer
`mxdrw.msbls.de/draw/Cable-Management.excalidraw` is **reference material
only**. mCables does **not** auto-ingest it. m will rebuild LOFT and OFFICE
from scratch inside the tool — the seed exists so the **exporter** mimics
its visual grammar:
- **Devices** = rectangles with a text label.
- **Ports** = small ellipses (~12×9) positioned on a device edge.
Positional, *not* containerId-bound. Stroke colour = cable type.
- **Cables** = arrows with `startBinding` / `endBinding` to ports or
devices or IO diamonds.
- **Cable types** = colour, with a legend at the top-left of the project's
first frame listing the project's cable_types.
- **IO markers** = small diamonds. Semantically **wall outlets / power
entry points** — a cable terminating at an IO marker means "this end is
plugged into a wall socket outside the diagram". They are *not*
inter-frame bridges and they do *not* pair up.
- **Frames** = sub-zones inside a project (`desk`, `rack`, `media`, …).
- **Lines** = decorative only (legend separators in the seed). Ignored on
export.
Legend colours (global, seeded once by migration 001):
| Type | Hex |
|---|---|
| Power | `#e03131` |
| USB | `#2f9e44` |
| HDMI | `#1971c2` |
| DP | `#9c36b5` |
| RJ45 | `#ffd500` |
## Out of scope (v0)
- Multi-user. mCables is m-only.
- Auth / sharing — LAN-trusted on mDock.
- Mobile / responsive — desktop browser only.
- Cable inventory beyond visual structure (no length, no purchase history,
no SKU). Strictly visual structure for v0.
- Import from `.excalidraw` at runtime. If a one-shot migration is ever
needed, a separate `mcables-migrate` CLI tool is the right shape, not a
hot API endpoint.
## Worker Preferences
- **First shift = inventor** (design pass): conventions, schema, API,
export pipeline, mDock deploy plan, UI flows, slices. Output:
`docs/design.md` + open questions for m.
- **Second shift = coder** (after m's go on the design): bootstrap repo
skeleton (Go module, SQLite migrations, server, exporter, frontend
scaffold). Take slices 14 first (project CRUD, frames/devices, ports
and cables, IO + cable-type editing); slice 5 (Excalidraw export) closes
the round-trip.
- Use **Sonnet** for both — greenfield, structure matters more than depth.