# NexusCRM Platform Architecture

Status: draft
Purpose: authoritative target architecture for the clean L0 platform boundary
Date: 2026-04-30

## 1. Executive Summary

NexusCRM's target architecture is a metadata-driven SObject platform. The
platform binary is not a CRM application. It is a generic runtime that can host
CRM, Jira-like, ITSM, ERP, or tenant-specific applications through installed
metadata packs.

The long-term target is an application-neutral `L0`. A clean platform startup
does not assume any installed `L1` app schema or metadata. Salesforce-compatible
CRM is the first `L1` pack being implemented, but a Jira-like pack must be able
to define `Issue`, `Project`, `Sprint`, boards, backlog views, workflow states,
and permissions on the same substrate without changing platform code.

The strongest design rule is:

> Salesforce compatibility is a runtime and protocol contract over generic
> engine primitives, not a baked-in CRM object catalog.

The platform should expose Salesforce-shaped APIs, metadata semantics, formula
syntax, permission primitives, Flow/ValidationRule/ApprovalProcess runtimes,
and SObject describe/query/CRUD behavior. It should not compile `Account`,
`Lead`, `Opportunity`, `Order`, `Campaign`, `ConvertLeads`, `ActivateOrder`, or
other CRM facts into L0 engine code.

Application shape belongs to installable packs. Tenant-specific shape belongs
to tenant overlay metadata. Record data belongs to per-tenant runtime state.

Any new `L0` primitive must pass the multi-app neutrality test: it should be
useful for CRM and for a non-CRM app such as Jira. If a contract names or
structurally assumes CRM objects, CRM actions, CRM report types, or CRM
lifecycle states, it is an `L1` CRM fact or a transitional adapter, not platform
substrate.

## 2. Deployment Model

The platform repository should be identical for every deployment:

```text
NexusCRM Platform (this repo)
  L0 engine code:
    - Generic CRUD over any SObject
    - Formula, ValidationRule, Flow, and ApprovalProcess runtimes
    - Permission, sharing, profile, role, and permission-set primitives
    - Layout, page-builder, action-bar, list-view, and renderer primitives
    - Schema service over _System_* metadata
    - Tooling/admin REST APIs for install and authoring
    - Metadata-driven frontend shell

  L0 engine schema:
    - _System_Object, _System_Field, _System_Relationship
    - _System_RecordType, _System_Action, _System_Layout
    - _System_ValidationRule, _System_Flow, _System_FlowDefinition
    - _System_ApprovalProcess
    - _System_Permission, _System_PermissionSet, _System_Role
    - _System_AutoNumber, _System_AuditLog, _System_Outbox
    - User, Profile, Group, UserRole as engine-universal admin objects

  No app SObjects. No app fields. No app permissions. No app flows.
```

App pack distributions are installed through tooling/admin APIs from configured
pack sources:

```text
nexuscrm-sf-crm-pack
  SObjects: Account, Lead, Opportunity, Order, Contact, Case, Quote, ...
  Standard fields, picklists, sharing defaults, relationships, record types
  CRM permission keys such as ConvertLeads
  CRM Flow, ValidationRule, ApprovalProcess, Action, Layout, and report metadata

nexuscrm-jira-pack
  SObjects: Issue, Project, Sprint, Board, Component, Version, Comment, ...
  Issue workflow as ValidationRule + Flow metadata
  Board/backlog metadata only through generic L0 renderer primitives
  Permissions such as Browse Project or Resolve Issue

nexuscrm-tenant-overlay-pack
  Tenant-specific __c fields, RecordTypes, picklists, approvals, flows,
  validation rules, permission grants, and layout overrides layered on top of
  installed app packs for one tenant.
```

Per-tenant runtime state contains `_System_*` rows from installed packs plus
admin-authored customization, SObject tables created from those rows, and
record data.

## 3. Layer Model

### L0: Platform Runtime

L0 is the generic substrate. It is identical for every deployment.

L0 owns:

- Generic CRUD over any SObject.
- Salesforce-shaped REST, metadata, tooling, describe, query, and UI API
  protocol surfaces.
- Generic schema service over `_System_*` metadata tables.
- Formula evaluator, including Salesforce-compatible primitives such as
  `ISCHANGED`, `PRIORVALUE`, relationship traversal, and expression coercion.
- ValidationRule evaluator.
- Flow runtime for record-triggered, scheduled, and invocable flows.
- ApprovalProcess engine and generic submit/approve/reject runtime.
- Permission engine, including generic Profile, PermissionSet, UserRole, Group,
  object permissions, field permissions, sharing rules, sharing models, and
  row-level evaluation.
- Layout, page-builder, action-bar, list-view, and renderer primitives.
- Outbox, audit, replay, import/apply, drift detection, and pack installation
  infrastructure.
- Installed-pack registry and apply/hash/verify machinery.
- Frontend metadata-driven shell that renders objects from describe, layout,
  action, and view metadata.

L0 owns engine schema:

- `_System_Object`, `_System_Field`, `_System_Relationship`.
- `_System_RecordType`.
- `_System_ValidationRule`.
- `_System_Flow`, `_System_FlowDefinition`.
- `_System_ApprovalProcess`.
- `_System_Permission`, `_System_PermissionSet`,
  `_System_ObjectPermissions`, `_System_FieldPermissions`.
- `_System_Role`, `_System_Group`, `_System_SharingRule`.
- `_System_Layout`, `_System_Action`, `_System_ListView`.
- `_System_AutoNumber`, `_System_AuditLog`, `_System_Outbox`.
- Generic admin objects such as `User`, `Profile`, `Group`, and `UserRole`
  when they are platform-universal metadata or identity primitives.

L0 must not own:

- CRM objects such as `Account`, `Lead`, `Opportunity`, `Order`, `Contact`,
  `Case`, `Quote`, `Contract`, `Product2`, `Pricebook2`, or `Campaign`.
- CRM fields, picklists, sharing defaults, record type rows, layouts, flows,
  validation rules, list views, actions, report templates, or seed policy.
- CRM permission keys such as `ConvertLeads`, `ImportLeads`, `ActivateOrder`,
  `ActivateContract`, `ManageCases`, or `TransferAnyLead`.
- Tenant-specific custom objects, `__c` fields, picklists, approval processes,
  flows, validation rules, role hierarchy, profile grants, or business policy.
- SObject-name branches inside generic records, query, permissions,
  automation, RLS, or frontend runtime code.
- Future app objects or behaviors such as Jira-like `Issue`, `Project`,
  `Sprint`, boards, backlogs, workflow states, or issue permissions. These are
  `L1` app-pack facts, not proof that `L0` should grow app-specific branches.

### L1: Application Pack

L1 is installable product metadata. A pack defines the base/default product
shape on top of the generic platform.

Examples:

- `nexuscrm-sf-crm-pack`: Salesforce-compatible CRM application shape.
- `nexuscrm-jira-pack`: Jira-like issue/project/sprint application shape.
- `nexuscrm-itsm-pack`: ITSM application shape.
- `nexuscrm-erp-pack`: ERP application shape.

The CRM pack owns the base/default CRM metadata:

- CRM SObjects: `Account`, `Lead`, `Opportunity`, `Order`, `Contact`, `Case`,
  `Quote`, `Contract`, `Product2`, `Pricebook2`, `Campaign`, `Task`, `Event`,
  line items, junction objects, and other CRM product objects.
- CRM fields, picklist defaults, sharing defaults, relationship semantics,
  autonumber formats, and seed policy.
- CRM RecordType rows for CRM objects. It does not own the generic RecordType
  primitive itself.
- CRM permission rows and default grants, such as `ConvertLeads`.
- CRM flows, validation rules, approval process metadata, field mappings, and
  action metadata.
- CRM layout, list-view, page, action placement, and report metadata.

A Jira-like pack would similarly own its product facts: issue/project/sprint
objects, fields, workflow states, board and backlog metadata, issue actions,
permissions, reports, and default views. These examples are requirements on
`L0` neutrality, not platform-owned defaults.

An L1 pack may configure only primitives that L0 already supports. If a Jira
pack needs board, swimlane, or backlog rendering, L0 must first provide generic
view/page primitives for that shape. The pack supplies metadata; it does not
smuggle app-specific frontend runtime code into the platform.

L1 ownership is not the same as permanent ownership of every live value. Many
pack defaults are admin-editable after installation. For those surfaces, the
pack owns the base value and L2 owns tenant/admin overrides. Effective metadata
is computed from base pack metadata plus tenant overlays.

### L2: Tenant Overlay

L2 is tenant/admin-authored metadata layered on top of installed L1 packs.

L2 owns:

- Tenant custom objects and `__c` fields.
- Tenant-specific field overlays, layouts, list views, record types, picklist
  values, flows, validation rules, approval processes, and sharing rules.
- Tenant profile grants, permission sets, role hierarchy, and group membership.
- Tenant business policy and tenant-specific automation.

The public-core repository must not contain tenant overlay payload. It may
contain generic tenant overlay machinery.

### L3: Record Data

L3 is user-created business data stored in SObject tables materialized from L0,
L1, and L2 metadata. L3 data is never shipped in the platform binary or in
public-core defaults.

## 4. Compatibility Contract

NexusCRM targets Salesforce API compatibility at the protocol and primitive
level.

This means:

- The REST API shape follows Salesforce-style SObject, describe, query, and
  metadata conventions.
- Formula, Flow, ValidationRule, ApprovalProcess, object permissions, field
  permissions, sharing, RecordType, and layout primitives should match
  Salesforce semantics where NexusCRM claims compatibility.
- The CRM pack may expose Salesforce-standard object names such as `Account`
  and `Lead`.
- A non-CRM pack may expose Nexus distribution-standard object names such as
  `Issue` or `Incident` if the deployment defines those as installed app
  objects. This is Salesforce-protocol compatibility, not a claim that those
  objects are part of Salesforce's standard catalog.
- Tenant custom objects and fields should still use Salesforce-style custom
  naming such as `Issue_Extension__c` or `RiskScore__c`.

This document does not rely on an unverified claim that Salesforce Sales Cloud
itself is implemented internally as an unlocked package. Salesforce's metadata
and package development model is a useful architectural reference, but
NexusCRM's boundary is defined by this repository's own layer contract.

## 5. Ownership Rules

Every fact must have exactly one owner and one authoritative read path.

### Engine Primitive vs Metadata Row

Many concepts exist at two levels. The primitive belongs to L0; product rows
belong to L1 or L2.

| Concept | L0 Owns | L1 Owns | L2 Owns |
| --- | --- | --- | --- |
| Object model | `_System_Object` schema and runtime | CRM/Jira/ITSM object rows | Tenant custom object rows |
| Field model | `_System_Field` schema and runtime | Product field rows | Tenant custom field rows |
| RecordType | Generic RecordType substrate | CRM record type rows | Tenant record type rows |
| Action | Action schema, renderer, dispatch primitive | CRM actions and default placements | Tenant actions and placements |
| CRUD actions | Generic CRUD capability | Optional product default placements | Tenant overrides |
| Permission | Permission schema and evaluator | Product permission rows/default grants | Tenant grants/custom permission sets |
| Profile/UserRole/Group | Generic model and evaluator | Product default grants only when explicitly pack-owned | Tenant role hierarchy and memberships |
| Approval | Generic engine | Product default approval metadata | Tenant approval processes |
| Board/Kanban | Generic renderer primitive, if supported | Product board metadata | Tenant board configuration |

### Base Defaults vs Tenant Overrides

Pack ownership means ownership of the shipped base/default metadata. It does not
mean that every live row or column remains immutable after installation.

Each metadata surface should declare its mutability semantics:

- Immutable pack facts: tenant overlays cannot change them without replacing or
  upgrading the pack.
- Default-only facts: the pack supplies an initial value; tenant/admin edits own
  the live value after override.
- Additive facts: the pack supplies base rows; tenant/admin rows are added
  alongside them.
- Overlayable facts: the effective value is base pack metadata plus L2 overlay
  metadata.

Examples:

- A CRM object and required platform-facing field identity may be immutable
  pack facts.
- Picklist values, layout sections, validation rule activation, sharing
  defaults, default grants, and page/action placements may be admin-editable or
  overlayable depending on the surface.
- Tenant profile grants, permission sets, role hierarchy, and custom fields are
  L2 facts even when they modify behavior on an L1 object.

Pack verify must compare live state according to those mutability semantics. It
must not reject legitimate tenant/admin overrides as drift. It should verify the
base/default payload and ownership metadata, then delegate effective-state
checks to generic overlay resolution rules.

### RecordType Rule

`RecordType` is a platform primitive. L0 may expose a Salesforce-compatible
RecordType API surface, but CRM does not own `RecordType` as an application
SObject.

The CRM pack owns rows such as:

- Account default record types.
- Order record types.
- Opportunity record types.

Those rows must be stored and verified through the same RecordType metadata
path as any other pack or tenant record type row.

### Action Rule

Generic CRUD primitives such as create, edit, delete, view, and list are L0
capabilities. Product packs may place those actions on product object layouts
or action bars, but they do not own the primitive itself.

CRM-specific action rows belong to the CRM pack. Examples:

- `ConvertLead`.
- `ActivateOrder`.
- `CreateQuote`.
- CRM-specific approval or sync actions.

The action row is metadata; the behavior it invokes must still be generic.
Allowed targets include Flow metadata, field mappings interpreted by L0,
ApprovalProcess runtime, generic CRUD primitives, or generic invocable actions.
The action must not resolve to a CRM-specific backend handler compiled into L0.

Tenant/admin actions belong to L2. Pack cleanup must never infer ownership from
an Id prefix such as `act_%`. It must use explicit ownership or expected
business keys scoped to the pack.

### Permission Rule

The permission engine is L0. Platform permissions are L0. Product permissions
are L1. Tenant grants are L2.

Examples:

- `ModifyAllData`, `ViewSetup`, and generic setup/admin permissions are L0.
- `ConvertLeads`, `ActivateOrder`, and `ManageCases` are CRM L1.
- A tenant's custom permission set, profile grant, or role hierarchy change is
  L2.

No Go switch should classify CRM permission API names as a permanent boundary.
The boundary must be data ownership: the CRM permission rows live in CRM pack
payload and are read by CRM pack apply/hash/verify.

## 6. Pack Contract

The installed-pack registry is a trust contract, not just a status table.

The target pack model is declarative. An installable app pack is a manifest plus
metadata payload. L0 owns the generic pack reader, dependency resolver, apply
engine, payload hash engine, and verify/drift engine.

For each installable app pack, the platform must resolve from configured pack
sources:

- Stable pack name.
- Version.
- Category.
- Dependencies.
- Manifest schema and supported pack format version.
- Payload hash.
- Declarative apply plan derived from the manifest and payload.
- Declarative verification plan derived from the manifest and payload.

Hand-written pack-specific Go `Apply` or `Verify` code is a transitional bridge
only. The current source-backed in-repo CRM pack may use generic Go adapters
while remaining surfaces are moved behind real component declarations, but the
target architecture must not let application packs ship arbitrary Go behavior
inside the platform binary. Otherwise CRM policy could simply move from generic
runtime branches into pack-specific Go and still violate L0.

The core invariant is:

> If a pack applies a payload, the same payload must be covered by that pack's
> payload hash and verified by that pack's verify path.

`_System_InstalledPack` may report a pack as `active/current` only when:

- The pack was applied successfully.
- The recorded payload hash matches the current payload hash.
- The pack's verify path passes for every payload surface it owns.
- Every installed-pack row resolves to a manifest and payload in the configured
  pack sources, or is explicitly tolerated by a migration mode.

An "unknown" installed pack means the row cannot be resolved from configured
pack sources, its manifest is missing, or its manifest schema/version is
unsupported. It must not mean "not compiled into this binary"; that would
undercut external L1 packs.

For the CRM pack, this includes all CRM-owned surfaces:

- Object and field metadata.
- Physical schema materialized from CRM object metadata.
- Relationship semantics.
- Record types.
- Permissions.
- Actions.
- Flows.
- Validation rules.
- Layouts and list fields.
- Field mappings.
- Autonumber metadata and seed policy.

Default grants and other overlayable defaults may preserve tenant/admin changes
where appropriate, but the catalog payload that introduces or changes those base
defaults must still move the pack hash so an apply path is triggered.

## 7. Apply and Verify Semantics

Pack application must be idempotent and ownership-scoped.

Apply should:

- Read the pack manifest and payload through the pack read path.
- Upsert owned metadata by stable business keys, not by newly generated ids
  alone.
- Preserve tenant/admin-owned metadata.
- Soft-delete or retire only stale rows owned by the same pack.
- Apply pack defaults without overwriting L2 overrides on overlayable surfaces.
- Refresh metadata caches at defined causal boundaries.
- Verify the pack before marking it active/current.

Verify should:

- Check installed app packs, not only runtime-required L0 packs.
- Fail when an installed app pack cannot be resolved from configured pack
  sources unless explicitly tolerated by a migration mode.
- Compare expected pack payload to live rows using the same ownership and
  business-key rules as apply.
- Detect drift in pack-owned base rows without rejecting unrelated L2 rows or
  legitimate tenant/admin overrides.

Warm upgrades must be safe. Legacy rows may use older ids. Apply and verify
must reconcile by stable business keys such as `(ObjectApiName, Name)` where
that is the real identity, then use persisted ids after upsert if needed.

## 8. Read Path Rules

The generic runtime must read facts from metadata services, not from app-specific
branches.

Allowed:

- Records runtime reads object and field shape from metadata tables.
- Permission runtime reads object permissions, field permissions, sharing
  metadata, and record ownership through generic helpers.
- Flow runtime executes flow metadata.
- Layout renderer reads layout/action/view metadata.
- Query/RLS reads generic sharing and field visibility metadata.

Blocked:

- `if object == "Order"` in records, permissions, query, automation, or
  frontend runtime.
- Hardcoded CRM status, picklist, RecordType DeveloperName, permission name, or
  approval process name branches in L0.
- Parallel registries for Salesforce-modeled facts that already have metadata
  representations.
- Feature modules owning derived SObject-shape maps instead of using metadata
  service helpers.

## 9. Public-Core Boundary

This repository is the public-core L0 baseline.

It may contain:

- Generic platform engines.
- Generic metadata schemas and interpreters.
- Generic pack installation, verification, migration, and drift machinery.
- Transitional in-repo CRM pack Go only when explicitly scoped as a migration
  adapter toward declarative external L1 payload.

It must not contain:

- Tenant-specific payload.
- Tenant product, channel, customer, or legal-entity names.
- Tenant-specific custom fields, objects, layouts, flows, validation rules,
  approval processes, picklists, or bootstrap rows.
- Runtime branches that make greenfield behavior depend on tenant data.

Historical cleanup must be handled by explicit migration or runbook, not by
permanent runtime branches in generic engines.

## 10. Target Distribution Shape

The final target is external L1 distribution packs.

Example final layout:

```text
nexuscrm-platform/
  backend/
    cmd/runtime-api/
    cmd/runtime-workers/
    cmd/runtime-integration/
    cmd/setup/
    internal/domain/
    internal/features/
    internal/infrastructure/
    internal/interfaces/
    internal/controlplane/
    internal/bootstrap/          # L0 bootstrap only

nexuscrm-sf-crm-pack/
  pack.json
  objects/
  fields/
  record-types/
  permissions/
  layouts/
  actions/
  flows/
  validation-rules/
  approval-processes/
  list-views/
  field-mappings/
  seed-policy/

nexuscrm-tenant-overlay-pack/
  pack.json
  custom-objects/
  custom-fields/
  layouts/
  flows/
  validation-rules/
  permission-grants/
```

The final shape should not be implemented by simply moving files first. The
trusted install boundary must exist before physical externalization.

The external pack should be declarative. It should contain manifests and
metadata files, not app-specific Go handlers that execute CRM policy inside the
platform process.

## 11. Implementation Roadmap

The safe implementation order is the one from issue #36.

### PR1: Boundary Contract and Runtime Install Model

Goal: prove that clean L0 can boot and that CRM is an installed L1 app pack.

PR1 should:

- Keep `RuntimeRequiredPacks()` clean L0.
- Keep CRM as a source-backed in-repo `AppPack`.
- Install CRM through the CRM-tagged setup command:
  `cd backend/distributions && GOWORK=off go run -tags crm ./cmd/setup install-pack --source official_repo:salesforce-crm-standard`.
- Ensure every CRM source-backed payload is covered by `PayloadHash()` and
  `Verify()`.
- Verify installed app packs during setup/server readiness.
- Add clean-L0 integration tests with no CRM residue.
- Avoid broad ownerless cleanup that can mutate tenant/admin metadata.

PR1 may keep some CRM adapter code only as a transitional implementation
detail. The ownership boundary must still be real, and any Go bridge must be
generic over manifest payload rather than a typed CRM pack owner.

### PR2: Physical CRM Payload Relocation

Goal: move CRM payload into pack-owned read paths.

PR2 should:

- Move CRM object specs, fields, layouts, actions, flows, validation rules,
  permissions, record types, list fields, field mappings, and seed policy into
  the CRM pack payload.
- Split platform permissions from CRM permissions.
- Remove duplicate or two-path payload sources.
- Ensure apply, hash, and verify all read the same pack payload.
- Start replacing pack-specific Go paths with generic declarative pack
  apply/hash/verify helpers.

### PR3: Compile-Time Substrate Cleanup

Goal: remove compile-time CRM knowledge from platform/shared code.

PR3 should remove or invert:

- CRM object name registries.
- CRM key-prefix maps.
- CRM relationship or field capability maps.
- Any static Salesforce CRM catalog facts compiled into L0.

### PR4: Backend Runtime Behavior Cleanup

Goal: remove CRM behavior branches from generic backend runtime.

PR4 should remove:

- Order activation branches.
- Campaign hierarchy branches.
- CRM related-list defaults in generic code.
- CRM analytics or historical cleanup branches in L0.
- Any CRM status/picklist/business lifecycle branch in records, query,
  automation, or permissions code.

### PR5: Frontend Cleanup, Guardrails, and Docs

Goal: keep the boundary from regressing.

PR5 should:

- Remove app-object assumptions from frontend runtime.
- Keep frontend app behavior metadata-driven.
- Add guardrail scans for CRM names in L0 code paths.
- Document allowed transitional exceptions.
- Ensure build/test checks enforce the clean boundary.

### External Pack Phase

After PR1 and PR2 are complete, the external pack/module design can begin.

That phase should define:

- Manifest format.
- Versioning.
- Dependency semantics.
- Upgrade semantics.
- Distribution mechanism.
- Pack registry behavior.
- Configured pack source resolution.
- Declarative apply/hash/verify semantics.
- Optional frontend extension contract, if packs need runtime UI extensions
  beyond generic renderer metadata.

## 12. Verification Requirements

The architecture is not valid without tests.

Required verification:

- Clean L0 boot applies only runtime-required packs and creates no CRM app
  objects, CRM actions, CRM flows, CRM permissions, CRM layouts, or CRM record
  types.
- Clean L0 still has generic platform primitives and platform permissions.
- Installing CRM pack creates CRM metadata and physical schema.
- `_System_InstalledPack` reports CRM active/current only after hash and verify
  pass.
- Changing any CRM-owned payload changes the CRM pack hash.
- Deleting or drifting immutable CRM-owned base metadata causes installed-pack
  verify to fail.
- Tenant/admin overrides on overlayable surfaces do not fail pack verify.
- Tenant/admin L2 rows are not deleted or rejected by CRM pack cleanup.
- Warm upgrades preserve legacy rows identified by stable business keys.
- Frontend shell can render installed app objects through metadata rather than
  hardcoded object lists.
- CRM action behavior resolves through generic primitives, Flow, field mappings,
  approval runtime, or generic invocable actions, not CRM-specific backend
  handlers in L0.

Useful guardrail scans should include CRM object names, CRM permission names,
hardcoded `__c` tenant fields, ProfileId/Profile-name behavior, RecordType
DeveloperName behavior, ApprovalProcess DeveloperName behavior, and SObject
branches in generic runtime code.

## 13. Decision Tests

Use these tests when reviewing new work:

1. Greenfield: can the platform boot with no app pack installed?
2. Pack ownership: if this metadata row is product default, which pack owns its
   base value?
3. Tenant ownership: if an admin changes this, is the change L2 rather than L0?
4. One path: does every caller read this fact from the same metadata path?
5. Hash/verify: if a pack applies it, does the same pack hash and verify it?
6. Runtime blindness: would this L0 code work for CRM, Jira, ITSM, and ERP
   without app-specific branches?
7. Salesforce primitive reuse: if Salesforce v63.0 already models the fact,
   are we using that representation instead of inventing a parallel one?
8. Override safety: will pack verify preserve legitimate L2 overrides on
   overlayable metadata surfaces?
9. Declarative pack boundary: is this app behavior encoded as metadata consumed
   by generic L0 machinery rather than pack-specific Go?

## 14. Open Mechanism Designs

This document is the architecture boundary contract. It is not the complete
implementation design for every mechanism required to reach that boundary.

The following mechanism designs must be completed before the architecture can
be treated as an implementation-ready plan.

### 14.1 Salesforce-Metamodel Scope

The platform is not an arbitrary application runtime. It is a
Salesforce-metamodel-compatible runtime.

Flow, ApprovalProcess, Profile, PermissionSet, SharingModel, RecordType,
layout/action metadata, and SObject describe semantics are Salesforce-shaped
primitives. Non-CRM apps such as Jira-like, ITSM, or ERP packs are in scope only
when they can be expressed through those primitives, or through new generic L0
primitives accepted into the platform.

Required follow-up:

- Define the supported non-CRM scope as Salesforce-shaped SObject applications,
  not arbitrary product runtimes.
- Remove or qualify examples that imply the platform can host non-Salesforce
  app models without adding generic L0 primitives.
- Define what counts as a generic L0 primitive versus app-specific runtime
  behavior.

### 14.2 Compound Action Semantics

CRM actions such as `ConvertLead` and `ActivateOrder` are the stress test for
the metadata-driven boundary. They are not simple buttons. They require
transactional orchestration, multi-object writes, field mappings, permission
checks, validation ordering, ownership changes, and post-commit side effects.

If these actions cannot resolve to CRM-specific backend handlers in L0, the
platform needs generic primitives that can express them.

Required follow-up:

- Define a generic compound-action or orchestration primitive, or prove that
  Flow plus existing field-mapping metadata is sufficient.
- Define transaction boundaries for multi-object actions.
- Define ordering for permission checks, validation rules, before-save flows,
  record writes, after-save flows, outbox writes, and audit events.
- Define rollback behavior when one object write fails.
- Add acceptance tests for `ConvertLead` and `ActivateOrder` implemented
  without CRM-specific L0 handlers.

### 14.3 Canonical Payload Hashing

`PayloadHash` is part of the installed-pack trust contract, so it must be
deterministic across machines, source locations, and pack implementations.

Required follow-up:

- Define the hash input model: manifest, ordered payload files, schema version,
  dependency versions, and any generated/coerced metadata.
- Define canonical serialization rules for JSON/YAML payloads, including field
  order, map order, null versus omitted fields, numeric formatting, whitespace,
  and default coercion.
- Decide whether the hash is based on raw file bytes, canonicalized metadata
  models, or both.
- Version the hash algorithm so future canonicalization changes are explicit
  compatibility events.
- Add tests proving byte-identical hashes for the same pack loaded from in-repo,
  local-path, and external source resolvers.

### 14.4 L1 Defaults vs L2 Overrides

Overlayable metadata needs a discriminator. Without one, verify cannot know
whether a changed live row is legitimate tenant/admin override or pack drift.

Required follow-up:

- Choose the override representation: owner/source stamp columns, base-hash
  columns, overlay tables, patch rows, change-tracking rows, or another explicit
  mechanism.
- Define mutability per metadata surface: immutable, default-only, additive, or
  overlayable.
- Define how effective metadata is computed from L1 base plus L2 overlay.
- Define how pack apply upgrades base defaults without overwriting tenant
  overrides.
- Define how verify detects drift in immutable/base facts while preserving
  legitimate overrides.
- Add tests for picklists, layouts, validation-rule activation, sharing
  defaults, permission grants, and action placements.

### 14.5 Pack Source Resolution

External packs require a resolver contract. "Unknown pack" must mean
"unresolvable from configured pack sources," not "not compiled into this
binary."

Required follow-up:

- Define a `PackSource` interface and supported source types for each phase:
  in-repo transitional bundle, local path, artifact registry, OCI/Git release,
  or other source.
- Define manifest lookup, version selection, dependency resolution, lock files,
  and source trust rules.
- Define offline behavior and cache behavior.
- Define how setup/apply and verify use the same resolver.
- Define how missing, incompatible, or revoked pack manifests affect
  `_System_InstalledPack`.

### 14.6 Installed-Pack State Machine

`_System_InstalledPack` is not the source of payload truth. It is an install
registry, last-applied evidence record, and derived health cache.

Required follow-up:

- Define states such as `installing`, `active`, `failed`, `stale`,
  `unresolved`, and any migration-only states.
- Define the writer for each state transition.
- Define which readers may trust stored state and which must recompute hash or
  verify live metadata.
- Define how server readiness, `setup verify`, and background drift checks
  interact.
- Define concurrency and locking for apply/verify operations.

### 14.7 Migration and Dual-Read Discipline

The refactor must not leave two authoritative read paths for the same fact.

Required follow-up:

- For each PR, state the single authoritative read path after the PR lands.
- For PR1, create the pack-owned reader even if its backing files remain in
  legacy locations.
- For PR2, change only the backing location behind that reader.
- Remove old readers in the same PR that switches the caller.
- Add tests that fail when both old and new paths can seed or verify the same
  metadata surface.

### 14.8 Upgrade and Uninstall Scope

Pack upgrade semantics cannot be postponed entirely because migration to the
source-backed CRM pack is a warm upgrade for existing deployments.

Required follow-up:

- Define v1 warm-upgrade guarantees: idempotent reconcile, no record-data loss,
  stable business-key matching, and owner-scoped stale cleanup.
- Define how destructive metadata changes are represented and approved.
- Define whether uninstall is unsupported in v1. If unsupported, setup and
  tooling APIs must say so explicitly.
- Define data-retention behavior for any future uninstall support.
- Add tests for legacy ids, changed payload hashes, stale pack rows, and tenant
  data preservation during upgrade.

### 14.9 Frontend Extension Scope

The metadata-driven frontend shell can render Salesforce-shaped describe,
layout, action, list-view, and page metadata. It should not imply arbitrary UI
surfaces are supported.

Required follow-up:

- Define the supported generic renderer set.
- Remove or qualify app examples that need unsupported UI primitives.
- If packs need board, backlog, Gantt, or other specialized views, design a
  frontend extension contract before claiming those packs are supported.

## 15. Final Position

The target architecture is:

- L0 is a generic Salesforce-compatible metadata runtime.
- L1 packs define product application shape.
- L2 overlays define tenant/admin customizations.
- L3 stores record data.

The platform binary should know how to interpret objects, fields, flows,
validation rules, permissions, layouts, actions, approval processes, and
installed packs. It should not know what a CRM is.

Application packs should be declarative payloads resolved from configured pack
sources. Pack-specific Go inside the platform binary is allowed only as a
temporary migration adapter, not as the final extension model.

The current safe path is not to jump directly to external repositories. The
safe path is:

1. Establish a trustworthy in-repo app-pack boundary.
2. Move CRM payload behind that boundary.
3. Remove compiled and runtime CRM knowledge from L0.
4. Add guardrails.
5. Externalize packs once the ownership and verification contracts are real.
