The previous docs commit fixed README + CLAUDE + the three "scope note"
guides but explicitly left the deeper how-to docs as-is, claiming the
architecture hadn't changed. That was wrong on closer reading: those
docs still described the pre-single-tenant world (tenant_id columns,
two-wall isolation, per-tenant routing) and the pre-implementation
plug-in API (class-based @PluginEndpoint controllers, ResponseBuilder,
context.log instead of context.logger, plugin.yml entryClass field).
A reader following any of those guides would write code that doesn't
compile.
This commit aligns them with what the framework actually does today.
What changed:
* docs/architecture/overview.md
- Guardrail #5 rewritten from "Multi-tenant in spirit from day one"
to "Single-tenant per instance, isolated database" matching
CLAUDE.md.
- The api.v1 package layout updated to match the actual current
surface: dropped `Tenant`, added `PrincipalId`,
`PersistenceExceptions`, the full plug-in package
(PluginContext, PluginEndpointRegistrar, PluginRequest/Response,
PluginJdbc, PluginRow), and the two live cross-PBC facades
(IdentityApi, CatalogApi).
- The whole "Multi-tenancy" section replaced with
"Single-tenant per instance" — drops the two-wall framing,
drops per-region routing, drops tenant_id columns and RLS.
The single-tenant rationale is explained in line with the
rest of the framework.
- Custom fields and metadata store sections drop "per tenant"
and tenant_id from their column lists.
- Audit row in the cross-cutting table drops tenant_id from
the column list and adds the PrincipalContext bridge note
that's actually live as of P4.1.
- "Tier 1 — Key user, no-code" intro drops "scoped to their tenant".
* docs/plugin-api/overview.md
- api.v1 package layout updated to match the real current surface
(same set of additions and deletions as the architecture
overview).
- Per-package orientation table rewritten to describe what is
actually in each package today, including which parts are
live (event/, plugin/, http/) and which are stubbed (i18n/,
workflow/, form/) with the relevant implementation-plan unit
referenced.
* docs/plugin-author/getting-started.md — full rewrite. The previous
version walked an author through APIs that don't exist:
• `Plugin.start(context: PluginContext)` and
`Plugin.stop(context: PluginContext)` — actual API is
`start(context)` and `stop()` (no arg), with the Pf4jPlugin +
VibeErpPlugin dual-supertype trick using import aliases.
• `context.log` — actual is `context.logger` (PluginLogger
interface).
• `plugin.yml` with `entryClass:` field and a structured
`metadata.i18n:` subkey — actual schema has no entryClass
(PF4J reads `Plugin-Class` from MANIFEST.MF) and `metadata:`
is a flat list of paths.
• Class-based `@PluginEndpoint` controllers with `ResponseBuilder`
— actual is lambda-based registration via
`context.endpoints.register(method, path, handler)` returning
`PluginResponse`.
• `request.translator.format(...)` — translator currently
throws UnsupportedOperationException; the example would crash.
The new version walks through:
1. The compileOnly api.v1 + pf4j dependency setup.
2. The dual-supertype Pf4jPlugin + VibeErpPlugin trick with
import aliases (matches the actual reference plug-in).
3. The real JAR layout (META-INF/MANIFEST.MF for PF4J,
plugin.yml for human metadata, META-INF/vibe-erp/db/changelog.xml
for Liquibase, META-INF/vibe-erp/metadata/<id>.yml for the
metadata loader).
4. Liquibase changelog at the unique META-INF path (not the
host's `db/changelog/master.xml` path which would collide
on parent-first classloader lookup — the bug we hit and
fixed in commit 1ead32d7).
5. Lambda-based endpoint registration with real working
examples (path templates, queryForObject, update, JSON
body parsing).
6. Metadata YAML shape matching what MetadataLoader actually
parses.
7. The boot-log sequence the operator will see, taken from
a real smoke run.
8. An honest "what this guide does not yet cover" list with
implementation-plan unit references.
* docs/i18n/guide.md
- Added a "Status" callout at the top: the api.v1 contract is
locked, the ICU4J implementation is P1.6 and not yet wired,
`context.translator` currently throws.
- Bundle filename convention corrected from `<locale>.properties`
to `messages_<locale>.properties` (matches what the reference
plug-in actually ships).
- plugin.yml example updated to show the flat `metadata:` array
that PluginManifest actually parses (the previous structured
`metadata.i18n:` subkey doesn't exist in the schema).
- Merge precedence renamed "tenant overrides" → "operator
overrides" since vibe_erp is single-tenant per instance.
- LocaleProvider resolution chain updated to drop "tenant default"
and reference `vibeerp.i18n.default-locale` configuration.
- "Time zones are per-tenant" → "per-user with an instance default".
- Locale-add reference to "i18n-overrides volume" annotated as
"once P1.6 lands".
* docs/customer-onboarding/guide.md
- Section 3 "Create a tenant" deleted; replaced with
"Configure the instance" pointing at vibeerp.instance.* config.
- First-boot bullet list rewritten to drop the bogus
"create default tenant row in identity__tenant" step (no such
table) and add the real metadata-loader and per-plug-in lifecycle
steps that happen.
- Mounted-volume table updated: `i18n-overrides/` is "operator
translation overrides", not "tenant-level".
- "Bind users to roles, bind roles to tenants" simplified;
OIDC group claims annotated as P4.2 (not yet wired).
- Go-live checklist drops "non-production tenant" wording and
"tenant locale defaults" (now instance-level config).
- "Hosted multi-tenant operations" deferred-list item rewritten
to "hosted operations: provisioning many independent vibe_erp
instances", matching the new single-tenant deployment model.
* docs/form-authoring/guide.md
- Tier 1 section drops "scoped to the tenant",
"tweaking for a specific tenant", "tenant-specific custom field".
* docs/workflow-authoring/guide.md
- "Approval chains that vary per tenant" → "Approval chains the
customer wants to author themselves".
* docs/index.md
- Architecture overview row description updated from
"multi-tenancy" to "single-tenant deployment model".
- i18n guide row description updated from "tenant overrides"
to "operator overrides".
Build: ./gradlew build still green (no source touched).
The remaining `tenant` references in docs are now ALL intentional —
they appear in sentences like "vibe_erp is single-tenant per instance",
"there is no create-a-tenant step", "no per-tenant bundles". The
historical specs under docs/superpowers/specs/ were intentionally
NOT touched: those are dated design records, and the implementation
plan already had the single-tenant refactor noted at the top.