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-checkpasses.- CI passes.
- Use the LOCAL application database at
backend/data/app.db. - Do not reset the LOCAL database between these tests.
- Keep any pre-
000069backup 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
- Sign out any existing user in Browser Session A.
- Sign in as
[email protected]with the currently configured LOCAL password. - Confirm the context selector shows Global administration.
- Using the application navigation, open Authentication.
- In the Authentication search/filter field, enter
[email protected]. - Find the
[email protected]Authentication Account. - Inspect its identity-boundary information.
- 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.
- Person binding says
- Using the application navigation, open Tenants.
- 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
primaryconcept 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
- Sign out any existing user in Browser Session B.
- Sign in as
[email protected]with the currently configured LOCAL password. - Confirm the selected Tenant is Default Tenant.
- Using application navigation, open People.
- Confirm the page states or otherwise clearly shows that the People directory is operating in the selected Tenant boundary.
- In Filter people, enter:
[email protected] - Confirm the Tenant Administrator's Person appears.
- Open that Person through the UI.
- Confirm the Person page loads and displays the existing Person information without an application error.
- Use Back to People.
- Using application navigation, open Collaborators.
- 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 Tenantis 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
- Continue in Browser Session B as
[email protected]. - Selected Tenant: Default Tenant.
Steps
- Using application navigation, open People.
- Click New Person.
- 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
- First Name:
- Click Create Person.
- Confirm the application returns to People and reports that the Person was added.
- Open the newly created Person through the UI.
- Open Address and enter:
- Street 1:
Rua Manual 30K3B - Street 2: leave blank
- State:
Pará - City:
Manual City - CEP:
66000-000 - Country: confirm
Brasil
- Street 1:
- Open Bank and enter:
- Bank Name:
Manual Bank - Bank Number:
001 - Checking Account:
30K3B-001 - PIX:
[email protected]
- Bank Name:
- Open Emergency and enter:
- Emergency Contact Name:
Manual Emergency - Emergency Cellular:
11999990000 - Emergency Email:
[email protected]
- Emergency Contact Name:
- Return to Personal.
- Change Nickname from
30K3B Physicalto:30K3B Canonical - Click Save Changes.
- Confirm
Person updated successfully.appears. - Confirm Profile Status shows the Person is complete/eligible to become a Collaborator.
- Click Back to People.
- In Filter people, enter:
30K3B Canonical - Confirm exactly the newly created Person is found.
- Clear the filter.
- Enter:
30K3B Physical - Confirm the Person is not returned under the old nickname.
- Clear the filter.
- Enter:
[email protected] - 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
- If necessary, sign in again as
[email protected]. - Confirm Global administration is selected.
- Using application navigation, open Authentication.
- In Create account, enter:
- Target Tenant:
Default Tenant - Person login email:
[email protected] - Temporary password:
Manual-30K3B-Password-1!
- Target Tenant:
- Click Create account.
- Confirm the account is created without an application error.
- In the Authentication account filter/search, enter:
[email protected] - Open/inspect the resulting account.
- 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/primaryproperty is shown.
- the Person binding is present and refers to
- Open a separate Browser Session C.
- Sign in as:
[email protected]using:Manual-30K3B-Password-1! - Confirm ERS directs the user to Change password.
- Enter:
- Current password:
Manual-30K3B-Password-1! - New password:
Manual-30K3B-Permanent-2! - Confirm new password:
Manual-30K3B-Permanent-2!
- Current password:
- Click Change password.
- Confirm ERS returns to sign-in and reports that the password changed.
- Sign in again as
[email protected]using:Manual-30K3B-Permanent-2! - Confirm the user lands on or can navigate to People and sees only their own Person self-service record.
- 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
- Browser Session B.
- Sign in as
[email protected]. - Selected Tenant: Default Tenant.
Steps
- Return to Browser Session B.
- Confirm
[email protected]is signed in and Default Tenant is selected. - Using application navigation, open Collaborators.
- Click Add.
- In Find eligible Person by nickname, enter:
30K3B Canonical - Select
ThirtyKThreeB ManualPerson. - In the Journey section enter/select:
- Journey Start Date:
2026-09-10 - Status:
Active - Sector:
Mining - Location:
Main Mine - Task:
Miner
- Journey Start Date:
- In the Payment section select/enter:
- Payment Method:
Daily wage - Payment Value:
100
- Payment Method:
- In Notes, enter:
30K.3B physical-schema-removal manual promotion test - Click Create Collaborator.
- Confirm the Collaborator is created.
- Open the created Collaborator Journey.
- Record the displayed Journey Code.
- Confirm the page displays:
ThirtyKThreeB ManualPerson;- a canonical Person ID;
- the Journey information entered above.
- Confirm Legacy Person ID is not displayed anywhere on the Collaborator detail page.
- Click Current Account.
- Confirm Person Current Account loads for this Person/Journey without an error.
- Return to Browser Session C.
- If necessary, sign in as
[email protected]using:Manual-30K3B-Permanent-2! - Using application navigation, open Collaborators.
- Confirm the page is the user's self-service My Collaborator Journeys view.
- Open the
30K3B CanonicalJourney. - Confirm its Journey Code matches the value recorded in step 13.
- Open Current Account.
- Confirm the Current Account loads successfully.
- 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 IDappears. - 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
- Stop the LOCAL backend cleanly.
- 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
- Start the normal LOCAL backend from the repository root:
make local-backend
- Start/leave running the normal frontend as appropriate:
make local-frontend
- In Browser Session C, sign in as:
[email protected]with:Manual-30K3B-Permanent-2! - Confirm the user's Person page loads.
- Open Collaborators.
- Open the
30K3B CanonicalJourney. - Open Current Account and confirm it loads.
- Stop the backend again.
- 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_checkreturns no rows;PRAGMA integrity_checkreturnsok.
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/SELFauthorization state remains; - SQLite foreign-key or integrity verification fails;
- normal backend startup recreates retired schema.
Glossary impact for manual testing: none.