Tight pass on m's review of v4 (single commit per head's instruction). Six locked answers integrated: 1. mCables is a schematic, not a physical-routing tool. Stripped 'trunk', 'frame-edge corridor', 'cable tray', 'path optimisation' from §5b.1, §5b.2, §7, §8, §9. Bundling reduces to the v3 endpoint- pair rule: ≥2 cables between the same A↔B endpoint pair → group as one bundle. Anything path-shaped is "out of scope, period" (§8). 2. Solver button-only for v0 (no change). Live-solve parked at 9+. 3. Unmet-requirement quick-fix: red badge on the affected device in the inspector with a single "+ Add <type> port to <device> and re-solve" button per §5b.4. New endpoint POST /api/projects/:pid/devices/:id/ports-and-resolve chains the port insert + the solve re-run in one transaction. 4. Setup templates fold INTO v4.1. New §2.4 with the schema for setup_templates + setup_template_devices + setup_template_requirements (migration 004), 3 built-in templates seeded (Living Room, Home Office, Server Rack). New API: GET /api/setup-templates, POST /api/projects/:pid/apply-template. New UI flow: "or start from a template" section in the New Project modal + an "Apply template" action on empty projects. Built-in catalog grows to 14 types (adds Screen, Keyboard, Mouse). 5. Catalog SQL seed in migration 002 (no change). 6. Promote-to-manual: explicit button on cable inspector (no change). §8 slice 6 absorbs the templates work alongside the solver MVP. §9 closes all six v4 questions; no open design questions remain. Trailer changes to "DESIGN v4.1 READY FOR REVIEW". CLAUDE.md mirrors: schematic-only framing, 14-type catalog, setup templates as a first-class feature, quick-fix UX note.
188 lines
9.2 KiB
Markdown
188 lines
9.2 KiB
Markdown
# mCables — Project Instructions
|
||
|
||
## Project Overview
|
||
|
||
Cable-management **framework + solver** for m's setup. m declares his
|
||
**devices** and the **connection requirements** between them ("NAS must
|
||
connect to Switch via RJ45"). mCables runs a solver that emits the cable
|
||
plan + bundle recommendations. mCables is a **schematic**, not a
|
||
physical-routing tool — cables are straight lines between endpoints; the
|
||
"maximum bundling" objective is satisfied by the endpoint-pair rule
|
||
(when two or more cables share the same A↔B endpoint pair, group them
|
||
into one bundle). The visual editor is still there for tweaking the
|
||
plan, but the solver is the headline.
|
||
|
||
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 **solver** that, given the project's devices + connection
|
||
requirements, emits the cable plan + bundle recommendations.
|
||
Objective: maximum bundling via the endpoint-pair rule (schematic
|
||
only — no path/trunk/cable-tray modelling).
|
||
- A **hybrid device-type catalog**: 14 built-in types (NAS, PC, Mac,
|
||
Notebook, TV, Soundbar, Switch, fritz, ChromeCast, SteamLink,
|
||
IOx-3/6/8, Screen, Keyboard, Mouse) with default port profiles,
|
||
extensible per project. Picking a type on device-create seeds the
|
||
device's ports automatically; m overrides per instance.
|
||
- **Setup templates** for bootstrapping a project from blank to
|
||
solver-ready: built-ins 'Living Room', 'Home Office', 'Server Rack'
|
||
stamp their device-types + connection requirements in one transaction.
|
||
- A visual editor for switching projects, adding frames/devices,
|
||
declaring requirements, running the solver, and tweaking the
|
||
resulting plan. Unmet requirements get a one-click quick-fix
|
||
("+ Add <type> port to <device> and re-solve").
|
||
- 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.
|
||
|
||
## 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, bundle, and **connection
|
||
requirement** 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.
|
||
- **Device types are hybrid.** `device_types` is one global table with
|
||
`project_id` NULL for the 11 built-in catalog rows (seeded by
|
||
migration 002) and `project_id = current` for project-custom types.
|
||
Each `device_type` carries a `device_type_ports` profile that seeds
|
||
`ports` rows when a device of that type is created. m can extend the
|
||
catalog per project; built-ins are read-only from the API.
|
||
- **Connection requirements** (`connection_requirements` table) are the
|
||
solver's input. m declares "from_device ↔ to_device, preferred cable
|
||
type, must_connect"; the solver assigns ports and emits cables.
|
||
- **Project deletion guardrail.** `DELETE /api/projects/:pid` requires
|
||
`?confirm=<name>` matching the project's current name. 400 otherwise.
|
||
- **Solver-owned vs. user-owned cables.** `cables.auto = 1` = created by
|
||
the solver and replaceable on re-solve. `auto = 0` = hand-drawn by m,
|
||
left alone by the solver. PATCHing endpoint or type of an auto cable
|
||
promotes it to manual (explicit "Promote to manual" button in the
|
||
inspector, per design v4 §5b.3).
|
||
|
||
## 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
|
||
|
||
- **Inventor shifts** (design passes): conventions, schema, API, export
|
||
pipeline, mDock deploy plan, UI flows, slices. Output: `docs/design.md`
|
||
+ open questions for m. v1–v4 are versioned in the doc's header callout.
|
||
- **Coder shifts** (after m's go on a design version): build to the
|
||
current design.md. Current state: slice 1 (project CRUD + global
|
||
cable_types) and slice 2 (frames + devices + drag) are merged; design
|
||
v4 reshapes slices 3+ (IO + cable-type editing → device-type catalog →
|
||
device-type manage → connection-requirements UI → solver → manual
|
||
port/cable draw → export). See `docs/design.md` §8 for the current
|
||
sequence.
|
||
- Use **Sonnet** for both — structure matters more than depth.
|