Bite 30K.3B — Manual Promotion Test Suite - RodrigoMattosoSilveira/EnterpriseRemoteSystems GitHub Wiki

ERS Bite 30K.3B — Manual Promotion Test Suite

Bite: 30K.3B — Physical Legacy Schema Removal and Migration Hardening
Branch: 30k3b (the trailing underscore in the handoff is an artifact)
SHA: 1b7f2089abb8cfb14fbc2f21b4dee4806bc8e253
Purpose: Prove that ERS remains usable after physical removal of the legacy identity schema, that all new identity writes use only the canonical identity graph, and that normal backend startup does not recreate retired structures.

Preconditions

  • make local-check passes.
  • CI passes.
  • Use the LOCAL application database at backend/data/app.db.
  • Do not reset the LOCAL database between these tests.
  • Keep any pre-000069 backup until all 30K.3B promotion tests pass.
  • Use separate browser sessions where requested so Application Administrator, Tenant Administrator, and Person-only sessions do not contaminate one another.
  • Application Administrator login: [email protected].
  • Tenant Administrator login: [email protected].
  • Tenant context used by this suite: Default Tenant.
  • Use the currently configured LOCAL passwords for the two pre-existing administrator accounts.
  • No additional fixture/seed script is required.

New deterministic Person used by this suite

Field Value
First Name ThirtyKThreeB
Last Name ManualPerson
Initial Nickname 30K3B Physical
Final Nickname 30K3B Canonical
CPF 27182818205
RG 30K3B-MANUAL-001
Cellular 11987654321
Email / Login [email protected]
Status Active
Street 1 Rua Manual 30K3B
Street 2 leave blank
State Pará
City Manual City
CEP 66000-000
Country Brasil
Bank Name Manual Bank
Bank Number 001
Checking Account 30K3B-001
PIX [email protected]
Emergency Contact Name Manual Emergency
Emergency Cellular 11999990000
Emergency Email [email protected]
Temporary Password Manual-30K3B-Password-1!
Permanent Password Manual-30K3B-Permanent-2!

Test 01 — Existing Application Administrator survives 000069 and remains GLOBAL-only

Identity and context

  • Browser Session A.
  • Sign in as Application Administrator: [email protected].
  • Select Global administration. Do not select Default Tenant.

Steps

  1. Sign out any existing user in Browser Session A.
  2. Sign in as [email protected] with the currently configured LOCAL password.
  3. Confirm the context selector shows Global administration.
  4. Using the application navigation, open Authentication.
  5. In the Authentication search/filter field, enter [email protected].
  6. Find the [email protected] Authentication Account.
  7. Inspect its identity-boundary information.
  8. Confirm:
    • Person binding says None — no Person linked.
    • Tenant Actor bindings says None — no Tenant Actors linked.
    • the linked Actor is shown as a Global Actor.
    • Actor ID and Actor Key are displayed.
    • there is no Primary, Primary Actor, or equivalent per-Actor primary indicator.
  9. Using the application navigation, open Tenants.
  10. Confirm the Tenants page loads normally and does not produce a 403, 500, blank page, or application error.

Expected result

The Application Administrator still resolves through the canonical GLOBAL AccountActor → APPLICATION Actor path after migration 000069 and has not acquired Person, Membership, or Tenant Actor identity.

PASS criteria

  • Authentication and Tenants load successfully.
  • [email protected] has no Person binding.
  • [email protected] has no Tenant Actor binding.
  • its Actor is GLOBAL.
  • no obsolete per-Actor primary concept is displayed.

Promotion blocker

Any Tenant Person/Membership/Actor binding on the Application Administrator, inability to load Authentication or Tenants, or any UI dependency on a primary Actor blocks promotion.


Test 02 — Existing Tenant Administrator identity survives physical legacy-schema removal

Identity and context

  • Browser Session B.
  • Sign in as Tenant Administrator: [email protected].
  • Select Default Tenant.

Steps

  1. Sign out any existing user in Browser Session B.
  2. Sign in as [email protected] with the currently configured LOCAL password.
  3. Confirm the selected Tenant is Default Tenant.
  4. Using application navigation, open People.
  5. Confirm the page states or otherwise clearly shows that the People directory is operating in the selected Tenant boundary.
  6. In Filter people, enter: [email protected]
  7. Confirm the Tenant Administrator's Person appears.
  8. Open that Person through the UI.
  9. Confirm the Person page loads and displays the existing Person information without an application error.
  10. Use Back to People.
  11. Using application navigation, open Collaborators.
  12. Confirm the Collaborators page loads in Default Tenant without a 403, 500, or identity-resolution error.

Expected result

A pre-existing Tenant Authentication Account still resolves through AccountActor → Membership → Global Person after the removed legacy columns/table no longer exist.

PASS criteria

  • the Tenant Administrator can sign in;
  • Default Tenant is usable;
  • People search finds the Tenant Administrator through current identity/search projections;
  • the Person page opens;
  • Collaborators loads normally.

Promotion blocker

Failure to authenticate, failure to resolve the Tenant Administrator's Person/Membership, incorrect Tenant boundary, or any server/UI error blocks promotion.


Test 03 — Create and update a new canonical Person after legacy people removal

Identity and context

Steps

  1. Using application navigation, open People.
  2. Click New Person.
  3. On the Personal tab enter:
    • First Name: ThirtyKThreeB
    • Last Name: ManualPerson
    • Nickname: 30K3B Physical
    • CPF: 27182818205
    • RG: 30K3B-MANUAL-001
    • Cellular: 11987654321
    • Email: [email protected]
    • Status: Active
  4. Click Create Person.
  5. Confirm the application returns to People and reports that the Person was added.
  6. Open the newly created Person through the UI.
  7. Open Address and enter:
    • Street 1: Rua Manual 30K3B
    • Street 2: leave blank
    • State: Pará
    • City: Manual City
    • CEP: 66000-000
    • Country: confirm Brasil
  8. Open Bank and enter:
    • Bank Name: Manual Bank
    • Bank Number: 001
    • Checking Account: 30K3B-001
    • PIX: [email protected]
  9. Open Emergency and enter:
    • Emergency Contact Name: Manual Emergency
    • Emergency Cellular: 11999990000
    • Emergency Email: [email protected]
  10. Return to Personal.
  11. Change Nickname from 30K3B Physical to: 30K3B Canonical
  12. Click Save Changes.
  13. Confirm Person updated successfully. appears.
  14. Confirm Profile Status shows the Person is complete/eligible to become a Collaborator.
  15. Click Back to People.
  16. In Filter people, enter: 30K3B Canonical
  17. Confirm exactly the newly created Person is found.
  18. Clear the filter.
  19. Enter: 30K3B Physical
  20. Confirm the Person is not returned under the old nickname.
  21. Clear the filter.
  22. Enter: [email protected]
  23. Confirm the same Person is returned.

Expected result

Creation and update operate entirely through global_people + person_tenant_memberships, and the canonical people_search_index reflects both insertion and subsequent Person updates without the legacy people table.

PASS criteria

  • Person creation succeeds.
  • all profile sections save.
  • Person becomes Collaborator-eligible.
  • the new nickname is searchable.
  • the old nickname no longer matches after the update.
  • email search finds the Person.

Promotion blocker

Any create/update failure, stale search result after nickname change, inability to find the Person by canonical search, or any error mentioning people, legacy_person_id, or a missing legacy column blocks promotion.


Test 04 — Create a new Authentication Account and complete temporary-password flow

Identity and context

  • Return to Browser Session A.
  • Sign in as [email protected].
  • Context: Global administration.

Steps

  1. If necessary, sign in again as [email protected].
  2. Confirm Global administration is selected.
  3. Using application navigation, open Authentication.
  4. In Create account, enter:
    • Target Tenant: Default Tenant
    • Person login email: [email protected]
    • Temporary password: Manual-30K3B-Password-1!
  5. Click Create account.
  6. Confirm the account is created without an application error.
  7. In the Authentication account filter/search, enter: [email protected]
  8. Open/inspect the resulting account.
  9. Confirm:
    • the Person binding is present and refers to ThirtyKThreeB ManualPerson;
    • exactly one Tenant Actor binding exists for Default Tenant;
    • that binding is a Tenant Actor;
    • Actor ID and Actor Key are shown;
    • no per-Actor Primary/primary property is shown.
  10. Open a separate Browser Session C.
  11. Sign in as: [email protected] using: Manual-30K3B-Password-1!
  12. Confirm ERS directs the user to Change password.
  13. Enter:
    • Current password: Manual-30K3B-Password-1!
    • New password: Manual-30K3B-Permanent-2!
    • Confirm new password: Manual-30K3B-Permanent-2!
  14. Click Change password.
  15. Confirm ERS returns to sign-in and reports that the password changed.
  16. Sign in again as [email protected] using: Manual-30K3B-Permanent-2!
  17. Confirm the user lands on or can navigate to People and sees only their own Person self-service record.
  18. Confirm the user does not have Application Administration functions such as Authentication or Tenants.

Expected result

A brand-new Account can be created after physical removal of auth_user_accounts.actor_id, auth_account_actors.is_primary, and the Actor legacy identity fields. Temporary-password and canonical AccountActor resolution work normally.

PASS criteria

  • account creation succeeds;
  • exactly one Default Tenant AccountActor binding is visible;
  • forced password change succeeds;
  • permanent-password sign-in succeeds;
  • Person self-service resolves correctly;
  • no unintended administrative privilege appears.

Promotion blocker

Any NOT NULL/missing-column error, failure to set/change the temporary password, incorrect Person or Tenant binding, inability to sign in, or privilege leakage blocks promotion.


Test 05 — Create a Collaborator Journey using only Membership identity

Identity and context

Steps

  1. Return to Browser Session B.
  2. Confirm [email protected] is signed in and Default Tenant is selected.
  3. Using application navigation, open Collaborators.
  4. Click Add.
  5. In Find eligible Person by nickname, enter: 30K3B Canonical
  6. Select ThirtyKThreeB ManualPerson.
  7. In the Journey section enter/select:
    • Journey Start Date: 2026-09-10
    • Status: Active
    • Sector: Mining
    • Location: Main Mine
    • Task: Miner
  8. In the Payment section select/enter:
    • Payment Method: Daily wage
    • Payment Value: 100
  9. In Notes, enter: 30K.3B physical-schema-removal manual promotion test
  10. Click Create Collaborator.
  11. Confirm the Collaborator is created.
  12. Open the created Collaborator Journey.
  13. Record the displayed Journey Code.
  14. Confirm the page displays:
    • ThirtyKThreeB ManualPerson;
    • a canonical Person ID;
    • the Journey information entered above.
  15. Confirm Legacy Person ID is not displayed anywhere on the Collaborator detail page.
  16. Click Current Account.
  17. Confirm Person Current Account loads for this Person/Journey without an error.
  18. Return to Browser Session C.
  19. If necessary, sign in as [email protected] using: Manual-30K3B-Permanent-2!
  20. Using application navigation, open Collaborators.
  21. Confirm the page is the user's self-service My Collaborator Journeys view.
  22. Open the 30K3B Canonical Journey.
  23. Confirm its Journey Code matches the value recorded in step 13.
  24. Open Current Account.
  25. Confirm the Current Account loads successfully.
  26. Do not reprovision or reset this Authentication Account during this test.

Expected result

collaborator_journeys.membership_id is sufficient to link the Journey to the same canonical Person and existing Authentication Account. No legacy collaborator_journeys.person_id is required.

PASS criteria

  • Journey creation succeeds.
  • the correct Person is displayed.
  • no Legacy Person ID appears.
  • Tenant Administrator can open Current Account.
  • the existing Person account automatically gains its Collaborator self-service view without Authentication reprovisioning.
  • the same Journey Code appears for the user.

Promotion blocker

Any need to reprovision Authentication, wrong Person/Journey resolution, appearance of a legacy Person identifier, missing Current Account, or identity/authorization error blocks promotion.


Test 06 — Verify the physical legacy schema is absent and new records are canonical

Identity and context

  • No browser identity is required.
  • Stop the LOCAL backend before running these SQL checks.
  • Run commands from the repository root.
  • Database: backend/data/app.db.

Steps

  1. Stop the LOCAL backend cleanly.
  2. Run:
sqlite3 -header -column backend/data/app.db <<'SQL'
SELECT COUNT(*) AS migration_000069_count
FROM schema_migrations
WHERE filename = '000069_physical_legacy_identity_schema_removal.up.sql';

SELECT COUNT(*) AS legacy_people_table_count
FROM sqlite_master
WHERE type = 'table'
  AND name = 'people';

SELECT
  (SELECT COUNT(*) FROM pragma_table_info('auth_user_accounts') WHERE name='actor_id') AS auth_user_accounts_actor_id,
  (SELECT COUNT(*) FROM pragma_table_info('auth_account_actors') WHERE name='is_primary') AS auth_account_actors_is_primary,
  (SELECT COUNT(*) FROM pragma_table_info('authz_actors') WHERE name='person_id') AS authz_actors_person_id,
  (SELECT COUNT(*) FROM pragma_table_info('authz_actors') WHERE name='collaborator_id') AS authz_actors_collaborator_id,
  (SELECT COUNT(*) FROM pragma_table_info('person_tenant_memberships') WHERE name='legacy_person_id') AS membership_legacy_person_id,
  (SELECT COUNT(*) FROM pragma_table_info('collaborator_journeys') WHERE name='person_id') AS collaborator_journey_person_id;

SELECT [notnull] AS collaborator_membership_not_null
FROM pragma_table_info('collaborator_journeys')
WHERE name='membership_id';

SELECT name
FROM pragma_table_info('people_search_index')
ORDER BY cid;

SELECT COUNT(*) AS obsolete_person_self_role_count
FROM authz_roles
WHERE code='PERSON'
   OR scope_type='SELF';

SELECT COUNT(*) AS obsolete_30k3a_write_guard_count
FROM sqlite_master
WHERE type='trigger'
  AND name IN (
    'trg_auth_user_accounts_legacy_actor_insert_prohibited',
    'trg_auth_user_accounts_legacy_actor_update_prohibited',
    'trg_auth_account_actors_legacy_primary_insert_prohibited',
    'trg_auth_account_actors_legacy_primary_update_prohibited',
    'trg_authz_actors_legacy_identity_insert_prohibited',
    'trg_authz_actors_legacy_identity_update_prohibited',
    'trg_person_membership_legacy_projection_insert_prohibited',
    'trg_person_membership_legacy_projection_update_prohibited',
    'trg_collaborator_journeys_legacy_person_insert_prohibited',
    'trg_collaborator_journeys_legacy_person_update_prohibited'
  );

SELECT
  gp.id AS global_person_id,
  gp.email,
  m.id AS membership_id,
  m.tenant_id,
  a.id AS account_id,
  aa.actor_id,
  aa.scope_type,
  aa.tenant_id AS actor_tenant_id,
  aa.membership_id AS actor_membership_id,
  cj.id AS collaborator_journey_id
FROM global_people gp
JOIN person_tenant_memberships m
  ON m.person_id = gp.id
LEFT JOIN auth_account_people ap
  ON ap.person_id = gp.id
LEFT JOIN auth_user_accounts a
  ON a.id = ap.account_id
LEFT JOIN auth_account_actors aa
  ON aa.account_id = a.id
 AND aa.membership_id = m.id
LEFT JOIN collaborator_journeys cj
  ON cj.membership_id = m.id
WHERE LOWER(gp.email) = LOWER('[email protected]');

PRAGMA foreign_key_check;
PRAGMA integrity_check;
SQL

Values to record and expected results

Check Expected
migration_000069_count 1
legacy_people_table_count 0
each of the six retired-column counts 0
collaborator_membership_not_null 1
people_search_index columns exactly membership_id, person_id, tenant_id, search_text
obsolete_person_self_role_count 0
obsolete_30k3a_write_guard_count 0
new Person canonical row present
membership present, tenant_id = default
Authentication Account present
AccountActor present, scope_type = TENANT, tenant_id = default
actor_membership_id equals the Person's membership_id
Collaborator Journey present
PRAGMA foreign_key_check no rows
PRAGMA integrity_check ok

Expected result

The physical legacy schema is gone, the 30K.3A compatibility triggers are gone because their target columns no longer exist, and the Person created during this suite is connected entirely through canonical identity.

PASS criteria

Every expected value above matches exactly.

Promotion blocker

Any legacy table or retired column still present, duplicate/missing 000069 marker, PERSON/SELF catalog row remaining, missing canonical linkage, foreign-key violation, or non-ok integrity result blocks promotion.


Test 07 — Normal backend startup must not recreate retired schema

Identity and context

  • This test begins immediately after Test 06.
  • Browser Session C will be used after restart.

Steps

  1. Start the normal LOCAL backend from the repository root:
make local-backend
  1. Start/leave running the normal frontend as appropriate:
make local-frontend
  1. In Browser Session C, sign in as: [email protected] with: Manual-30K3B-Permanent-2!
  2. Confirm the user's Person page loads.
  3. Open Collaborators.
  4. Open the 30K3B Canonical Journey.
  5. Open Current Account and confirm it loads.
  6. Stop the backend again.
  7. Run:
sqlite3 -header -column backend/data/app.db <<'SQL'
SELECT COUNT(*) AS migration_000069_count
FROM schema_migrations
WHERE filename = '000069_physical_legacy_identity_schema_removal.up.sql';

SELECT COUNT(*) AS legacy_people_table_count
FROM sqlite_master
WHERE type='table'
  AND name='people';

SELECT
  (SELECT COUNT(*) FROM pragma_table_info('auth_user_accounts') WHERE name='actor_id') AS auth_user_accounts_actor_id,
  (SELECT COUNT(*) FROM pragma_table_info('auth_account_actors') WHERE name='is_primary') AS auth_account_actors_is_primary,
  (SELECT COUNT(*) FROM pragma_table_info('authz_actors') WHERE name='person_id') AS authz_actors_person_id,
  (SELECT COUNT(*) FROM pragma_table_info('authz_actors') WHERE name='collaborator_id') AS authz_actors_collaborator_id,
  (SELECT COUNT(*) FROM pragma_table_info('person_tenant_memberships') WHERE name='legacy_person_id') AS membership_legacy_person_id,
  (SELECT COUNT(*) FROM pragma_table_info('collaborator_journeys') WHERE name='person_id') AS collaborator_journey_person_id;

PRAGMA foreign_key_check;
PRAGMA integrity_check;
SQL

Expected result

A normal runtime startup/AutoMigrate cycle does not recreate the retired people table or any of the six retired identity columns.

PASS criteria

  • user-visible canonical identity still works after restart;
  • migration_000069_count = 1;
  • legacy_people_table_count = 0;
  • all six retired-column counts remain 0;
  • PRAGMA foreign_key_check returns no rows;
  • PRAGMA integrity_check returns ok.

Promotion blocker

If startup recreates any retired table/column, changes the 000069 migration marker count, or breaks the new user's canonical identity, do not promote 30K.3B.


Final promotion decision

Promote 30K.3B — Physical Legacy Schema Removal and Migration Hardening only if Tests 01 through 07 all PASS.

Do not promote if any of the following occurs:

  • existing Application Administrator or Tenant Administrator identity stops resolving;
  • Application Administrator acquires Person/Tenant identity;
  • a new Person, Authentication Account, or Collaborator Journey cannot be created through the UI;
  • temporary-password or password-change workflow fails;
  • Person search becomes stale after a canonical Person update;
  • a Person must be reprovisioned after becoming a Collaborator;
  • any legacy identity table/column reappears;
  • any current row depends on a removed legacy identity field;
  • obsolete PERSON/SELF authorization state remains;
  • SQLite foreign-key or integrity verification fails;
  • normal backend startup recreates retired schema.

Glossary impact for manual testing: none.