This document provides build commands, testing procedures, and code style guidelines for agentic coding assistants working in this monorepo.
Always use bun instead of npm or yarn
# Build all packages and apps
bun turbo run build
# Run all linters across the project
bun turbo run lint
# Check TypeScript types everywhere
bun turbo run check-types
# Format code
bun run format
# Run development servers for all apps
bun turbo run dev --parallel
# Validate deployment lane compatibility (Vercel + AWS)
bun run check:deploy:matrix- Treat deployment config as lane-based. Use
DEPLOY_TARGET=vercelon Vercel andDEPLOY_TARGET=awson AWS. - Keep platform differences in environment variables and infra files (
infra/**, rootDockerfile), not in shared hardcoded app behavior. - Do not hardcode production
localhostin rewrites or API targets. - Use
API_PROXY_TARGET(PSV rewrite target),NEXT_PUBLIC_API_URL(browser API URL), andDOCS_URL/NETWORK_EDITOR_URL/PSV_URL/DESIGN_AGENTS_URL(web cross-app rewrites). - For any deployment-related change, run
bun run check:deploy:matrixbefore merging.
# Build specific app (run from app directory)
bun run build
# Run development server
bun run dev
# Lint code
bun run lint
# Type check
bun run check-types
# Run tests (Vitest)
bun run test # Watch mode
bun run test:run # Single run
# Run a single test file
TMPDIR=./.tmp vitest run path/to/test.test.tsx
# Run tests matching a pattern
TMPDIR=./.tmp vitest run -t "test name or pattern"# From package directory
ruff check . # Lint
ruff format . # Format
mypy # Type checking
pytest # Run tests
pytest -v tests/test_specific.py # Run single test file
pytest tests/test_specific.py::test_name # Run specific test
pytest -k "pattern" # Run tests matching pattern- Imports: Named exports only (
export {MyClass}), no default exports. Use ES6 modules - Variables:
constby default,letonly when reassignment is needed. Nevervar - Types: Strict mode enabled. Avoid
any; preferunknownor specific types. Be explicit for complex types - Units/UOM: Never hardcode unit conversions. Always use
convertUnitfrompackages/physics-engine/src/unitConversion.ts. For display unit selection in forms, use@eng-suite/engineering-units— see UoM System section below - Formatting: Explicit semicolons, single quotes for strings, template literals for interpolation
- Naming:
- Classes/Interfaces/Types:
UpperCamelCase - Variables/Functions:
lowerCamelCase - Constants:
CONSTANT_CASE - No underscore prefixes for private properties (use
privatemodifier)
- Classes/Interfaces/Types:
- Classes: Use
readonlyfor properties never reassigned outside constructor. Useprivate/protectedover defaultpublic - Equality: Always use
===and!==, never==or!=
- State Management: Use Zustand for global state, React Context for app-wide settings (e.g., Theme)
- UI Components: Use either Material UI (
sx-driven) or shadcn/ui + Tailwind, based on module-scoped guidance. Follow the nearest scopedAGENTS.mdfor the app you are modifying - Glassmorphism: Import from
@eng-suite/ui-kit:glassPanelSxfor panelsliquidGlassBorderSxfor bordersglassInputSx,glassSelectSx,glassRadioSxfor form elements
- Theming: Always use
theme.palette.modefor conditional dark/light styles- Dark:
rgba(255, 255, 255, 0.1)backgrounds - Light:
rgba(0, 0, 0, 0.05)backgrounds
- Dark:
- Input Panels: Use iOS-style deferred commit inputs in Network Editor:
IOSQuantityPage- Numbers with/without units (Enter commits, Escape reverts)IOSTextInputPage- Text input with deferred commitIOSPickerPage- Selection, commits on select- Required prop:
onBack={() => navigator.pop()} - Avoid
IOSNumberInputPagein new code
- Testing: Vitest with jsdom environment, Testing Library for component tests
- No comments: Do not add comments unless explicitly requested
- Imports: Separate lines, grouped: standard library, third-party, local
- Formatting: 4-space indentation (no tabs), 80-100 char line limit (ruff: 100)
- Naming:
- Classes:
PascalCase - Functions/Variables/Modules:
snake_case - Constants:
ALL_CAPS_WITH_UNDERSCORES - Private:
_leading_underscore
- Classes:
- Type Annotations: Required for all public APIs. Use Python 3.10+ type syntax
- Exceptions: Never use bare
except:. Use specific exceptions - Docstrings: Triple double quotes
"""with one-line summary,Args:,Returns:,Raises:sections - No comments: Do not add comments unless explicitly requested
- Inputs: Real-time validation with visible error highlighting
- Warnings: Show warnings for physically possible but suspicious values
- Feedback: Provide detailed context explaining physical/regulatory basis for errors
- Boundaries: Use Pydantic models for Python validation; TypeScript types + runtime checks for frontend
- Use
describe,it,expectfromvitest - Use
@testing-library/reactfor component tests - Mock functions with
vi.fn() - Test user interactions with
@testing-library/user-event
- Use descriptive test names:
test_specific_scenario - Create helper functions (
make_fluid(),make_section()) for test fixtures - Use
conftest.pyfor shared fixtures - Write tests for edge cases and error conditions
- Test both success and failure paths
- Engineering calculations: <100ms feedback on input changes
- PDF generation: <2 seconds
- Diagramming: Maintain 60fps with large networks
- Optimize calculations to prevent blocking the UI
- Models: Typed dataclasses for fluids, components, pipe sections, outputs, networks
- Calculators: Dedicated calculator per loss component (friction, fittings, elevation, valves, orifices, user loss)
- Solver: Coordinates per-section calculations, aggregates totals, propagates inlet/outlet states
- IO Layer: YAML config input, JSON/CSV output
- Extension: Add new calculators by implementing
LossCalculatorprotocol
- App Router: Next.js App Router for routing
- State: Zustand stores with actions for updates
- API: REST endpoints from FastAPI backend
- Shared Types: Define in shared packages (
packages/physics-engine/src/types.ts)
apps/: Next.js applications (docs, network-editor, psv, venting-calculation, web)packages/: Shared packages (api-client, api-std, engineering-units, eslint-config, physics-engine, tsconfig, types, typescript-config, ui, ui-kit, unit-converter)conductor/: Development guidelines and track metadatainfra/: Docker and infrastructure configsservices/: Backend services (api, calc-engine, design-agents)
When making changes, always match the existing code style and patterns in the file you're editing. Run bun run lint (TypeScript) or ruff check (Python) before committing.
This document defines architecture, execution model, build commands, testing procedures, and code style rules for automated coding agents (Codex) and human contributors working in this monorepo.
This file is authoritative. If any other document conflicts with it, this file wins.
Engineering equations should live in Python by default.
- TypeScript is primarily for UI, orchestration, APIs, persistence, and visualization
- Python (
services/calc-engine) remains the preferred location for shared, high-risk, or cross-domain engineering logic - Module-scoped exceptions are allowed when explicitly documented in the nearest scoped
AGENTS.md, including:- scope and ownership
- verification strategy
- performance and traceability constraints
UI (TypeScript)
│
▼
API / Orchestrator (TypeScript)
│ JSON / OpenAPI
▼
Calculation Engine (Python)
│
▼
Engineering Database
| Layer | Language | Responsibilities | Explicitly Forbidden |
|---|---|---|---|
| UI / Editors | TypeScript | Input forms, visualization, interaction | Undocumented engineering math without scoped exception and verification |
| API / Orchestrator | TypeScript | Auth, RBAC, run lifecycle, persistence, reporting | Unverified equations and correction factors |
| Calculation Engine | Python | All engineering equations, standards, checks | UI logic, auth |
| Database | SQL / Object | Inputs, outputs, provenance, versions | Hidden logic |
- All new calculator save/load work must use the shared calculation persistence model in
services/api. - The current snapshot belongs in
calculations. - Immutable audit and restore history belongs in
calculation_versions. - New calculator apps must not introduce app-specific primary save/load tables or a parallel database persistence model without an explicit scoped exception documented in the nearest
AGENTS.md. - File import/export is allowed as a transport, but it must map into the same canonical saved payload shape used by the shared calculations API.
All engineering calculations use a worker-based execution model to ensure scalability and predictable performance.
- API threads should avoid long-running engineering calculations unless a scoped exception defines bounded execution.
- Python calculations run in isolated worker processes by default.
- Concurrency is explicitly bounded
- Excess requests are queued, not blocked or rejected
UI (TypeScript)
│
▼
API / Orchestrator (TypeScript)
│ - validate schema
│ - persist input snapshot
│ - assign calc_id + version
▼
Job Dispatch
│
▼
Python Worker Pool (multiprocess)
│ - deterministic calculation
│ - units, correlations, checks
│ - results + provenance
▼
API Persistence & Reporting
- Process-based workers (not threads)
- Typical sizing:
- 1 worker per CPU core
- Start with 2–4 workers per instance
- Queueing is acceptable and preferred over blocking
Engineers may see:
“Calculating… (queued)”
This is expected behavior.
| Calculation Type | Mode |
|---|---|
| RO sizing, venting, valve sizing | Synchronous |
| Multiphase hydraulics, large networks | Asynchronous (job ID + polling) |
- Spawning Python per request
- Undocumented equation duplication across layers
- Blocking API threads with long calculations
- Duplicating formulas across languages
process-engineering-suite/
├─ apps/ # Frontend applications (Next.js)
│ ├─ docs/ # Documentation site
│ ├─ network-editor/ # Network topology editor
│ ├─ psv/ # PSV workflow app
│ ├─ venting-calculation/ # Tank venting calculator
│ └─ web/ # Dashboard UI
│
├─ services/ # Backend services (Python)
│ ├─ api/ # FastAPI REST API
│ ├─ calc-engine/ # Core engineering calculations (Python)
│ │ ├─ hydraulics/ # Network hydraulics
│ │ └─ pes_calc/ # PES calculation engine
│ └─ design-agents/ # AI design agents
│
├─ packages/ # Shared libraries
│ ├─ api-client/ # API client SDKs
│ ├─ api-std/ # API standards and shared contracts
│ ├─ engineering-units/ # ★ Shared UoM constants + store factory (@eng-suite/engineering-units)
│ ├─ eslint-config/ # ESLint shared configuration
│ ├─ physics-engine/ # Shared physics helpers + convertUnit
│ ├─ tsconfig/ # Shared tsconfig presets
│ ├─ types/ # Shared TypeScript types
│ ├─ typescript-config/ # TypeScript tooling defaults
│ ├─ ui/ # Shared UI primitives
│ ├─ ui-kit/ # Shared UI components
│ └─ unit-converter/ # Unit conversion (Python)
│
├─ docs/ # Architecture documentation
├─ infra/ # Docker and deployment
└─ AGENTS.md
Excel calculators being replaced:
- Hydraulics (single-phase via Network Editor, multi-phase via PES)
- Restriction orifice sizing
- Pump calculations
- Control valve sizing
- Vessel and tank sizing
- 2-phase and 3-phase separators
- API 2000 atmospheric / low-pressure tank venting
PSV sizing is handled by the PSV Suite and integrated, not re-implemented.
Always use bun. Never npm or yarn.
bun turbo run build
bun turbo run lint
bun turbo run check-types
bun run format
bun turbo run dev --parallel
bun run check:deploy:matrix- Always preserve both deployment lanes. Configure lane intent via
DEPLOY_TARGET:vercelfor Vercel projectsawsfor AWS deploymentslocalfor local development
- Avoid lane regressions by keeping platform targets env-driven:
NEXT_PUBLIC_API_URLAPI_PROXY_TARGETDOCS_URL,NETWORK_EDITOR_URL,PSV_URL,DESIGN_AGENTS_URL
- Before shipping deployment changes, run
bun run check:deploy:vercelandbun run check:deploy:aws(orbun run check:deploy:matrix).
bun run build
bun run dev
bun run lint
bun run check-types
bun run test
bun run test:runruff check .
ruff format .
mypy
pytest
pytest -v tests/test_specific.py- Named exports only (no default exports)
constby default, novar- Strict typing; avoid
any - Never hardcode unit conversions; use
convertUnitfrompackages/physics-engine/src/unitConversion.ts - For user-selectable display units in forms, use
@eng-suite/engineering-units— see UoM System section - Explicit semicolons, single quotes
===/!==only- No comments unless explicitly requested
- Zustand for global state
- Material UI (MUI) with
sx - Use
@eng-suite/ui-kitglass styles - iOS-style deferred commit inputs in Network Editor
- Vitest + Testing Library
- 4-space indentation, max 100 chars
- Type annotations required for public APIs
- No bare
except - Docstrings required
- No comments unless explicitly requested
- Golden-case regression tests derived from Excel are mandatory
- Numerical tolerance must be explicit
- CI must fail if results change without a version bump
- API records
calc_id,calc_version,schema_versionfor every run
- Most calculations: < 1 second
- Complex cases: 1–3 seconds
- Queueing acceptable
- Deterministic results > speed
When generating or modifying code:
- Prefer putting engineering logic in
services/calc-engine/unless a scoped exception exists. - Put UI, orchestration, persistence in
apps/orpackages/ - Respect the worker execution model by default
- Do not add engineering math to TypeScript unless the nearest scoped
AGENTS.mdexplicitly allows it - Never duplicate calculation logic without a documented reason and verification plan
Violations introduce latency, inconsistency, and loss of trust.
All user-selectable unit-of-measure logic for frontend apps lives in:
packages/engineering-units (@eng-suite/engineering-units)
import {
UOM_OPTIONS, // available units per category (12 categories)
BASE_UNITS, // canonical base unit per category (always stored in form)
UOM_LABEL, // ASCII key → unicode display label ('C' → '°C')
type UomCategory, // keyof UOM_OPTIONS
createUomStore, // Zustand store factory with persist + migrate
} from '@eng-suite/engineering-units'| Rule | Detail |
|---|---|
| Form state → base units always | mm, kPag, C, m3/h, Nm3/h, … |
| Conversion is UI-only | Happens inside UomInput component, never at the API boundary |
| Zod validation → base units | Ranges stay consistent; no schema changes when adding display units |
| Unit keys are ASCII | m3/h, Nm3/h, C, kg/cm2g — never unicode superscripts |
- Add
"@eng-suite/engineering-units": "*"to the app'spackage.json - Add tsconfig path alias:
"@eng-suite/engineering-units": ["../../packages/engineering-units/src/index.ts"]
- Create
src/lib/uom.ts— re-export from@eng-suite/engineering-units, add any app-specific constants - Create
src/lib/store/uomStore.ts:import { createUomStore } from '@eng-suite/engineering-units' import { BASE_UNITS } from '../uom' export const useUomStore = createUomStore('my-app-uom-prefs', BASE_UNITS)
- Copy
UomInput.tsxfromapps/venting-calculationand adjust the RHF form type - Run
bun installat repo root to link the package
apps/venting-calculation is the canonical example app with a complete UoM implementation.
PES is an engineering system, not a spreadsheet replacement app.
Correctness, traceability, scalability, and reproducibility take priority over convenience or development speed.