• 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.
    vibe_erp authored
     
    Browse Code »