Files
leistungsbilanz-ts/AGENTS.md
T

14 KiB

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.
  • All supported runtime project-command stores, including full snapshot restoration, share src/db/repositories/project-command-transaction.persistence.ts for the atomic domain-write, revision and history transition boundary. Its applied forward-command variant preserves derived CircuitDeviceRow override metadata.
  • Low-level appendProjectRevision persistence is adapter-internal and tested directly; do not reintroduce a standalone runtime revision repository.
  • Runtime domain services receive narrow reader/store dependencies explicitly; concrete SQLite repositories are instantiated only under src/server/composition.
  • General application repositories also require an explicit AppDatabase; src/server/composition/application-repositories.ts owns their runtime instances, and controllers never import the global SQLite client.
  • The upgrade-only legacy migration service follows the same dependency rule; its concrete repositories are instantiated only in scripts/db-migrate-legacy-consumers.ts.
  • General Circuit, CircuitDeviceRow, CircuitList and DistributionBoard repositories expose only active reads. Runtime writes belong in typed command repositories; direct integration fixtures belong under tests/support.

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 central revision boundary creates an automatic logical snapshot after each 25 new revisions and retains only the newest 12 automatic snapshots per project. Named snapshots are never removed by this retention policy. The project page exposes persistent project-wide Undo/Redo in the header of 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 settings use the persistent project.update-settings command. Project metadata, both voltage defaults and the enabled distribution-board supply types change in one revision and Undo/Redo restores them together; the project PUT route requires expectedRevision. The system catalog is AV, SV, EV, USV, MSR, SiBe; at least one must be enabled and a type used by a board cannot be disabled. Distribution-board setup uses distribution-board.insert with a complete stable snapshot of the board, circuit list and four default sections. Its inverse removes only the same unchanged and still-empty structure; the POST route requires expectedRevision and returns the updated history state. Distribution-board floor assignment and a project-enabled supply type use distribution-board.update; both values are snapshot/export fields and one persistent undo step. Floor and room setup use project-floor.insert and project-room.insert with complete stable snapshots. Their inverses remove only unchanged records and reject floors with assigned rooms/distribution boards or rooms referenced by device rows or retained upgrade data. Both POST routes require expectedRevision and return the updated history state.

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

  • 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
  • npm run typecheck:scripts
  • 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.