feat: PII, tenancy, search/API, help bundle, health, full GDPR erase, bulk-load profiles #13

Merged
KofTwentyTwo merged 8 commits from feature/pii-tenancy-api into develop 2026-10-14 20:00:15 +00:00
KofTwentyTwo commented 2026-10-14 19:57:44 +00:00 (Migrated from github.com)

Summary

P4 part B of the qbit-crm 0.2.0 design:

  • PII: every personal field is classified DataClassification.PII. A field-name regression test catches any unclassified email, phone, name, address or LinkedIn field.
  • Tenancy (optional, off by default):
    • withTenantSecurityKeyType / withTenantAllAccessKeyName add a tenantKey record lock to main and settings tables, and join-chain locks to sub-tables.
    • The columns live in a separate crm-tenancy Liquibase changelog.
    • Isolation tests cover tenant A/B and all-access.
  • Contingent dropdowns: the deal's primary contact is limited to the deal's company. Stage and outcome are also validated server-side.
  • Search and API: main tables have search fields. When withApiName is set, main and settings tables are published through the QQQ API middleware, with an API-to-admin field parity test.
  • ESB: deferred. RC.6 cannot exclude classified values, so configuring it fails with a clear message.
  • Help: a 391-entry help/qbit-crm.yaml bundle in the qqq#1198 format, with a STAND-IN(core=QRun-IO/qqq#1198, removeBy=4.1.0-RC.9) loader and a completeness test.
  • Health: checks for transport readiness, sequence lag and scoring freshness (STAND-IN(core=QRun-IO/qqq#1199, removeBy=4.1.0-RC.9)).
  • GDPR: Erase Personal Data now covers contacts, web leads, list members, email messages, activities, consent text, external mappings, enrollments and core audit copies. A test asserts that no original PII string remains.
  • Bulk load: starter profiles for contacts, companies and deals, seeded into the host's saved-profile table when it exists.
  • Host-adoption fixes found while installing on kof22 (WB#224):
    • the app is a plain QAppMetaData, so it can be attached as a child app
    • home table columns have no fixed widths
    • emails are 320 characters and UTM fields 500
    • lead source names taken from UTM are capped at 100

Codex implemented this; Claude reviewed it and wrote the host-adoption fixes.

Test plan

  • ./mvnw verify: 454 tests, 0 failures; JaCoCo 92%; checkstyle and guardrails pass, including G4 stand-in markers

🤖 Generated with Claude Code

## Summary P4 part B of the qbit-crm 0.2.0 design: - **PII:** every personal field is classified `DataClassification.PII`. A field-name regression test catches any unclassified email, phone, name, address or LinkedIn field. - **Tenancy (optional, off by default):** - `withTenantSecurityKeyType` / `withTenantAllAccessKeyName` add a `tenantKey` record lock to main and settings tables, and join-chain locks to sub-tables. - The columns live in a separate `crm-tenancy` Liquibase changelog. - Isolation tests cover tenant A/B and all-access. - **Contingent dropdowns:** the deal's primary contact is limited to the deal's company. Stage and outcome are also validated server-side. - **Search and API:** main tables have search fields. When `withApiName` is set, main and settings tables are published through the QQQ API middleware, with an API-to-admin field parity test. - **ESB:** deferred. RC.6 cannot exclude classified values, so configuring it fails with a clear message. - **Help:** a 391-entry `help/qbit-crm.yaml` bundle in the qqq#1198 format, with a `STAND-IN(core=QRun-IO/qqq#1198, removeBy=4.1.0-RC.9)` loader and a completeness test. - **Health:** checks for transport readiness, sequence lag and scoring freshness (`STAND-IN(core=QRun-IO/qqq#1199, removeBy=4.1.0-RC.9)`). - **GDPR:** Erase Personal Data now covers contacts, web leads, list members, email messages, activities, consent text, external mappings, enrollments and core audit copies. A test asserts that no original PII string remains. - **Bulk load:** starter profiles for contacts, companies and deals, seeded into the host's saved-profile table when it exists. - **Host-adoption fixes found while installing on kof22 (WB#224):** - the app is a plain `QAppMetaData`, so it can be attached as a child app - home table columns have no fixed widths - emails are 320 characters and UTM fields 500 - lead source names taken from UTM are capped at 100 Codex implemented this; Claude reviewed it and wrote the host-adoption fixes. ## Test plan - [x] `./mvnw verify`: 454 tests, 0 failures; JaCoCo 92%; checkstyle and guardrails pass, including G4 stand-in markers 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.