# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project intent `vibe_erp` is an **ERP framework** (not an ERP application) aimed at the **printing industry**. The goal is a pluggable platform on which any printing business — with its own workflows, forms, roles, and rules — can be assembled by **configuration and plug-in modules**, not by forking or rewriting the core. The reference business in `raw/业务流程设计文档/` is **one example customer**, not the spec. It must be treated as a *fixture* to validate that the framework can express such a workflow, never as a source of hard-coded entities or features in the core. Top-level domains in the reference doc (use only as inspiration / test cases): `销售管理` (sales) · `采购管理` (purchasing) · `材料仓库管理` (raw material warehouse) · `成品仓库管理` (finished goods warehouse) · `生产管理` (production) · `工艺管理` (process/craft) · `车间管理` (workshop) · `设备管理` (equipment) · `品质管理` (quality) · `财务管理` (finance) · `项目管理` (project) ## Architectural guardrails These rules exist because the whole point of the project is reusability across customers. Violating them defeats the project. 1. **Core stays domain-agnostic.** No printing-specific entity, field, status, or workflow step belongs in the framework core. "Plate", "ink", "press", "color proof", etc. live in plug-in modules or configuration — never in core tables, core types, or core code paths. 2. **Workflows are data, not code.** State machines, approval chains, document routing, and form definitions must be declarative (config / DB / DSL) so a new customer can be onboarded by editing definitions, not by editing source. 3. **Extensibility seams come first.** Before adding any feature, identify the extension point (hook, plug-in interface, event, custom field, scripting hook) it should hang off of. If no seam exists yet, design the seam first, then implement the reference customer's behavior on top of it. 4. **The reference customer is a test, not a requirement.** When implementing something inspired by `raw/业务流程设计文档/`, ask: "Could a different printing shop with different rules also be expressed here without code changes?" If no, redesign. 5. **Multi-tenant from day one in spirit.** Even if deployment is single-tenant, data models and APIs should not assume one company, one workflow, or one form layout. 6. **Global / i18n from day one.** This codebase is the foundation of an ERP/EBC product intended to be sold worldwide. No hard-coded user-facing strings, no hard-coded currency, date format, time zone, number format, address shape, tax model, or language. All such concerns must go through localization, formatting, and configuration layers — even in the very first prototype. The reference docs being in Chinese does **not** mean Chinese is the primary or default locale; it is just one supported locale among many. 7. **Clean Core.** Borrowed from SAP S/4HANA's vocabulary: extensions **never modify the core**. The core is stable, generic, and upgrade-safe; everything customer-specific lives in plug-ins or in metadata rows. Extensions are graded A/B/C/D by upgrade safety: - **A** = Tier-1 metadata only (custom fields, forms, workflows, rules) — always upgrade-safe - **B** = Tier-2 plug-in using only the public `api.v1.*` surface — safe within a major version - **C** = Tier-2 plug-in using deprecated `api.v1` symbols — safe until next major; loader emits warnings - **D** = Tier-2 plug-in reaching into `platform.*` or `pbc.*.internal.*` via reflection — UNSUPPORTED, rejected by the plug-in linter at install time 8. **Two-tier extensibility, both first-class.** The framework offers two extension paths and both must be designed for, not bolted on: - **Tier 1 — key user / no-code:** business analysts add fields, forms, list views, workflows, automation rules, custom entities, reports, and translations through the web UI. Everything is stored as rows in the `metadata__*` tables (Doctype-style), tagged `source = 'user'`, scoped to their tenant. No build, no restart. The OpenAPI spec, REST API, and AI-agent function catalog auto-update from the metadata. - **Tier 2 — developer / pro-code:** PF4J JAR plug-ins. Plug-ins see only `org.vibeerp.api.v1.*`. Importing anything from `platform.*` or any PBC's internal package fails the plug-in linter. 9. **PBC boundaries are sacred.** Each core capability (`pbc-identity`, `pbc-catalog`, `pbc-inventory`, `pbc-orders-sales`, `pbc-production`, …) is its own **Packaged Business Capability**: own Gradle subproject, own table-name prefix, own bounded context, own public API surface. **PBCs never import each other.** Cross-PBC interaction goes through (a) the event bus or (b) service interfaces declared in `api.v1.ext.`. The Gradle build enforces this — `pbc-orders-sales` cannot declare `pbc-inventory` as a dependency. This is what makes "modular monolith now, splittable later" real instead of aspirational. 10. **`api.v1` is the only stable contract.** The package `org.vibeerp.api.v1.*` is the single semver-governed surface that plug-ins consume. It is published as `api-v1.jar` to Maven Central. Everything else in the codebase (`platform.*`, `pbc.*.internal.*`, every concrete Spring bean) is internal and may change in any release. Adding to `api.v1` is allowed within a major version; renaming, removing, or behavior-changing anything in `api.v1` is a major version bump. When in doubt, keep things OUT of `api.v1`; it is easier to grow the API deliberately than to maintain a regretted symbol forever. 11. **AI agents are a first-class client.** The framework is built so that the same business operations exposed via REST/OpenAPI are also callable by LLM-driven agents through an MCP server (or equivalent function-calling surface). The MCP endpoint itself is a v1.1 deliverable, but v1.0 must not architect it out — operations must be discoverable, typed, permission-checked, and locale-aware in a way that lets an agent call them safely. AI agents are listed alongside humans, web clients, mobile clients, and integrations as supported callers. ## Working with the reference docs - `raw/业务流程设计文档/` is in **Chinese**. Module and process names are domain terms (e.g. `工艺` = process/craft, `车间` = workshop floor, `品质` = QC). Preserve original terminology when quoting; do not silently rename concepts when discussing them. - Treat these documents as **read-only reference material**. Do not move, restructure, or "clean up" `raw/`. - When the user asks for a feature "from the doc", confirm whether they want it built as a **reference plug-in / fixture** on top of the framework, or as a **core capability** — the answer is almost always the former. ## Documentation discipline Because this is a framework that other developers (and future-you) will build on, **documentation is part of "done"**, not an afterthought. As the repo grows: - Maintain a top-level `docs/` tree alongside the code. At minimum it should cover: architecture overview, extension points / plug-in API, workflow & form definition format, configuration reference, i18n/localization guide, and a getting-started guide for onboarding a new customer. - Every new extension point, plug-in interface, config key, or workflow primitive added to the core **must** ship with documentation in the same change. A PR that adds a public seam without docs is incomplete. - Keep `raw/业务流程设计文档/` separate from `docs/`. `raw/` is one customer's business reference (read-only fixture). `docs/` is the framework's own documentation, written in English by default with localization where appropriate. - When a doc and the code disagree, treat it as a bug — fix whichever is wrong, do not silently let docs rot. ## Stack and shape (decided, not yet built) The architecture has been brainstormed, validated against 2026 ERP/EBC SOTA, and approved. The full design lives at `docs/superpowers/specs/2026-04-07-vibe-erp-architecture-design.md` and is the source of truth for everything below. - **Backend:** Kotlin on the JVM, Spring Boot, single fat-JAR / single Docker image - **Workflow engine:** Embedded Flowable (BPMN 2.0) - **Persistence:** PostgreSQL (the only mandatory external dependency) - **Multi-tenancy:** row-level `tenant_id` + Postgres Row-Level Security (defense in depth, same code path for self-host and hosted) - **Custom fields:** JSONB `ext` column on every business table, described by `metadata__custom_field` rows; GIN-indexed - **Plug-in framework:** PF4J + Spring Boot child contexts (classloader isolation per plug-in) - **i18n:** ICU MessageFormat (ICU4J) + Spring `MessageSource` - **Reporting:** JasperReports - **Auth:** Built-in JWT + OIDC (Keycloak-compatible) - **Web client (v1):** React + TypeScript SPA; **mobile (React Native) is v2** - **API:** REST + OpenAPI; MCP server is v1.1 (seam exists in v1.0) **Topology:** one Spring Boot process per instance, one Docker image per release, one mounted volume (`/opt/vibe-erp/`) for `config/`, `plugins/`, `i18n-overrides/`, `files/`, `logs/`. Customer extensions live **outside** the image. Upgrading vibe_erp = swapping the image; extensions and config stay put. **Module layout (Gradle multi-project):** ``` vibe-erp/ ├── api/api-v1/ ← THE CONTRACT (semver, published to Maven Central) ├── platform/* ← Framework runtime (internal, not exposed to plug-ins) ├── pbc/* ← Core PBCs: identity, catalog, partners, inventory, │ warehousing, orders-sales, orders-purchase, │ production, quality, finance ├── reference-customer/ │ └── plugin-printing-shop/ ← Reference plug-in expressing raw/业务流程设计文档/. │ Built and CI-tested; NOT loaded by default. ├── web/ ← React + TypeScript SPA └── docs/ ← Framework documentation ``` **Dependency rule (enforced by the Gradle build):** ``` api/api-v1 depends on: nothing (Kotlin stdlib + jakarta.validation only) platform/* depends on: api/api-v1 + Spring + libs pbc/* depends on: api/api-v1 + platform/* (NEVER another pbc) plugins (incl. ref) depend on: api/api-v1 only ``` ## Repository state The repository currently contains the reference business documents under `raw/`, this `CLAUDE.md`, and the architecture spec at `docs/superpowers/specs/2026-04-07-vibe-erp-architecture-design.md`. **There is no source code, build system, package manifest, test suite, or CI configuration yet.** The next concrete step is an implementation plan, then bootstrapping the Gradle multi-project skeleton. When source code is added, this file should be updated with: build/test/lint commands, the actual package name root (`org.vibeerp` is assumed but not yet committed), and any deviations from the design spec. ## What to do when asked to "just add" a printing-specific feature Stop and reframe. The correct shape is almost always: - a **generic mechanism** in the core (e.g. "documents with configurable line items and a configurable approval workflow"), plus - a **plug-in / config bundle** that uses that mechanism to express the specific printing-shop behavior. If you cannot see how to split a request that way, ask the user before writing code. ## The reference plug-in is the executable acceptance test `reference-customer/plugin-printing-shop/` is the framework's primary correctness test. It expresses the workflows in `raw/业务流程设计文档/` using **only `api.v1`**. Two consequences: - If a feature in `pbc/*` exists only to make this plug-in work, the design has failed guardrail #1 — that feature must move into the plug-in. - If the plug-in needs to reach into a `platform.*` or `pbc.*` internal class, the seam is wrong and `api.v1` needs to grow (deliberately, with a version bump consideration). CI must build and load this plug-in in an integration test environment against a real Postgres on every PR. A green core build with a red printing-shop integration test is a release-blocker.