Bite #30K.3A — Legacy Identity Dependency Elimination - RodrigoMattosoSilveira/EnterpriseRemoteSystems GitHub Wiki

Bite #30K.3A — Legacy Identity Dependency Elimination

Manual Promotion Test Suite

Branch: 30k3a
Passing SHA: 2b1fff6b14bcda09be2082f50dc77a1b566e4419
Automated status: make local-check and CI pass.

Purpose

These manual tests verify that the user-visible identity and authorization behavior still works after 30K.3A removed live runtime/writer dependence on the retired identity fields, and that newly created identity records do not write those retired fields.

The intended canonical identity path is:

Authentication Account → AccountActor → Person-Tenant Membership → Global Person → Collaborator Journey

The legacy columns remain physically present for this bite, but new runtime writes must not populate them.

Test data used by this suite

Create and use this deterministic identity during the tests:

Field Value
First name ThirtyKThreeA
Last name ManualPerson
Nickname 30K3A Person Only
CPF 31415926590
RG 30K3A-MANUAL-001
Cellular 11987654321
Email / future login [email protected]
Country Brasil
PIX key [email protected]
Street Rua Manual 30K3A
State PA
CEP 66000-000
City Manual City
Bank Manual Bank
Bank number 001
Checking account 30K3A-001
Emergency contact Manual Emergency
Emergency cellular 11999990000
Emergency email [email protected]
Temporary authentication password Manual-30K3A-Password-1!

Use Tenant A for the tenant-scoped tests.

Required sign-in identities

  1. Application Administrator: sign in with [email protected] using the existing local Application Administrator password.
  2. Tenant A Tenant Administrator: sign in as [email protected] using the existing local Tenant Administrator password.
  3. 30K3A Person: after Test 03 creates the Authentication Account, sign in with [email protected].

If [email protected] already exists from an earlier attempt, delete/reset only that manual test data before starting. Do not reuse an ambiguous pre-existing record.


Test 01 — Application Administrator remains global/control-plane only

Identity

Sign in as Application Administrator:

[email protected]

Required context

Select Administration / Global administration. Do not select or assume Tenant A.

Steps

  1. Sign out of any existing session.
  2. Sign in as [email protected].
  3. Confirm the header/context identifies the session as the Application Administrator in the global Administration context.
  4. Open Administration → Authentication.
  5. Open Administration → Tenants.
  6. Confirm both pages load without 403 errors or application exceptions.
  7. Confirm the Application Administrator is not silently switched into Tenant A merely by visiting these pages.
  8. If the UI exposes a tenant/context selector, confirm the Application Administrator's standing identity remains the global/control-plane identity.

Expected result

  • Authentication Administration loads.
  • Tenant Administration loads.
  • No tenant-scoped Person or Collaborator identity is implicitly attached to the Application Administrator.
  • No 403/500/application error occurs during normal global administration navigation.

PASS

PASS only if all expected results occur.

Promotion blocker

Block promotion if the Application Administrator requires a tenant identity to use global administration, is silently assigned a Tenant Actor, or receives a 403/500 caused by identity resolution.


Test 02 — Create a complete Person through the Tenant A UI

Identity

Sign in as the Tenant A Tenant Administrator:

[email protected]

Required context

Select Tenant A.

Steps

  1. Sign out of the Application Administrator session.
  2. Sign in as the Tenant A Tenant Administrator.
  3. Confirm the selected context is Tenant A.
  4. Navigate through the UI to People.
  5. Choose Create Person.
  6. Enter the following values exactly:
    • First name: ThirtyKThreeA
    • Last name: ManualPerson
    • Nickname: 30K3A Person Only
    • CPF: 31415926590
    • RG: 30K3A-MANUAL-001
    • Cellular: 11987654321
    • Email: [email protected]
    • Country: Brasil
    • PIX key: [email protected]
    • Street: Rua Manual 30K3A
    • State: PA
    • CEP: 66000-000
    • City: Manual City
    • Bank: Manual Bank
    • Bank number: 001
    • Checking account: 30K3A-001
    • Emergency contact: Manual Emergency
    • Emergency cellular: 11999990000
    • Emergency email: [email protected]
  7. Complete any other UI-required field with the normal valid Tenant A value.
  8. Save the Person.
  9. Return to the People list using the UI.
  10. Search for 30K3A Person Only.
  11. Open the Person from the search result.
  12. Confirm the displayed email is [email protected].
  13. Confirm the Person is an active member of Tenant A.
  14. Confirm there is not yet a Collaborator Journey for this Person.

Expected result

The Person is created and is visible through the normal Tenant A People UI without any legacy-identity-related error.

PASS

PASS only if the Person can be created, found again through the UI, and opened successfully in Tenant A.

Promotion blocker

Block promotion for a 400/403/500 error, duplicate/incorrect Person projection, inability to find the newly created Person, or creation of an unexpected Collaborator Journey.


Test 03 — Create a Person-only Authentication Account and verify self-service

Identity

Start as Application Administrator:

[email protected]

Then sign in as the newly created Person:

[email protected]

Required context

For account creation, use Administration / Global administration.

For the Person sign-in, select Tenant A if the application asks for a tenant after authentication.

Steps

  1. Sign out of the Tenant A Tenant Administrator session.
  2. Sign in as [email protected].
  3. Navigate to Administration → Authentication.
  4. Choose the UI action to create/provision an Authentication Account.
  5. Select Tenant A as the target tenant if the form asks for the tenant.
  6. Select the Person whose email is [email protected].
  7. Set the temporary password to Manual-30K3A-Password-1!.
  8. Submit the account creation form.
  9. Confirm the account appears in Authentication Administration and is associated with the expected Person and Tenant A Actor/Membership projection.
  10. Sign out.
  11. Sign in as [email protected] using Manual-30K3A-Password-1!.
  12. If the application requires a password change on first login, complete it and record the new password in your test notes.
  13. Select Tenant A if prompted.
  14. Navigate through the normal UI to the signed-in Person's own Person information.
  15. Confirm the page belongs to 30K3A Person Only.
  16. Navigate to the Person's Current Account/history view.
  17. Confirm the page loads successfully even though this Person has never yet been a Collaborator.
  18. Confirm the Person does not receive Tenant Administrator, Expense Operator, or other delegated administrative permissions merely because an Authentication Account exists.

Expected result

A Person who has never been a Collaborator can authenticate through the canonical Account/AccountActor/Membership graph and can use intrinsic Person self-service.

PASS

PASS only if the Person can sign in, see their own Person record and Current Account/history, and does not receive delegated administrative privileges.

Promotion blocker

Block promotion if account creation fails because a legacy actor_id, Actor person_id, legacy Person projection, or Collaborator identity is absent; if login fails; or if delegated permissions appear unexpectedly.


Test 04 — Make the same Person a Collaborator without changing Authentication identity

Identity

First sign in as the Tenant A Tenant Administrator.

Then sign in again as:

[email protected]

Required context

Use Tenant A throughout.

Steps

  1. Sign out of the 30K3A Person session.
  2. Sign in as the Tenant A Tenant Administrator.
  3. Confirm Tenant A is selected.
  4. Navigate to Collaborators through the UI.
  5. Choose Create Collaborator.
  6. Select the existing Person 30K3A Person Only.
  7. Use valid Tenant A reference-data choices. When these labels are available, use:
    • Payment method: Daily
    • Daily/payment value: 100
    • Sector: Mining
    • Location: Main Mine
    • Task: Miner
  8. Use today's date as the Journey start date.
  9. Use a default end date at least 30 days after the start date.
  10. Save the Collaborator Journey.
  11. Return to the Collaborators list.
  12. Search for 30K3A Person Only.
  13. Open the newly created Collaborator Journey.
  14. Confirm the Person shown is 30K3A Person Only.
  15. Record the Collaborator Journey ID displayed by the UI.
  16. Sign out.
  17. Sign in again as [email protected].
  18. Select Tenant A.
  19. Confirm the same Authentication Account still works; no second account or re-provisioning step should be required.
  20. Navigate to the user's work/Collaborator features.
  21. Confirm current Collaborator features are now available in addition to the Person self-service features verified in Test 03.
  22. Re-open the user's Person information and Current Account/history to confirm the original Person self-service remains available.

Expected result

Collaborator status is derived from the canonical Membership/Journey relationship. Turning the Person into a Collaborator must not require or create a new legacy identity linkage.

PASS

PASS only if the same Authentication Account continues to work and the user's permissions expand appropriately from Person-only to current-Collaborator capabilities.

Promotion blocker

Block promotion if the user must be re-provisioned, receives a different Person identity, loses Person self-service, cannot reach Collaborator features, or receives an identity-resolution error.


Test 05 — Verify the new records contain canonical identity only

This is a database-level verification because the retired physical columns are intentionally not exposed in the UI.

Identity

No application sign-in is required for the SQL portion.

Prerequisites

Tests 02–04 must have passed.

Stop the backend before inspecting or mutating the local database.

Steps

From the repository root, run:

sqlite3 -header -column backend/data/app.db "
SELECT
  gp.id AS global_person_id,
  gp.email,
  m.id AS membership_id,
  m.tenant_id,
  m.legacy_person_id
FROM global_people gp
JOIN person_tenant_memberships m
  ON m.person_id = gp.id
WHERE lower(gp.email) = lower('[email protected]');
"

Record the returned global_person_id, membership_id, and tenant_id.

Then run:

sqlite3 -header -column backend/data/app.db "
SELECT
  a.id AS account_id,
  a.login,
  a.actor_id AS legacy_account_actor_id,
  ap.person_id AS canonical_person_id,
  aa.actor_id AS canonical_actor_id,
  aa.scope_type,
  aa.tenant_id,
  aa.membership_id,
  aa.is_primary AS legacy_is_primary,
  az.person_id AS legacy_actor_person_id,
  az.collaborator_id AS legacy_actor_collaborator_id
FROM auth_user_accounts a
JOIN auth_account_people ap
  ON ap.account_id = a.id
JOIN auth_account_actors aa
  ON aa.account_id = a.id
JOIN authz_actors az
  ON az.id = aa.actor_id
WHERE lower(a.login) = lower('[email protected]');
"

Then run:

sqlite3 -header -column backend/data/app.db "
SELECT
  cj.id AS collaborator_journey_id,
  cj.tenant_id,
  cj.membership_id,
  cj.person_id AS legacy_collaborator_person_id,
  m.person_id AS canonical_person_id
FROM collaborator_journeys cj
JOIN person_tenant_memberships m
  ON m.id = cj.membership_id
 AND m.tenant_id = cj.tenant_id
JOIN global_people gp
  ON gp.id = m.person_id
WHERE lower(gp.email) = lower('[email protected]')
ORDER BY cj.created_at DESC;
"

Finally run:

sqlite3 -header -column backend/data/app.db "
SELECT COUNT(*) AS legacy_people_rows
FROM people
WHERE lower(email) = lower('[email protected]');
"

Expected result

For the 30K3A Person:

  • exactly one canonical Global Person is returned;
  • the Tenant A Membership exists;
  • person_tenant_memberships.legacy_person_id is NULL;
  • the Authentication Account exists;
  • auth_user_accounts.actor_id is NULL;
  • an auth_account_people Person binding exists;
  • a TENANT auth_account_actors binding exists;
  • its membership_id matches the Person's Tenant A Membership;
  • auth_account_actors.is_primary remains 0;
  • authz_actors.person_id is NULL;
  • authz_actors.collaborator_id is NULL;
  • the Collaborator Journey uses the canonical membership_id;
  • collaborator_journeys.person_id is NULL;
  • the legacy people table contains 0 rows for [email protected].

PASS

PASS only if every expected result is true.

Promotion blocker

Any newly populated retired identity field is a promotion blocker.


Test 06 — Verify the database rejects attempts to reintroduce legacy identity writes

This is intentionally SQL-based because 30K.3A's write-prohibition triggers are a database invariant.

Keep the backend stopped.

Run each command separately.

06A — Legacy Authentication Account actor_id

sqlite3 backend/data/app.db "
UPDATE auth_user_accounts
SET actor_id = (
  SELECT aa.actor_id
  FROM auth_account_actors aa
  WHERE aa.account_id = auth_user_accounts.id
    AND aa.scope_type = 'TENANT'
  LIMIT 1
)
WHERE lower(login) = lower('[email protected]');
"

Expected error contains:

legacy_authentication_actor_write_prohibited

06B — Legacy AccountActor is_primary

sqlite3 backend/data/app.db "
UPDATE auth_account_actors
SET is_primary = 1
WHERE account_id = (
  SELECT id
  FROM auth_user_accounts
  WHERE lower(login) = lower('[email protected]')
);
"

Expected error contains:

legacy_primary_actor_write_prohibited

06C — Legacy Actor person_id

sqlite3 backend/data/app.db "
UPDATE authz_actors
SET person_id = (
  SELECT ap.person_id
  FROM auth_account_people ap
  JOIN auth_user_accounts a
    ON a.id = ap.account_id
  WHERE lower(a.login) = lower('[email protected]')
)
WHERE id = (
  SELECT aa.actor_id
  FROM auth_account_actors aa
  JOIN auth_user_accounts a
    ON a.id = aa.account_id
  WHERE lower(a.login) = lower('[email protected]')
    AND aa.scope_type = 'TENANT'
  LIMIT 1
);
"

Expected error contains:

legacy_actor_identity_write_prohibited

06D — Legacy Membership legacy_person_id

sqlite3 backend/data/app.db "
UPDATE person_tenant_memberships
SET legacy_person_id = '30k3a-forbidden-legacy-person'
WHERE id = (
  SELECT aa.membership_id
  FROM auth_account_actors aa
  JOIN auth_user_accounts a
    ON a.id = aa.account_id
  WHERE lower(a.login) = lower('[email protected]')
    AND aa.scope_type = 'TENANT'
  LIMIT 1
);
"

Expected error contains:

legacy_membership_person_write_prohibited

06E — Legacy Collaborator person_id

sqlite3 backend/data/app.db "
UPDATE collaborator_journeys
SET person_id = '30k3a-forbidden-legacy-person'
WHERE id = (
  SELECT cj.id
  FROM collaborator_journeys cj
  JOIN person_tenant_memberships m
    ON m.id = cj.membership_id
  JOIN global_people gp
    ON gp.id = m.person_id
  WHERE lower(gp.email) = lower('[email protected]')
  ORDER BY cj.created_at DESC
  LIMIT 1
);
"

Expected error contains:

legacy_collaborator_person_write_prohibited

Final verification

Re-run all four read-only queries from Test 05.

Expected result

  • Every prohibited update fails with the specified trigger error.
  • None of the retired fields changes.
  • The canonical rows remain intact.

PASS

PASS only if all five prohibited writes are rejected and the subsequent read-only verification is unchanged.

Promotion blocker

Block promotion if any prohibited write succeeds, if the wrong row is modified, or if canonical identity becomes inconsistent after a rejected write.


Final promotion decision

Promote Bite #30K.3A only if:

  • Test 01 passes — Application Administrator remains global/control-plane only.
  • Test 02 passes — a new Tenant A Person can be created through the UI.
  • Test 03 passes — a Person-only Authentication Account can authenticate and use Person self-service.
  • Test 04 passes — the same account gains current-Collaborator behavior from the canonical Journey/Membership relationship.
  • Test 05 passes — all newly created identity uses canonical rows and every retired identity field remains empty.
  • Test 06 passes — every attempted legacy identity write is rejected.
  • No unexpected 400, 403, 500, JavaScript exception, tenant-switching anomaly, or cross-tenant authorization leak is observed.

If all items pass, manual promotion testing for Bite #30K.3A is complete.