355 lines
12 KiB
Markdown
355 lines
12 KiB
Markdown
# AGENTS.md
|
|
|
|
## Project Goal
|
|
|
|
Maintain and extend a spreadsheet-like electrical distribution board circuit list editor.
|
|
|
|
The editor is used for electrical planning in execution design.
|
|
|
|
It must support circuits, device rows, project devices, drag-and-drop restructuring, stable equipment identifiers and later electrical sizing logic.
|
|
|
|
## Current Supported Architecture
|
|
|
|
- Frontend: Next.js App Router under `src/app` with reusable editor modules under `src/frontend`.
|
|
- API: Express composition starts in `src/server/index.ts`.
|
|
- Domain rules: `src/domain`.
|
|
- Persistence: SQLite/Drizzle schemas, repositories and migrations under `src/db`.
|
|
- Primary editor route:
|
|
`src/app/projects/[projectId]/circuit-lists/[circuitListId]/tree-edit/page.tsx`.
|
|
- Primary editor component: `src/frontend/components/circuit-tree-editor.tsx`.
|
|
- Grid ownership, projection, insertion and safety rules live in the pure
|
|
`src/frontend/components/circuit-grid-*.ts` modules.
|
|
- Critical multi-write commands use injected transaction repositories with real
|
|
SQLite commit/rollback tests.
|
|
|
|
The supported runtime model is Circuit-First. The former Consumer UI and API are
|
|
removed. Retained `consumers` rows, migration mappings and reports are upgrade-only
|
|
data accessed by `npm run db:migrate:legacy-consumers`; do not build application
|
|
features on them.
|
|
|
|
See `docs/current-architecture.md` for the complete module and request flow.
|
|
|
|
## Critical Domain Rules
|
|
|
|
- A circuit is not the same thing as one device row.
|
|
- A circuit contains zero, one or multiple device rows.
|
|
- A single-device circuit may be displayed as one compact row.
|
|
- A multi-device circuit must be displayed with a circuit summary row and indented device rows.
|
|
- Equipment identifiers belong to circuits, not to every device row.
|
|
- Device rows inside a circuit do not have their own equipment identifiers.
|
|
- Protection and cable data belong to the circuit, not to individual devices.
|
|
- Device-level values include quantity, power per unit, simultaneity factor, cosPhi, room data, cost group and category.
|
|
- Circuit total power is the sum of all device rows in that circuit.
|
|
- Existing equipment identifiers must never be changed automatically.
|
|
- Renumbering is always an explicit user action.
|
|
|
|
## Existing Codebase Rule
|
|
|
|
There is an existing codebase.
|
|
|
|
Do not blindly continue old assumptions.
|
|
|
|
Do not blindly delete the codebase.
|
|
|
|
First audit current code against the specification.
|
|
|
|
Keep working parts if they match the target model.
|
|
|
|
Refactor or replace parts that conflict with the target model.
|
|
|
|
Before large changes, summarize:
|
|
|
|
- what is kept
|
|
- what is changed
|
|
- what is removed
|
|
- why
|
|
|
|
## Implementation Discipline
|
|
|
|
Work in phases.
|
|
|
|
Do not implement unrelated future features while working on a phase.
|
|
|
|
Before coding, briefly state:
|
|
|
|
- files to change
|
|
- data model impact
|
|
- UI impact
|
|
- risks
|
|
|
|
Prefer small, reviewable changes.
|
|
|
|
Do not rewrite the whole app unless the current architecture blocks the required domain model.
|
|
|
|
## Naming
|
|
|
|
Use English names in code.
|
|
|
|
Use clear domain names:
|
|
|
|
- `ProjectDevice`
|
|
- `DistributionBoard`
|
|
- `CircuitSection`
|
|
- `Circuit`
|
|
- `CircuitDeviceRow`
|
|
- `equipmentIdentifier`
|
|
- `displayName`
|
|
- `phaseType`
|
|
- `quantity`
|
|
- `powerPerUnit`
|
|
- `simultaneityFactor`
|
|
- `cosPhi`
|
|
- `rowTotalPower`
|
|
- `circuitTotalPower`
|
|
|
|
Avoid ambiguous names like:
|
|
|
|
- `item`
|
|
- `thing`
|
|
- `entry`
|
|
- `rowData`
|
|
|
|
Use them only for local UI variables where the context is obvious.
|
|
|
|
## Numbering Rules
|
|
|
|
Default sections:
|
|
|
|
- Lighting: prefix `-1F`
|
|
- Single-phase circuits: prefix `-2F`
|
|
- Three-phase circuits: prefix `-3F`
|
|
|
|
New circuit identifier:
|
|
|
|
- highest existing number in the section + 1
|
|
|
|
Do not fill gaps automatically.
|
|
|
|
Do not renumber after insert, delete, move, sort or drag-and-drop.
|
|
|
|
Renumber only when the user explicitly triggers "Renumber section".
|
|
|
|
## UI Rules
|
|
|
|
The circuit list table should behave like a spreadsheet.
|
|
|
|
User-facing frontend text must be German unless a domain-standard technical term
|
|
is intentionally retained.
|
|
|
|
Cells show static text by default.
|
|
|
|
Inline edit starts by:
|
|
|
|
- double click
|
|
- Enter
|
|
- typing
|
|
- F2
|
|
|
|
Keyboard behavior:
|
|
|
|
- Enter confirms
|
|
- Escape cancels
|
|
- Tab / Shift+Tab navigates editable cells
|
|
- Arrow keys navigate cells when not editing
|
|
|
|
Support Ctrl+Plus and Ctrl+Shift+Plus for insertion.
|
|
|
|
## Bootstrap / Styling Rule
|
|
|
|
Bootstrap may be used for the general application UI, such as navigation, page layout, buttons, forms, cards, alerts and modals.
|
|
|
|
Do not use Bootstrap as the core table/grid framework for the circuit list editor.
|
|
|
|
The circuit list editor must remain a custom spreadsheet-like component with controlled cell selection, inline edit mode, row grouping, drag-and-drop indicators and keyboard behavior.
|
|
|
|
Avoid permanent Bootstrap form controls inside table cells. Table cells should show static text by default and switch to inputs only while editing.
|
|
|
|
## Drag-and-Drop Rules
|
|
|
|
Dragging from the circuit identifier / circuit handle moves the whole circuit.
|
|
|
|
Dragging from the device area moves device rows.
|
|
|
|
Project devices can be dragged from a sidebar into the circuit list.
|
|
|
|
Drop onto a free placeholder creates a new circuit.
|
|
|
|
Drop onto an existing circuit adds the device to that circuit.
|
|
|
|
Moving a device recalculates affected circuit totals.
|
|
|
|
Moving rows never renumbers circuits automatically.
|
|
|
|
Show clear visual drop indicators.
|
|
|
|
Reject invalid drop targets or require confirmation.
|
|
|
|
## Sorting and Filtering
|
|
|
|
Filtering should work through column headers.
|
|
|
|
Sorting should work through column headers.
|
|
|
|
Sorting moves complete circuits as blocks.
|
|
|
|
Sorting must not split device rows away from their circuit.
|
|
|
|
Sorting does not renumber.
|
|
|
|
The user may explicitly renumber after sorting.
|
|
|
|
## Linked Project Devices
|
|
|
|
Circuit device rows may be linked to project devices.
|
|
|
|
`displayName` is copied on insert but not synchronized automatically.
|
|
|
|
When a project device changes, show affected linked rows and let the user choose which fields to sync.
|
|
|
|
Never silently overwrite local changes.
|
|
|
|
Allow disconnecting linked rows from project devices.
|
|
|
|
## Manual Rows
|
|
|
|
Manual rows are allowed and common.
|
|
|
|
Manual rows can later be saved as project devices.
|
|
|
|
After saving, the row becomes linked to the new project device.
|
|
|
|
## Undo / Redo
|
|
|
|
The editor reads undo/redo eligibility from the project-wide server history on
|
|
initial load and after every tree reload. Undo/redo therefore remains available
|
|
after a page refresh or application restart. All currently supported Circuit
|
|
and CircuitDeviceRow writes execute persistent project commands. Applying a
|
|
sorted view across multiple sections is one atomic `circuit.reorder-sections`
|
|
command and one undo step. Explicit renumbering uses the collision-safe
|
|
`circuit.renumber-section` command and is never triggered implicitly.
|
|
Immutable revision metadata is available through the paginated
|
|
`GET /api/projects/:projectId/history/revisions` endpoint; it does not expose
|
|
stored command payloads.
|
|
Named logical snapshots can be created and listed through project-scoped API
|
|
endpoints. Their schema-versioned payload contains the complete supported
|
|
project runtime state and a SHA-256, excludes global/upgrade-only data and does
|
|
not change the project revision or undo/redo stacks. Restoring a server-stored
|
|
snapshot verifies its checksum and the current-state hash, replaces supported
|
|
project data atomically and records a new `restore` revision with a complete
|
|
inverse command. Restore can therefore be undone and redone after a restart.
|
|
The project page exposes an initially collapsed German snapshot/timeline UI
|
|
with explicit restore confirmation and cursor-based loading of older revision
|
|
metadata.
|
|
Insertions and generated move targets use client-generated stable UUIDs, and
|
|
undo restores the same ids from complete server snapshots. The tree response
|
|
supplies the optimistic `currentRevision`; obsolete direct field PATCH,
|
|
structure POST, move, reorder, renumber, identifier-restore, Circuit and
|
|
CircuitDeviceRow DELETE routes are removed.
|
|
CircuitDeviceRow moves between existing circuits and moves that create one new
|
|
placeholder target circuit are persisted. The latter stores the complete empty
|
|
target snapshot so undo can restore the rows and remove only the unchanged
|
|
generated circuit. Complete
|
|
in-section Circuit reorders are persisted separately and change sort positions
|
|
without changing equipment identifiers. Explicit complete-section renumbering
|
|
is persisted through a separate collision-safe command and is never triggered
|
|
by sorting or moving. Project-device synchronization, disconnect and reconnect
|
|
are persisted as one atomic multi-row command with complete expected/target row
|
|
snapshots, including link and override metadata. Canonical ProjectDevice field
|
|
updates are also persisted and never synchronize linked rows implicitly.
|
|
ProjectDevice insertion/deletion preserves stable device ids. Deletion captures
|
|
complete disconnected snapshots of linked rows so undo can restore only rows
|
|
that have remained unchanged. ProjectDevice create, update, delete and
|
|
global-to-project copy API/UI paths use these persistent commands and track the
|
|
returned project revision. ProjectDevice synchronization/disconnect API and UI
|
|
paths do the same; their undo action uses the project-wide history endpoint.
|
|
Project voltage settings use the persistent `project.update-settings` command.
|
|
Both voltage defaults change in one revision and Undo/Redo restores them
|
|
together; the project PUT route requires `expectedRevision`.
|
|
|
|
Required operations:
|
|
|
|
- insert circuit
|
|
- insert device
|
|
- delete circuit
|
|
- delete device
|
|
- move circuit
|
|
- move device
|
|
- multi-row move
|
|
- renumber section
|
|
- edit cell value
|
|
- edit equipment identifier
|
|
- synchronization changes
|
|
|
|
## Future Sizing
|
|
|
|
Do not implement full cable/protection sizing unless explicitly requested.
|
|
|
|
Keep the structure ready for it.
|
|
|
|
Future sizing will need:
|
|
|
|
- circuit total power
|
|
- phase type
|
|
- voltage
|
|
- cosPhi
|
|
- cable length
|
|
- cable type
|
|
- cable cross-section
|
|
- protection rated current
|
|
- control requirement such as DALI
|
|
|
|
Users must be able to override sizing suggestions.
|
|
|
|
## Persistence and Migration Rules
|
|
|
|
- SQLite is the currently supported database.
|
|
- Never edit an already applied migration.
|
|
- Back up an existing database before applying a new migration.
|
|
- Inspect generated SQL; it must contain only the intended schema change.
|
|
- Keep database backups separate from logical project snapshots.
|
|
- Do not import the global SQLite singleton into domain services.
|
|
- Keep synchronous SQLite transaction behavior inside persistence adapters.
|
|
- Preserve stable UUIDs and explicit transaction boundaries for a later
|
|
PostgreSQL adapter.
|
|
|
|
## Current Deferred Work
|
|
|
|
- periodic snapshot and retention policy
|
|
- Revit/CSV/IFCGUID round-trip
|
|
- full electrical sizing
|
|
- multi-user/PostgreSQL operation
|
|
- supported production deployment
|
|
|
|
Do not implement these while working on an unrelated phase.
|
|
|
|
## Documentation and Verification
|
|
|
|
- `README.md` is the setup entry point.
|
|
- `docs/README.md` is the documentation map.
|
|
- `docs/current-architecture.md` describes current code paths.
|
|
- `docs/spec/` contains requirements and roadmap, not proof of implementation.
|
|
- `docs/archive/` is historical and must not drive current implementation.
|
|
|
|
For a normal code change run the relevant focused tests plus:
|
|
|
|
- `npm test`
|
|
- `npm run build:api`
|
|
- `npm run build:web`
|
|
- `npx tsc --noEmit -p tsconfig.next.json`
|
|
|
|
Use a concise imperative commit message for each completed, verified work package.
|
|
|
|
## Response Style for Codex
|
|
|
|
Be concise.
|
|
|
|
Do not restate the whole specification.
|
|
|
|
Reference the relevant file and section.
|
|
|
|
When uncertain, choose the simplest implementation that preserves the domain model.
|
|
|
|
Do not add libraries unless there is a clear reason.
|
|
|
|
Do not introduce global state management unless needed.
|