# nexuscrm-pingcap-pack Agent Guide

## Repository Shape

`nexuscrm-pingcap-pack` is not the NexusCRM platform/runtime repository.

This repository is the PingCAP downstream stack repository:

- it carries PingCAP-specific pack/configuration inputs
- it assembles the final tested pack artifact
- it supplies sealed L2 semantics to the independent local deployment scripts
- it drives staging remote deploy with `main` as the default

Use `README.md`, `docs/stack-test-model.md`, `stack.lock.json`, and `scripts/`
to discover the live stack contract. Keep this file focused on rules that are
easy to miss from the repo layout alone.

---

## NexusCRM platform — repository & module map

The platform is split **by release boundary** into seven repositories across
three layers. Rule of thumb: a *repository* is a release boundary, a *Go module*
is an architecture boundary, a *package* is an import boundary. Dependencies
always point **down** — `L2 → L1 → L0` — and inside L0 everything depends on the
neutral contract while nothing depends back up.

- **L0 — platform mechanism** (app-blind: knows metadata / records / policy, not
  Salesforce and not any tenant). Five repos.
- **L1 — product / dialect** (the Salesforce-compatible CRM shape). One repo.
- **L2 — tenant delivery** (one customer's overlays + final pack + deploy). One repo.

### The seven repositories

| Repo | Layer | Module path / pin | Owns |
|---|---|---|---|
| **NexusCRM** (core) | L0 | `github.com/nexuscrm/*` (workspace) | The engine + host + frontend: `metadata`, `records`, `automation`, `analytics`, `permissions`, `persistence`, `storage`, `db`, `auth`, `audit`, `events`, `files`, `outbound`, `kernel`, the `backend` HTTP host, the React `frontend`, and `tools/boundarygate`. The mutually-recursive engine SCC stays here as a modular monolith. |
| **nexuscrm-contract** | L0 | `github.com/pingcap-inc/nexuscrm-contract` `v0.1.0` | Neutral typed contract / extension IR. Dependency root; runtime-pure (no `database/sql`, no `net/http`). Everything imports it; it imports nothing back. |
| **nexuscrm-controlplane** | L0 | `…/nexuscrm-controlplane` `v0.1.4` | Pack install / upgrade / migrate / verify / drift lifecycle + schema bootstrap (`bootstrapimpl`). Backend-free. |
| **nexuscrm-api-host** | L0 | `…/nexuscrm-api-host` `v0.1.0` | Transport host primitives: dialect transport binding, auth / session / permission middleware, websocket, security headers, version routes. |
| **nexuscrm-connectors** | L0 | `…/nexuscrm-connectors` `v0.1.0` | Integration runtime: inbound events / routing, webhooks, outbound callouts, named credentials, connector registry (capabilities / health / throttle / sync state). |
| **nexuscrm-app-packs** | L1 | pack repo + `…/dialects/dialect-*` modules | Official Salesforce-compatible CRM packs, the SF CRM dialect Go modules (`dialect-sf-crm`, `dialect-sf-crm-soql`), app frontend source, and a sealed CI-only non-CRM conformance fixture. Consumed via `official_repo:<pack>` + `NEXUSCRM_OFFICIAL_APP_PACKS_ROOT`. |
| **nexuscrm-pingcap-pack** | L2 | tenant pack repo | PingCAP tenant overlays, final merged pack assembly, bootstrap / deploy orchestration. Nothing depends on it. |

### Where each concern lives (routing table)

| If you need to work on… | Go to |
|---|---|
| Metadata engine / schema interpreter | NexusCRM → `nexuscrm-metadata` |
| Records CRUD / query execution | NexusCRM → `nexuscrm-records` |
| Flow / validation / automation | NexusCRM → `nexuscrm-automation` |
| Reports / dashboards / analytics | NexusCRM → `nexuscrm-analytics` |
| Permissions / FLS / RLS / sharing | NexusCRM → `nexuscrm-permissions` |
| Persistence / storage / DB driver | NexusCRM → `nexuscrm-persistence`, `nexuscrm-storage`, `nexuscrm-db` |
| Auth / session / JWT | NexusCRM → `nexuscrm-auth` (+ `nexuscrm-api-host` middleware) |
| Audit / events / outbox | NexusCRM → `nexuscrm-audit`, `nexuscrm-events`, `nexuscrm-outbound` |
| Files / attachments | NexusCRM → `nexuscrm-files` |
| Backend HTTP host, REST handlers, engine wiring | NexusCRM → `backend` |
| React runtime + host SDK | NexusCRM → `frontend` (`src/host-sdk`, `src/host`) |
| Module-boundary gate / policy | NexusCRM → `tools/boundarygate` |
| Neutral types / contract / IR | **nexuscrm-contract** |
| Pack install / upgrade / verify / drift / bootstrap | **nexuscrm-controlplane** |
| Transport host, dialect binding, middleware, websocket | **nexuscrm-api-host** |
| Connectors / webhooks / callouts / named credentials | **nexuscrm-connectors** |
| Salesforce CRM dialect / SObject·SOQL skin / official packs / app frontend | **nexuscrm-app-packs** (L1) |
| PingCAP tenant config / final pack / deploy | **nexuscrm-pingcap-pack** (L2) |

### Dependency direction (hard rules)

- `nexuscrm-contract` depends on the **Go stdlib only**; every other L0 module imports it.
- `nexuscrm-controlplane`, `nexuscrm-api-host`, `nexuscrm-connectors` depend on the
  contract (+ pinned `github.com/nexuscrm/*` core modules) but **must not import `backend`**.
- `NexusCRM/backend` consumes the published L0 modules as **version pins** (no local
  `replace` on `main`).
- `nexuscrm-app-packs` (L1) builds on the platform's public contract; **L0 must not import L1**.
- `nexuscrm-pingcap-pack` (L2) consumes NexusCRM + app-packs **by ref**; nothing depends on it.
- Inside NexusCRM the engine modules form a mutually-recursive SCC and are kept as
  **one modular monolith** — deliberately *not* split into separate versioned repos.

### Phase status — don't mistake interim state

- **Phase 1 — physical / release split: ✅ merged to `main`.** Seven-repo topology,
  published L0 modules consumed via real version pins, `boundarygate` Forbidden-edge = 0.
- **Phase 2 — L0 / L1 / L2 semantic purity: ⏳ not started.** Rule: *mechanism stays in
  L0; Salesforce / product / tenant shape moves to L1 / L2.* The binding execution
  contract (sequence **L0-A … L0-F**, 12-point DoD) lives in NexusCRM
  `docs/architecture/repo-split.md`. **Do not start Phase 2 behavior migration outside
  that contract.**

---

## Synced Upstream Context

This repo keeps a generated, namespaced copy of upstream `NexusCRM` context for
reference only:

- upstream agent docs: `docs/upstream/nexuscrm/CLAUDE.md` and
  `docs/upstream/nexuscrm/AGENTS.md`
- upstream design/open-source docs: `docs/upstream/nexuscrm/docs/`
- sync manifest and path mapping: `docs/upstream/nexuscrm/README.md` and
  `docs/upstream/nexuscrm/SYNCED_FROM.json`

Those files help agents reason about the selected upstream runtime. They do not
replace this downstream guide, and they do not move PingCAP-specific ownership
out of this repo. Upstream `.claude/**` and `.agents/**` directories are not
copied.

---

## Architecture Rule

This repo is a downstream configuration-and-orchestration repo.

The core split is:

- **upstream `NexusCRM`**
  - platform/runtime/control-plane authority
- **official app packs `nexuscrm-app-packs`**
  - official standard CRM pack authority
- **downstream `nexuscrm-pingcap-pack`**
  - PingCAP-specific metadata/config inputs
  - final pack assembly
  - D3 deployment semantics and remote deploy orchestration

Do not treat this repo as if it owns the platform binary.

When describing the system, keep the two repositories aligned:

1. upstream builds and serves the generic platform/runtime
2. downstream chooses the upstream ref to use
3. downstream assembles a final pack from the official standard pack + downstream inputs
4. local and remote deploy paths test that final assembled result

---

## Canonical Test Object

The canonical test object in this repo is not the raw official standard pack.

It is:

1. upstream `NexusCRM`, defaulting to `main`
2. official pack from `pingcap-inc/nexuscrm-app-packs`, defaulting to `main`
3. plus downstream checked-in `packs/Salesforce-PingCAP-L2`
4. resulting in the final downstream pack `packs/NOT-EDIT-final-merged-packs`

Current authority files:

- `stack.lock.json`
- `scripts/build_pingcap_salesforce_sample_pack.sh`
- `scripts/local_app_install.py`
- `docs/stack-test-model.md`

If these disagree with older issue text or stale notes, prefer the current tree.

---

## Repository Boundary

Facts belong in different places:

- **generic platform/runtime behavior**
  - belongs upstream in `NexusCRM`
- **generic standard CRM pack content**
  - belongs in `pingcap-inc/nexuscrm-app-packs`
- **PingCAP-specific pack shape, assembly choices, deploy conventions**
  - belongs in this downstream repo

If a fix is needed because the upstream runtime, setup flow, standard pack, or
platform contract is wrong, send the fix upstream. Do not permanently paper over
upstream substrate gaps in downstream shell scripts unless the transitional
boundary is explicit.

---

## Sibling Repo Model

This stack is designed around sibling repositories, not nesting:

```text
.../NexusCRM
.../nexuscrm-app-packs
.../nexuscrm-pingcap-pack
```

The downstream pack builder and deploy paths default to resolving:

- upstream runtime as `../NexusCRM`
- official app packs as `../nexuscrm-app-packs`

Local convenience symlinks do not change the authority model. A path such as
`temp/nexuscrm-pingcap-pack` is only a workspace shortcut to the downstream repo.

---

## Local / Deploy Contract

For local functional/API/browser verification, use only the independent
entrypoints documented in [Independent local deployment](docs/local-independent-deployment.md):
`scripts/local_l0.py` for L0/database initialization and L0 process lifecycle,
`scripts/local_app_install.py` for first App installation, and
`scripts/local_l1.py` for AppService/frontend deployment and L1 lifecycle.
The first-install order is L0 `init`, App installation, L0 `activate`, L1
`deploy`. Flags and prerequisites belong in that runbook and each CLI's `--help`.
Local images/tags are accepted without deployment version or provenance checks.
This path has no metadata upgrade, rollback, or partial-install recovery;
component `down` preserves the separate database project and volume.

Test and Staging may be used for production-simulation verification: exercise
production-relevant topology, migrations/upgrades, CRUD/worker journeys,
restart/failure behavior, and external integrations through the target's
checked-in controllers and acceptance runners. Use the environment matrix in
[deployments/README.md](deployments/README.md) and its linked runbooks; record
actual image digests, metadata revision, topology, cases and results. Local
functional success alone is not release/provenance or upgrade evidence. A
standalone Test component operation proves only its selected scope; formal
Staging-to-Prod promotion still requires the signed BOM and Staging evidence.

Both paths install the final assembled L2 pack. Keep business fixtures in
case-local setup; the new local installer does not apply deployment Seed profiles.

Default ref policy:

- local verification selects explicitly supplied images, installation settings,
  and assembled packs; a signed BOM is not a local prerequisite
- pack build defaults to latest `main`
- EXP shared-environment deploy is no longer driven by an automatic GitHub
  Actions workflow
- classic staging remote deploy is main-only: downstream must resolve to
  `origin/main`, and upstream / official app-pack refs are derived from that
  downstream commit's final-pack `.build-lineage.json`; manual staging ref
  overrides, release-tag replay, and patch-bundle ref overrides are forbidden

The final-pack build contract consists of `stack.lock.json`, the checked-in L2
source, the generated final pack, and its `.build-lineage.json`. Reproduce the
committed artifact from the exact lineage commits with
`scripts/build_pingcap_salesforce_sample_pack.sh --from-final-pack-lineage`.
Build lineage is provenance only; it does not select a release. Local
iteration remains a non-release path.
Remote Test1/Test3 D3 full deployment, production-to-Test database refresh, and SFCRM
AppService replacement run from the exact controller commit selected by an
authorized manual dispatch of `.github/workflows/deploy-test-serviceification.yml`;
the dispatch event's `github.sha` is the controller authority. Full deployment
consumes an already-signed resolved BOM. Test1 and Test2 also have standalone
component management: independent L0 API/Worker management, AppRelease/L2
metadata installation, and L1 AppService/UI deployment. Their L0 managers
consume a published Engine Release without a BOM, pack apply, or database
initialization. See `docs/test1-standalone-deployment.md` and
`docs/test2-standalone-deployment.md` for the separate workflows. Test2 has
completed its standalone transition; the one-time transition controller is
retired. Integration has one separate stop/deploy owner,
`manage-test2-integration.yml`. It consumes one published Integration digest and
recreates the process from canonical environment and Runtime configuration.
It never selects a Core branch, copies old container configuration, changes
metadata, or operates sibling processes. Test2's nine L0 work-family roles
include Permission Authoring recovery and run in nine Worker processes. The old
Layout publication Worker remains retired. CRM 0.72 restores synchronous Layout
administration through `runtime.layout.authoring.v2`; its selected Runtime and
AppRelease must be upgraded together through the stopped sequence. Saves do not
publish Catalog or require a Layout Worker. See the Test2 runbook for this boundary.
Test2 is no longer accepted by
the shared full-D3 workflow; use its standalone component controllers for deployment.
The independent `refresh-test-database-from-production.yml` remains a database
maintenance operation, not a deployment path. The selected-ref D3 controller may replace
only the SFCRM AppService process with one already-published exact
`ghcr.io/pingcap-inc/nexuscrm-sfcrm-appservice` digest. That operation never
checks out product source, builds or publishes an image, or claims signed L1
Application/AppRelease provenance. That diagnostic changes no database,
Catalog, application metadata, or frontend. The next full signed-BOM
deployment clears its transient image override. Staging and production do not
inherit this selected-ref private-Test controller model.
Full serviceified D3 deployments to Test, Staging, and multi-prod consume one
exact signed resolved BOM; the BOM independently selects the L0 Engine, signed
L1 AppRelease, and L2 metadata tree. Private-Test process diagnostics do not
become release deployments. The D3 Staging release Tag carries that resolved-BOM
reference, the successful Staging run/artifact identity, and the nullable
`patchBundle` path/digest used by that run.
The Git Tag target must equal the signed BOM's exact L2 metadata commit. Prod
re-downloads and verifies that artifact, independently verifies BOM-L2/Tag
equality, and converges the target while the previous runtime remains live.
After target cutover and public readback, it stops and verifies only the
pending-authority-bound predecessor, re-proves the public target, then publishes
current authority. Retirement failure preserves pending authority for an
exact-Tag retry. Stopped containers and release files remain; retention is a
separate operation. See `deployments/online/runbooks/prod.md` for the exact scope.
Prod exposes only the release Tag as input. When the Tag binds a Patch Bundle,
the Prod Operator resolves it from the exact Tag-selected L2 checkout and
replays it immediately before Catalog apply. The release-wide lock covers legacy
handoff and release-authority reconciliation through closeout. The first D3
release is a one-way handoff from any still-live Classic runtime. A durable
`multi_prod/d3/in-progress.json` owns the target until Prod proves public target
readback, publishes the D3 current marker, and clears the pending authority;
the Tag does not reinterpret final-pack lineage.
Use `scripts/verify_final_pack_regen.py --double-build` when exact rebuild
evidence is required. This is an explicit release-engineering check, not a
credentialed pull-request workflow. Formal SIT and deployment authority remain
with the signed resolved BOM paths described above.

Do not add another local deployment wrapper or duplicate these entrypoints.
Test3 also has an explicitly authorized host-only development mode in the
existing `run_serviceification_d3_test.py` entrypoint: `prepare-local` and
`--local-unsigned`. It consumes immutable local image IDs and a separate
unsigned manifest, preserves the database and normal deployment checks, and
cannot be used by Actions, Test1/Test2, Staging or Prod. It is not signed release
or promotion evidence. See `deploy/offline/test/README.md`.
Keep remote release deployment semantics in their existing shared D3 machinery.

---

## Deployment Seeding Rule

When a task touches data that should exist after local App installation or a
remote deploy, load `.agents/skills/deployment-seeding/SKILL.md` before editing.

Use the current seed contract instead of ad hoc deploy scripting:

- stable, shared environment-contract data belongs in the environment baseline
  seed under `deployments/offline/runtime/seeds/`
- run-specific or mutation-heavy test data belongs in case-local setup
- PingCAP-specific metadata belongs in `packs/Salesforce-PingCAP-L2`, followed
  by regenerating `packs/NOT-EDIT-final-merged-packs`
- generic runtime behavior belongs upstream in `NexusCRM`
- generic standard CRM metadata belongs in `pingcap-inc/nexuscrm-app-packs`

Deployment seed changes must stay idempotent and environment-agnostic: use
deterministic `key` and `match` values, use `{{ record.some_key.id }}`
references instead of generated IDs, keep secrets out of tracked YAML, and do
not query live staging to build deploy seed data.

### Deploy Seed agent publication gate

All deploy-time Seed mutations must use the canonical Seed Dispatcher. This is
a mandatory agent publication gate even when no CI job enforces it:

- every deploy-owned Seed source under `deployments/offline/runtime/seeds/`
  must be declared by at least one module in
  `deployments/offline/runtime/seeds/deploy-seed-catalog.json`; intentional
  shared sources must remain explicit and reviewed
- every Seed-owned module must declare a reviewed Dispatcher adapter, explicit
  profile invocation, and complete expected-callsite ownership
- deployment drivers must not call module-specific Seed appliers
  directly; they may invoke only `scripts/apply_deploy_seed.py` or the reviewed
  `apply_deploy_seed_phase()` integration
- a deploy-time database/API data mutation that could otherwise be mistaken
  for Seed must be registered as an explicit Catalog exclusion with a named
  owner and reason
- run-scoped and case-specific fixtures must stay outside deploy profiles and
  must not be promoted into the environment baseline solely to make a test pass
- secrets must never appear in tracked Seed sources or Seed evidence; secret
  provisioning is a separate operation, not Seed

Before publishing or updating a PR that changes deploy Seed data, adapters,
Catalog ownership, or Seed callsites, agents must run the mandatory checks in
`.agents/skills/deployment-seeding/SKILL.md`. Agents must not publish while an
applicable check is failing. The PR body must report the dataset ownership
classification, Catalog module and profiles, canonical apply path,
idempotency/match contract, and exact validation results.

---

## Working Standard

- Read `docs/stack-test-model.md` before changing the stack shape.
- Read `stack.lock.json` before changing bootstrap or deploy behavior.
- Keep `stack.lock.json`, build scripts, docs, and workflow inputs aligned.
- Prefer failing closed when refs, pack inputs, or runtime fingerprints do not match.
- Do not silently switch the tested artifact from final pack back to raw fragments.
- If local notes conflict with tracked repo docs, reconcile the repo docs.

Useful paths:

- `docs/stack-test-model.md`
- `stack.lock.json`
- `scripts/build_pingcap_salesforce_sample_pack.sh`
- `scripts/local_app_install.py`
