vibe_erp — Project Progress
Snapshot of where the framework stands today, what's done, and what's next. The detailed plan of record is
docs/superpowers/specs/2026-04-07-vibe-erp-implementation-plan.md. The architecture is locked indocs/superpowers/specs/2026-04-07-vibe-erp-architecture-design.md.
At a glance
| Latest version | v0.32.0-SNAPSHOT (P3.1 dynamic form renderer + i18n + edit forms + double-entry GL + OIDC + S3) |
| Latest commit | b0b0183 feat(web): P3.1 dynamic custom field renderer |
| Repo | https://github.com/reporkey/vibe-erp |
| Modules | 25 JVM subprojects + :web SPA |
| Unit tests | 356, all green |
| Real PBCs implemented | 10 of 10 (pbc-identity, pbc-catalog, pbc-partners, pbc-inventory, pbc-warehousing, pbc-orders-sales, pbc-orders-purchase, pbc-finance, pbc-production, pbc-quality) — P5.x row of the implementation plan complete at minimal v1 scope |
| End-to-end smoke runs | An SO confirmed with 2 lines auto-spawns 2 draft work orders via SalesOrderConfirmedSubscriber; completing one credits the finished-good stock via PRODUCTION_RECEIPT, cancelling the other flips its status, and a manual WO can still be created with no source SO. All in one run: 6 outbox rows DISPATCHED across orders_sales.SalesOrder and production.WorkOrder topics; pbc-finance still writes its AR row for the underlying SO; the inventory__stock_movement ledger carries the production receipt tagged WO:<code>. First PBC that REACTS to another PBC's events by creating new business state (not just derived reporting state). |
| Plug-ins serving HTTP | 1 (reference printing-shop, 7 endpoints + own DB schema + own metadata + own i18n bundles) |
| Production-ready? | No. Foundation is solid; many capabilities still deferred. |
Current stage
Foundation complete; all platform services and core PBCs live; React SPA serving CRUD for every entity; Tier 1 customization rendering in the browser. All 12 cross-cutting platform services are live (9 Phase-1 + 3 auth/authz). All 10 core PBCs are implemented (6 feature-complete, 4 at minimal v1 scope). The full buy-sell-make loop works end-to-end: purchase receipt, sales shipment, production receipt, and material issue all feed the same inventory__stock_movement ledger. Event-driven cross-PBC integration is live in both directions — pbc-finance subscribes to all 6 order lifecycle events (confirm + ship/receive + cancel) and writes balanced double-entry journal entries with idempotent handling. The React SPA has create/edit/detail pages for every manageable entity, a dynamic custom-field renderer (P3.1), client-side i18n (en-US + zh-CN), and identity management screens.
The next phase focuses on Tier 1 designer UIs (form renderer, form designer, list view designer, rules engine) and workflow UIs (BPMN designer, user-task form rendering).
Total scope (the v1.0 cut line)
The framework reaches v1.0 when, on a fresh Postgres, an operator can docker run the image, log in, drop a customer plug-in JAR into ./plugins/, restart, and walk a real workflow end-to-end without writing any code — and the same image, against a different Postgres pointed at a different company, also serves a different customer with a different plug-in. See the implementation plan for the full v1.0 acceptance bar.
That target breaks down into roughly 30 work units across 8 phases. About 22 are done as of today, plus the event-driven cross-PBC integration follow-up that completes the P1.7 story. Below is the full list with status.
Phase 1 — Platform completion (foundation)
| # | Unit | Status |
|---|---|---|
| N/A — removed by single-tenant refactor | ||
| P1.2 | Plug-in linter (ASM bytecode scan) | ✅ DONE — 7af11f2
|
| P1.3 | Plug-in lifecycle: HTTP endpoints, DefaultPluginContext, dispatcher | ✅ DONE — 20d7ddc
|
| P1.4 | Plug-in Liquibase application + PluginJdbc
|
✅ DONE — 7af11f2
|
| P1.5 | Metadata store seeding | ✅ DONE — 1ead32d
|
| P1.6 | ICU4J Translator implementation + locale resolution |
✅ DONE — 01c71a6
|
| P1.7 | Event bus + transactional outbox | ✅ DONE — c2f2314
|
| P1.8 | JasperReports integration | ✅ DONE — 89f47a3 — new platform-reports module with JasperReports 6.21.3; api.v1 ReportRenderer + ReportRenderException; JasperReportRenderer @Component running JDK javac via JRJavacCompiler; PluginContext.reports wired with default-throw; /api/v1/reports/ping + /api/v1/reports/render endpoints; built-in vibeerp-ping-report.jrxml self-test |
| P1.9 | File store (local + S3) | ✅ DONE — f2156c5 local + c2fab13 S3 — api.v1 FileStorage + FileHandle + FileReadResult; platform-files with LocalDiskFileStorage (sidecar metadata, atomic put, path-traversal guards) + S3FileStorage (AWS SDK v2, works with AWS/MinIO/DigitalOcean Spaces). Backend selected by vibeerp.files.backend (local=default, s3=opt-in). S3 client is lazy-init, supports static credentials or IAM chain, configurable key-prefix for multi-instance buckets. PluginContext.files wired; FileController with multipart upload/download/list/delete. |
| P1.10 | Job scheduler (Quartz) | ✅ DONE — 5d6a2f1 — new platform-jobs module with Quartz Spring Boot starter + JDBC job store against host Postgres; api.v1 JobHandler/JobScheduler/JobContext; owner-tagged JobHandlerRegistry parallel to TaskHandlerRegistry; QuartzJobBridge routes by key; PrincipalContext.runAs("system:jobs:<key>") wraps every execution; HTTP surface at /api/v1/jobs/** (list/trigger/schedule/unschedule); built-in vibeerp.jobs.ping diagnostic handler |
Phase 2 — Embedded workflow engine
| # | Unit | Status |
|---|---|---|
| P2.1 | Embedded Flowable (BPMN 2.0) + TaskHandler wiring |
✅ DONE — 7bff422 core + ef9e5b4 principal propagation + a091d38 plug-in-loaded handler registration + 66ad87d plug-in JAR BPMN auto-deployment — platform-workflow module shares host Postgres; dispatcher routes service-task execution to TaskHandlerRegistry beans by activity id; authenticated HTTP caller's principal flows through reserved __vibeerp_* process vars into ctx.principal(); plug-ins register handlers via context.taskHandlers + ship BPMNs under processes/*.bpmn20.xml in their JAR (host PluginProcessDeployer reads + deploys with category=pluginId + cascading undeploy on stop); POST/GET /api/v1/workflow/** endpoints; reference plug-in ships a live plugin-printing-shop-plate-approval BPMN driving printing_shop.plate.approve
|
| P2.2 | BPMN designer (web) | 🔜 Pending — depends on R1 |
| P2.3 | User-task form rendering | 🔜 Pending |
Phase 3 — Metadata store: forms and rules (Tier 1 customization)
| # | Unit | Status |
|---|---|---|
| P3.1 | Dynamic custom field form renderer | ✅ DONE — b0b0183 — DynamicExtFields SPA component fetches custom field declarations from metadata API and renders type-matched inputs (string/integer/decimal/boolean/date/enum) with i18n labels. Wired into CreateItemPage + CreatePartnerPage. No new backend code — uses the existing custom-fields endpoint. |
| P3.2 | Form renderer (web) | 🔜 Pending — depends on R1 |
| P3.3 | Form designer (web) | 🔜 Pending — depends on R1 |
| P3.4 | Custom field application (JSONB ext validation) |
✅ DONE — 5bffbc4
|
| P3.5 | Rules engine (event-driven) | 🔜 Pending |
| P3.6 | List view designer (web) | 🔜 Pending — depends on R1 |
Phase 4 — Authentication and authorization
| # | Unit | Status |
|---|---|---|
| P4.1 | Built-in JWT auth + Argon2id + bootstrap admin | ✅ DONE — 540d916
|
| P4.2 | OIDC integration (Keycloak-compatible) | ✅ DONE — 6ad72c7 — composite JwtDecoder tries built-in HS256 first, falls back to external OIDC provider's JWKS (auto-discovered from issuer-uri). PrincipalContextFilter handles both claim formats (built-in username+roles vs Keycloak preferred_username+realm_access.roles). Opt-in via vibeerp.security.oidc.issuer-uri. No new dependencies (uses existing spring-security-oauth2-resource-server). |
| P4.3 | Permission checking against metadata__role_permission
|
✅ DONE — 75bf870
|
Phase 5 — Core PBCs
| # | Unit | Status |
|---|---|---|
| P5.1 |
pbc-catalog — items, units of measure |
✅ DONE — 69f3daa
|
| P5.2 |
pbc-partners — customers, suppliers, contacts, addresses |
✅ DONE — 3e40cae
|
| P5.3 |
pbc-inventory — locations + stock balances (movements deferred) |
✅ DONE — f63a73d
|
| P5.4 |
pbc-warehousing — receipts, picks, transfers, counts |
✅ Partial — ba50780 — StockTransfer aggregate live (create/confirm/cancel), confirm writes atomic TRANSFER_OUT + TRANSFER_IN ledger pair via InventoryApi; transactional rollback verified; receipts (own-aggregate version), picks, counts still pending |
| P5.5 |
pbc-orders-sales — sales orders + lines (deliveries deferred) |
✅ DONE — a8eb2a6
|
| P5.6 |
pbc-orders-purchase — POs + receive flow (RFQs deferred) |
✅ DONE — 2861656
|
| P5.7 |
pbc-production — work orders, routings, operations |
✅ Partial — fa86718 v3 routings/ops + 35ad8a8 event-driven routings + 1b0f6d8 shop-floor dashboard. WorkOrderOperation child entity with per-op sequential state machine (PENDING → IN_PROGRESS → COMPLETED); WorkOrder.complete() gated behind every operation reaching COMPLETED; startOperation + completeOperation verbs stamp startedAt/completedAt and record operator-entered actualMinutes for variance reporting. WorkOrderRequestedEvent carries optional RoutingOperationSpec list so customer plug-ins can attach routings through api.v1 alone (reference plug-in ships a 4-step CUT → PRINT → FOLD → BIND default). GET /work-orders/shop-floor projects IN_PROGRESS work orders into a flat dashboard snapshot with current operation, planned/actual time totals, and operations-completed count. Scheduling / capacity planning / machine FKs still pending. |
| P5.8 |
pbc-quality — inspection plans, results, holds |
✅ Partial — 4835785 — InspectionRecord aggregate live (append-only record of QC decisions with APPROVED/REJECTED + inspected/rejected quantities + source reference + inspector). Plans/templates + cross-PBC reactions (quarantine on rejection, WO scrap) still pending. |
| P5.9 |
pbc-finance — GL, chart of accounts, double-entry journal entries |
✅ Partial — 4d0dd9f chart of accounts (6 seeded: Cash, AR, Inventory, AP, Revenue, COGS) + 3745de3 double-entry JournalEntryLine child entity with per-account debit/credit legs. Event subscribers now write balanced entries: SO confirm → DR AR / CR Revenue; PO confirm → DR Inventory / CR AP. SPA shows expandable debit/credit lines. Remaining: period close, trial balance, full P&L / balance sheet views. |
Phase 6 — Web SPA (React + TS)
| # | Unit | Status |
|---|---|---|
| R1 | Vite + React + TS bootstrap, login flow, typed REST client | ✅ DONE — fc62d6d — new :web Gradle subproject wraps Vite/React 18/TypeScript/Tailwind 3.4 via Exec tasks; :distribution consumes the dist via an outgoing/incoming Gradle configuration and stages it into classpath:/static/ so a single fat-jar serves both API and SPA. Hand-written typed fetch client over /api/v1/** (211 KB JS gzipped, no codegen toolchain). AuthContext stores the JWT in localStorage with a 401 handler. SpaController forwards every known SPA route prefix to /index.html so React Router deep links work on hard refresh. SecurityConfiguration reordered: /api/** stays .authenticated() before the SPA permitAll rules so the "API is always authenticated" invariant holds even with the SPA bundled in the same image. Dockerfile build stage adds apk add nodejs npm so ./gradlew :distribution:bootJar produces a self-contained image with the SPA inside. End-to-end smoke verified on fresh Postgres: log in, walk SO DRAFT → CONFIRMED → SHIPPED, watch stock balances drop + AR journal entry settle — all in a real browser. |
| R2 | Identity screens (user admin, role admin) | ✅ DONE — 82c5267 — UsersPage (list + link to detail), CreateUserPage, UserDetailPage (role toggle with Assign/Revoke), RolesPage (list + inline create). Backend: new RoleService + RoleController with list/create roles, assign/revoke role endpoints. identity.yml updated with identity.role.create permission. |
| R3 | Customize / metadata UIs | 🔜 Pending |
| R4 | Per-PBC create/edit screens | ✅ Partial — 2535324 + 1777189 — create forms for items, partners, locations, purchase orders, work orders (with BOM + routing editors), stock adjustment. Every PBC entity that operators need to manage has a SPA form. Remaining: edit forms (patch existing records), richer BOM/routing editors. |
Phase 7 — Reference printing-shop plug-in
| # | Unit | Status |
|---|---|---|
| REF.0 | Hello-world plug-in (PF4J + Plugin lifecycle + endpoints + own DB) | ✅ DONE through v0.6 |
| REF.1 | Real BPMN workflow handler (quote → job card) | ✅ DONE — 3027c1f core + 35ad8a8 v3 routings — plug-in BPMN publishes WorkOrderRequestedEvent (new api.v1 event); pbc-production's WorkOrderRequestedSubscriber reacts by creating a DRAFT WorkOrder via WorkOrderService.create. Idempotent on event code. Plug-in has zero compile-time coupling to pbc-production; pbc-production has zero knowledge the plug-in exists. v3 follow-up: event now carries optional RoutingOperationSpec list; the plug-in's handler ships a default 4-step CUT → PRINT → FOLD → BIND routing that reaches the created WO as v3 operations, end-to-end through api.v1 alone. |
| REF.2 | Plate / ink / press CRUD as customer-style domain | ✅ Partial — plate + ink CRUD live (7af11f2) |
| REF.3 | Reference forms + automation rules in plug-in metadata | 🔜 Pending — depends on P3.3, P3.4 |
Phase 8 — Hosted, AI agents, mobile (post-v1.0)
| # | Unit | Status |
|---|---|---|
| H1 | Instance provisioning console (one process per customer) | 🔜 Post-v1.0 |
| A1 | MCP server (REST endpoints exposed as AI-agent functions) | 🔜 v1.1 |
| M1 | React Native skeleton | 🔜 v2 |
What's live right now
These are the cross-cutting platform services already wired into the running framework. Each was built with a real end-to-end smoke test against a Postgres container.
| Service | Module | What it does |
|---|---|---|
| Auth (P4.1) |
platform-security, pbc-identity
|
Username/password login → HMAC-SHA256 JWT (15min access + 7d refresh). Bootstrap admin printed to logs on first boot. Spring Security filter chain enforces auth on every endpoint except /actuator/health, /api/v1/_meta/**, /api/v1/auth/login, /api/v1/auth/refresh. Principal bridges into the audit listener so created_by columns carry real user UUIDs. |
| Authorization (P4.3) | platform-security.authz |
@RequirePermission("partners.partner.deactivate") on controller methods, enforced by a Spring AOP @Aspect. JWT access tokens carry a roles claim populated from identity__user_role at login time. PrincipalContextFilter populates a per-request AuthorizationContext with the principal's role set; the aspect consults PermissionEvaluator.has(roles, key) which special-cases the wildcard admin role and otherwise reads metadata__role_permission. Failed checks throw PermissionDeniedException → 403 with the offending key in the body. The bootstrap admin gets the admin role on first boot; non-admin users with no role assignments get 403 on every protected endpoint. |
| Plug-in HTTP (P1.3) | platform-plugins |
Plug-ins call context.endpoints.register(method, path, handler) to mount lambdas under /api/v1/plugins/<plugin-id>/<path>. Path templates with {var} extraction via Spring's AntPathMatcher. Plug-in code never imports Spring MVC types. |
| Plug-in linter (P1.2) | platform-plugins |
At plug-in load time (before any plug-in code runs), ASM-walks every .class entry for references to org.vibeerp.platform.* or org.vibeerp.pbc.*. Forbidden references unload the plug-in with a per-class violation report. |
| Plug-in DB schemas (P1.4) | platform-plugins |
Each plug-in ships its own META-INF/vibe-erp/db/changelog.xml. The host's PluginLiquibaseRunner applies it against the shared host datasource at plug-in start. Plug-ins query their own tables via the api.v1 PluginJdbc typed-SQL surface — no Spring or Hibernate types ever leak. |
| Event bus + outbox (P1.7) | platform-events |
Synchronous in-process delivery PLUS a transactional outbox row in the same DB transaction. Propagation.MANDATORY so the bus refuses to publish outside an active transaction (no publish-and-rollback leaks). OutboxPoller flips PENDING → DISPATCHED every 5s. Wildcard ** topic for the audit subscriber; topic-string and class-based subscribe. Now exercised end-to-end: SalesOrderService.confirm/ship/cancel and PurchaseOrderService.confirm/receive/cancel each publish a typed event from api.v1.event.orders.* inside the same @Transactional method as their state change and ledger writes. Smoke test confirms the wildcard EventAuditLogSubscriber logs every one and platform__event_outbox rows are persisted + dispatched. |
| Metadata loader (P1.5) | platform-metadata |
Walks the host classpath and each plug-in JAR for META-INF/vibe-erp/metadata/*.yml, upserts entities/permissions/menus into metadata__* tables tagged by source. Idempotent (delete-by-source then insert). User-edited metadata (source='user') is never touched. Public GET /api/v1/_meta/metadata returns the full set for SPA + AI-agent + OpenAPI introspection. |
| i18n / Translator (P1.6) | platform-i18n |
ICU4J-backed Translator with named placeholders, plurals, gender, locale-aware number/date formatting. Per-plug-in instance scoped via a (classLoader, baseName) chain — plug-in's META-INF/vibe-erp/i18n/messages_<locale>.properties resolves before the host's messages_<locale>.properties for shared keys. RequestLocaleProvider reads Accept-Language from the active HTTP request via the servlet container, falling back to vibeerp.i18n.defaultLocale outside an HTTP context. JVM-default locale fallback explicitly disabled to prevent silent locale leaks. |
| Custom field application (P3.4) | platform-metadata.customfield |
CustomFieldRegistry reads metadata__custom_field rows into an in-memory index keyed by entity name, refreshed at boot and after every plug-in load. ExtJsonValidator validates the JSONB ext map of any entity against the declared FieldTypes — String maxLength, Integer/Decimal numeric coercion with precision/scale enforcement, Boolean, Date/DateTime ISO-8601 parsing, Enum allowed values, UUID format. Unknown keys are rejected; required missing fields are rejected; ALL violations are returned in a single 400 so a form submitter fixes everything in one round-trip. Partner is the first PBC entity to wire ext through PartnerService.create/update; the public GET /api/v1/_meta/metadata/custom-fields/{entityName} endpoint serves the api.v1 runtime view of declarations to the SPA / OpenAPI / AI agent. |
| PBC pattern (P5.x recipe) | all 10 pbc-* modules |
Ten PBCs prove the recipe. All 10 follow the same DDD layering (domain/application/http/infrastructure). Cross-PBC interaction goes through api.v1 service interfaces (InventoryApi, CatalogApi, PartnersApi) or the event bus — never source dependencies. pbc-finance is the framework's first CONSUMER PBC: it subscribes to all 6 order lifecycle events and writes balanced double-entry journal entries with idempotent handling. pbc-production demonstrates child entities (WorkOrderInput, WorkOrderOperation) and event-driven routing from plug-in requests. pbc-warehousing demonstrates atomic transfer pairs (TRANSFER_OUT + TRANSFER_IN) via InventoryApi. pbc-quality demonstrates append-only inspection records. |
What the reference plug-in proves end-to-end
The printing-shop reference plug-in is the framework's executable acceptance test. It demonstrates everything a real customer plug-in needs:
Plug-in JAR drop → host bootstrap:
1. PF4J discovers the JAR and reads plugin.yml
2. PluginLinter ASM-walks every class — passes
3. PluginLiquibaseRunner applies META-INF/vibe-erp/db/changelog.xml
→ creates plugin_printingshop__plate, plugin_printingshop__ink_recipe
4. MetadataLoader.loadFromPluginJar reads
META-INF/vibe-erp/metadata/printing-shop.yml
→ 2 entities, 5 permissions, 2 menus tagged source='plugin:printing-shop'
5. VibeErpPluginManager calls Plugin.start(context) with a real
PluginContext (logger + endpoints + eventBus + jdbc all wired)
6. The plug-in's start() lambda registers 7 HTTP endpoints
7. Boot completes, app is serving traffic
Live HTTP traffic:
POST /api/v1/auth/login (admin) → 200, JWT
POST /api/v1/plugins/printing-shop/plates (Bearer) → 201, plate created
GET /api/v1/plugins/printing-shop/plates → list of plates
GET /api/v1/plugins/printing-shop/plates/{id} → fetch by id
POST /api/v1/plugins/printing-shop/inks → 201, ink created
GET /api/v1/_meta/metadata → all 6 entities + 18 permissions
GET /api/v1/identity/users (Bearer) → still works
GET /api/v1/catalog/uoms (Bearer) → 15 seeded UoMs
This is real: a JAR file dropped into a directory, loaded by the framework, executing customer-specific business logic against its own database tables, exposed via REST. The plug-in code never imports a single internal framework class — and the linter would refuse to load it if it tried.
What's not yet live (the deferred list)
- Form designer / renderer. P3.2 (metadata-driven form layout renderer) and P3.3 (web UI for designing forms) are pending.
- List view designer. P3.6 (metadata-driven list view configuration) is pending.
- Rules engine. P3.5 (event-driven automation rules stored as metadata rows) is pending.
- BPMN designer. P2.2 (web UI for editing BPMN workflows) is pending.
- User-task form rendering. P2.3 (rendering Flowable user-task forms in the SPA) is pending.
- Metadata admin UIs. R3 (customize / metadata screens in the SPA) is pending.
- MCP server. The architecture leaves room for it; the implementation is v1.1.
- Mobile. v2.
How to run the end-to-end demo
# Bring up Postgres + stage the reference plug-in JAR
docker compose up -d db
./gradlew :reference-customer:plugin-printing-shop:installToDev
# Boot the framework (serves both the REST API and the React SPA
# from the same fat-jar on http://localhost:8080)
./gradlew :distribution:bootRun
# Open the browser:
open http://localhost:8080
The bootstrap admin password is printed to the boot log on first start. On a fresh database, create your own master data through the SPA or API.
Walk the framework:
- Log in as
admin(password from the boot log). - Create items, partners, and locations via the SPA create forms.
- Create a sales order → Confirm → watch the AR journal entry appear automatically (event-driven cross-PBC integration).
-
Ship the sales order → stock balances drop, the journal entry
settles to
AR SETTLED, andSALES_SHIPMENTrows appear in the inventory movements ledger. - Create a purchase order → Confirm → Receive for the mirror-image flow.
- Create a work order with BOM inputs → Start → complete operations → Complete → raw materials consumed, finished goods produced.
- Shop Floor polls every 5 seconds; start a work order and watch it appear with a progress bar.
Or for the API-only version:
# In another shell:
curl -s localhost:8080/api/v1/_meta/info
curl -s localhost:8080/api/v1/_meta/metadata | jq
ACCESS=$(curl -s -H 'Content-Type: application/json' \
-X POST localhost:8080/api/v1/auth/login \
-d '{"username":"admin","password":"<from-logs>"}' | jq -r .accessToken)
curl -s -H "Authorization: Bearer $ACCESS" localhost:8080/api/v1/catalog/items
curl -s -H "Authorization: Bearer $ACCESS" localhost:8080/api/v1/orders/sales-orders
curl -s -H "Authorization: Bearer $ACCESS" localhost:8080/api/v1/plugins/printing-shop/plates
OpenAPI spec at /v3/api-docs and Swagger UI at /swagger-ui/index.html.
Module map
api/api-v1 PUBLIC CONTRACT — semver-governed
platform/platform-bootstrap Spring Boot main, props, web filters
platform/platform-persistence Audit base, JPA entities, PrincipalContext
platform/platform-security JWT issuer/verifier, Spring Security config, password encoder
platform/platform-events EventBus impl, transactional outbox, poller
platform/platform-metadata MetadataLoader, MetadataController
platform/platform-i18n IcuTranslator, RequestLocaleProvider (P1.6)
platform/platform-plugins PF4J host, linter, Liquibase runner, endpoint dispatcher, PluginJdbc
pbc/pbc-identity User entity end-to-end + auth + bootstrap admin
pbc/pbc-catalog Item + Uom entities + cross-PBC CatalogApi facade
pbc/pbc-partners Partner + Address + Contact entities + cross-PBC PartnersApi facade
pbc/pbc-inventory Location + StockBalance + cross-PBC InventoryApi facade
(first PBC to CONSUME another PBC's facade — CatalogApi)
pbc/pbc-orders-sales SalesOrder + SalesOrderLine + cross-PBC SalesOrdersApi facade
(first PBC to CONSUME TWO facades simultaneously — PartnersApi + CatalogApi
and the first cross-PBC WRITE flow via InventoryApi.recordMovement)
pbc/pbc-orders-purchase PurchaseOrder + PurchaseOrderLine + cross-PBC PurchaseOrdersApi facade
(the buying-side mirror; receives via InventoryApi.recordMovement
with positive PURCHASE_RECEIPT deltas)
pbc/pbc-finance JournalEntry + read-only controller; first CONSUMER PBC.
Subscribes to SalesOrderConfirmedEvent + PurchaseOrderConfirmedEvent
via api.v1 EventBus.subscribe(eventType, listener) and writes
idempotent AR/AP rows. No outbound facade.
reference-customer/plugin-printing-shop
Reference plug-in: own DB schema (plate, ink_recipe),
own REST endpoints, own metadata YAML
distribution Bootable Spring Boot fat-jar assembly
17 Gradle subprojects. Architectural dependency rule (PBCs never import each other; plug-ins only see api.v1) is enforced by the root build.gradle.kts at configuration time.
Where to look next
-
Detailed roadmap:
docs/superpowers/specs/2026-04-07-vibe-erp-implementation-plan.md— every unit, every dependency, every acceptance test. -
Architecture rationale:
docs/superpowers/specs/2026-04-07-vibe-erp-architecture-design.md— 14 sections, validated against 2026 ERP/EBC SOTA. -
Project guardrails:
CLAUDE.md— the 11 rules every change is held to. - Git history: every chunk is one commit with a detailed message describing what landed, what was deferred, and any bug caught by the smoke test.