Bite #30H — Tenant Administrator Cardinality - RodrigoMattosoSilveira/EnterpriseRemoteSystems GitHub Wiki

Enterprise Remote Systems — Bite 30H Manual Promotion Tests

Promotion candidate

  • Bite: Bite #30H — Tenant Administrator Cardinality
  • Branch: 736-bite-30h---tenant-administrator-cardinality
  • Committed SHA supplied for promotion: d02e1f65e495b458a77553fabafb4f2c61d183be
  • Required cardinality:
    • Tenant → active TENANT_ADMIN Role Grants: 0..2
    • Global Person → Tenants concurrently administered: 0..1
    • When a Tenant has two active TENANT_ADMIN Role Grants, the grants must belong to two distinct global Persons.
    • Deactivating an Actor does not free a Tenant Administrator slot. Only explicit Role Grant revocation frees the slot.

Source-verification note: the ZIP for SHA d02e1f65e495b458a77553fabafb4f2c61d183be was not exposed to the assistant runtime when this suite was generated. The suite was derived from the immediately preceding Bite 30H source plus the final E2E correction that led to the reported all-green local gates. Before promotion, confirm the attached/current branch contains the same 000062_tenant_administrator_cardinality migration and the final E2E cleanup changes.


1. Promotion-test philosophy

These are functional manual tests first. Perform Tests 01–10 through the supported product UI.

Do not use SQL or DevTools to substitute for information that should be visible in the UI. If the UI cannot show the cardinality state, candidate eligibility, current assignment count, inactive-Actor state, or grant blocker described below, treat that as an observability/UX defect.

A separate technical promotion gate appears after the UI tests. SQL there is read-only except for creation/removal of a disposable temporary migration database.


2. Preconditions

Before beginning:

  • All Bite 30H software changes are committed.
  • git status --short is empty.
  • git rev-parse HEAD prints exactly:
 d02e1f65e495b458a77553fabafb4f2c61d183be
  • make local-check passes.
  • Full Playwright passes.
  • Backend is stopped before fixture reset.

Prepare deterministic data

Use the supplied Bite 30H wrapper from the project root:

python3 ~/Downloads/seed-bite30h-manual-testdata.py \
  --repo-root . \
  --db-path backend/data/app.db

The wrapper invokes the branch's canonical:

scripts/seed-bite30g-final-manual-testdata.py --reset-existing-batch

and verifies the Bite 30H database guards and baseline administrator assignments.

Then restart ERS:

make local-backend

In another terminal:

make local-frontend

Use a fresh browser session after the reset.


3. Deterministic identities

Application Administrator

For the standard local environment initialized by scripts/init-dev-env.sh:

If your backend/.env overrides DEV_ADMIN_EMAIL or DEV_ADMIN_PASSWORD, use those configured values instead.

Common Bite 30G/30H fixture password

Manual-30C-Password!

Tenants

Tenant A

  • Name: Byte 28A Manual Test
  • Deterministic fixture ID when the fixture creates it: manual30g-tenant-a

Tenant B

  • Name: Default Tenant
  • ID: default

Starting Tenant Administrators

Operator D — Tenant A Administrator

  • Login: [email protected]
  • Actor display: 30G Operator D · Tenant A
  • Actor key: manual30g-actor-key-operator-tenant-a

Operator E — Tenant B Administrator

  • Login: [email protected]
  • Actor display: 30G Operator E · Tenant B
  • Actor key: manual30g-actor-key-tenant-b-admin

Cardinality candidates

Identity A — same global Person in two Tenants

  • Login: [email protected]
  • Global Person: manual30g-global-person-a
  • Tenant A Actor display: 30G Identity A · Tenant A
  • Tenant A Actor key: manual30g-actor-key-a-tenant-a
  • Tenant B Actor display: 30G Identity A · Tenant B
  • Tenant B Actor key: manual30g-actor-key-a-tenant-b

Identity A is the critical fixture for proving that two Tenant Actors cannot bypass the Person 0..1-across-Tenants rule.

Identity B — distinct Person in Tenant A

  • Login: [email protected]
  • Global Person: manual30g-global-person-b
  • Actor display: 30G Identity B · Tenant owes
  • Actor key: manual30g-actor-key-b-tenant-a

Identity C — additional distinct Person in Tenant A

  • Login: [email protected]
  • Global Person: manual30g-global-person-c
  • Actor display: 30G Identity C · Collaborator owes
  • Actor key: manual30g-actor-key-c-tenant-a

4. State discipline

Perform the tests in order. Several tests deliberately change Role Grants and then restore them.

Expected initial state:

Tenant Active TENANT_ADMIN assignments Active administrator Actors
Tenant A 1/2 — Operator D 1
Tenant B 1/2 — Operator E 1

Expected final restored state after Test 10:

Tenant Active TENANT_ADMIN assignments Active administrator Actors
Tenant A 1/2 — Operator D 1
Tenant B 1/2 — Operator E 1

If your state diverges unexpectedly, stop rather than compensating with SQL.


[✔︎] Test 01 — Baseline Tenant Administrator cardinality is visible

Purpose

Verify the product exposes the 30H assignment capacity separately from current Actor availability.

Steps

  1. Sign in as the Application Administrator.
  2. Open Tenants.
  3. Open Byte 28A Manual Test.
  4. In Operational status, verify the Tenant is ACTIVE_READY.
  5. [✔︎] Verify Tenant administrators displays:
    • 1 of 2 assignments
    • (1 active Actor)
  6. In the Tenant administrators section, verify 30G Operator D · Tenant A is listed.
  7. [✔︎] Verify the page explains:
    • a Tenant may have up to two active TENANT_ADMIN assignments;
    • the assignments must belong to distinct Persons;
    • a Person may administer only one Tenant at a time;
    • Actor deactivation does not release an assignment.
  8. [✔︎] Verify the blue information message says one of two slots is occupied and a second distinct Person may be assigned for redundancy.
  9. [✔︎] Verify the selector reads Select an eligible active actor and Assign Admin is disabled until a candidate is selected.
  10. Return to Tenants and open Default Tenant.
  11. [✔︎] Verify 1 of 2 assignments, (1 active Actor), and 30G Operator E · Tenant B.

Pass criteria

  • Tenant A visibly reports 1 of 2 assignments and one active administrator Actor.
  • Tenant B visibly reports 1 of 2 assignments and one active administrator Actor.
  • Assignment capacity and active-Actor count are presented as different concepts.
  • The 30H rules are understandable without SQL or DevTools.

[x] Test 02 — A second distinct Person can occupy the backup slot

Purpose

Prove the Tenant upper bound is two, not one, and establish the cross-Tenant Person test condition.

Steps

  1. As Application Administrator, open Default Tenant.
  2. In Tenant administrators, open the eligible-Actor selector.
  3. Select 30G Identity A · Tenant B.
  4. Click Assign Admin.
  5. Verify the success message confirms assignment.
  6. Verify the page now reports:
    • 2 of 2 assignments
    • (2 active Actors)
  7. Verify both are listed:
    • 30G Operator E · Tenant B
    • 30G Identity A · Tenant B
  8. Verify the page says Both Tenant Administrator slots are occupied.
  9. Verify the selector is disabled and its placeholder says Maximum of two administrators assigned.

Pass criteria

  • A second distinct global Person can be assigned.
  • The Tenant reaches exactly 2/2.
  • A third assignment cannot be initiated from Tenant Management.

Do not revoke Identity A yet. Test 03 depends on this state.


[x] Test 03 — The same global Person cannot administer two Tenants

Purpose

Prove that separate Tenant Actors do not let one global Person bridge privileged Tenant data.

Steps — Tenant Management path

  1. With Identity A still a Tenant Administrator in Default Tenant, open Byte 28A Manual Test.
  2. Verify Tenant A still reports 1 of 2 assignments.
  3. Locate Unavailable administrator candidates.
  4. Find 30G Identity A · Tenant A.
  5. Verify it is not present in the eligible selector.
  6. Verify its ineligibility reason states that this Person already administers tenant default.

Steps — Application Authorization path

  1. Open Authorization.
  2. Find the Actor card for:
manual30g-actor-key-a-tenant-a
  1. Verify its binding shows an Authentication Account and an ACTIVE Person–Tenant Membership for Tenant A.
  2. Choose role TENANT_ADMIN.
  3. Verify Grant tenant resolves to Tenant A's immutable Tenant ID, not default and not *.
  4. Verify Grant Role is disabled.
  5. Verify the page displays a blocker equivalent to:
TENANT_ADMIN grant unavailable: This Person already administers tenant default; a Person may administer only one Tenant at a time.
  1. Change the role selector to a normal compatible tenant operator Role such as EXPENSE_OPERATOR.
  2. Verify the cross-Tenant Tenant Administrator blocker disappears. Do not grant the operator Role.

Cleanup

  1. Return to Default Tenant.
  2. Click Revoke for 30G Identity A · Tenant B.
  3. Verify Default Tenant returns to:
  • 1 of 2 assignments
  • (1 active Actor)
  • only 30G Operator E · Tenant B assigned.

Pass criteria

  • The same global Person cannot receive TENANT_ADMIN in Tenant A while administering Tenant B.
  • The restriction follows the global Person across different Tenant Actor records.
  • The restriction is specific to TENANT_ADMIN; normal compatible delegated roles are not incorrectly blocked by the Person 0..1 rule.
  • Revocation releases Identity A from the cross-Tenant restriction.

Test 04 — Tenant A can reach exactly two administrators

Purpose

Establish a full 2/2 Tenant for maximum-capacity and lifecycle tests.

Steps

  1. Open Byte 28A Manual Test.
  2. Select 30G Identity A · Tenant A from Select an eligible active actor.
  3. Click Assign Admin.
  4. Verify the page reports:
    • 2 of 2 assignments
    • (2 active Actors)
  5. Verify both are listed:
    • 30G Operator D · Tenant A
    • 30G Identity A · Tenant A
  6. Verify Both Tenant Administrator slots are occupied.
  7. Verify the assignment selector is disabled with Maximum of two administrators assigned.

Pass criteria

  • Tenant A accepts a second distinct Person.
  • The product presents 2/2 as a valid redundancy state, not an error state.

Leave Tenant A at 2/2 for Tests 05–06.


Test 05 — A third Tenant Administrator is blocked in both administration surfaces

Purpose

Prove the Tenant upper bound is enforced before an invalid submission.

Tenant Management path

  1. Stay on Byte 28A Manual Test.
  2. Verify the assignment selector is disabled at 2/2.
  3. Verify neither Identity B nor Identity C can be selected for a third assignment.

Application Authorization path

  1. Open Authorization.
  2. Find Actor:
manual30g-actor-key-b-tenant-a
  1. Select TENANT_ADMIN.
  2. Verify Grant Role is disabled.
  3. Verify the blocker says:
Tenant already has the maximum of two active Tenant Administrators.
  1. Repeat with Actor:
manual30g-actor-key-c-tenant-a
  1. Verify the same maximum-of-two blocker.

Pass criteria

  • Tenant Management prevents the third assignment.
  • Application Authorization independently prevents the third assignment.
  • No transient third TENANT_ADMIN grant appears.

Test 06 — Deactivating an administrator Actor does not release its slot

Purpose

Prove cardinality belongs to the active Role Grant rather than Actor availability.

Steps

  1. In Authorization, find:
manual30g-actor-key-a-tenant-a
  1. Confirm its active TENANT_ADMIN Role Grant for Tenant A is visible.
  2. Click Deactivate for the Actor.
  3. Verify the Actor card now says Inactive while the TENANT_ADMIN Role Grant remains listed.
  4. Open Byte 28A Manual Test.
  5. Verify the page still reports:
    • 2 of 2 assignments
    • (1 active Actor)
  6. Verify 30G Identity A · Tenant A remains in the assigned administrator list and is labeled Inactive actor.
  7. Verify Both Tenant Administrator slots are occupied still appears.
  8. Verify the assignment selector remains disabled.
  9. Open Authorization, find Identity B's Tenant A Actor, choose TENANT_ADMIN, and verify the maximum-of-two blocker still applies.

Pass criteria

  • Actor deactivation reduces active administrator Actor count from 2 to 1.
  • Assignment count remains 2/2.
  • The inactive Actor's grant still consumes a slot.
  • A third administrator remains prohibited.

Leave Identity A's Actor inactive and its Role Grant active for Test 07.


Test 07 — Explicit Role Grant revocation releases the slot

Purpose

Prove only explicit authorization revocation releases Tenant Administrator capacity.

Steps

  1. Open Byte 28A Manual Test.
  2. Click Revoke for 30G Identity A · Tenant A.
  3. Verify the success message says the Tenant Administrator assignment was revoked.
  4. Verify Tenant A now reports:
    • 1 of 2 assignments
    • (1 active Actor)
  5. Verify the eligible selector is enabled again.
  6. Under Unavailable administrator candidates, verify the now-unassigned Identity A Actor is unavailable because it is inactive, with a reason equivalent to:
Inactive actors cannot be assigned as Tenant Administrators
  1. Open Authorization.
  2. Find manual30g-actor-key-a-tenant-a.
  3. manual30g-actor-key-a-tenant-aVerify the TENANT_ADMIN grant is gone/inactive.
  4. Click Activate to restore Identity A's Actor for later regression use.

Pass criteria

  • Revocation, not Actor deactivation, releases the second slot.
  • Tenant A returns to 1/2.
  • An inactive unassigned Actor is not eligible for a new Tenant Administrator assignment.
  • Reactivation alone does not recreate the revoked TENANT_ADMIN Role Grant.

Test 08 — Safe administrator handoff without a coverage gap

Purpose

Verify the reason for allowing two slots: a replacement can be verified before the departing administrator is revoked.

Establish overlap

  1. Open Byte 28A Manual Test as Application Administrator.
  2. Select 30G Identity B · Tenant owes.
  3. Click Assign Admin.
  4. Verify Tenant A reports 2 of 2 assignments and two active Actors:
    • Operator D
    • Identity B

Verify the replacement before removing the original

  1. Sign out.
  2. Sign in as:
[email protected]

with password:

Manual-30C-Password!
  1. Verify the session is in Tenant A / Byte 28A Manual Test.
  2. Verify Tenant Administrator capabilities are visible in navigation, including Authorization and Reference data.
  3. Verify application-only control-plane links Tenants and Authentication are not exposed merely because Identity B is a Tenant Administrator.
  4. Open Authorization and verify the page loads for Tenant A rather than returning Forbidden.

Complete the handoff

  1. Sign out Identity B and sign back in as Application Administrator.
  2. Open Byte 28A Manual Test.
  3. Revoke 30G Operator D · Tenant A.
  4. Verify the Tenant remains ACTIVE_READY.
  5. Verify it reports:
  • 1 of 2 assignments
  • (1 active Actor)
  • Identity B is the remaining administrator.
  1. Select 30G Operator D · Tenant A and assign it again. 17.[x] Verify 2/2 is restored.
  2. Revoke 30G Identity B · Tenant owes.
  3. Verify the initial Tenant A baseline is restored:
  • 1 of 2 assignments
  • (1 active Actor)
  • Operator D is the sole Tenant Administrator.

Pass criteria

  • A second administrator can be assigned before removing the first.
  • The replacement can actually authenticate and exercise Tenant Administrator navigation/authorization.
  • Removing the original after verification does not create an administrative coverage gap.
  • The baseline can be restored without recreating Actors or Authentication Accounts.

Test 09 — Zero Tenant Administrators is a valid transitional state but not operationally ready

Purpose

Verify the lower bound is zero while the product clearly warns about loss of administrative coverage.

Steps

  1. As Application Administrator, open Byte 28A Manual Test.
  2. Confirm Operator D is currently the only Tenant Administrator.
  3. Click Revoke for Operator D.
  4. Verify the Tenant remains an existing active Tenant; it is not deleted or automatically deactivated.
  5. Verify Operational status becomes ACTIVE_NO_TENANT_ADMIN.
  6. Verify the administrator summary reports:
    • 0 of 2 assignments
    • (0 active Actors)
  7. Verify the warning says:
No Tenant Administrator is assigned. Assign at least one for operational readiness.
  1. Verify the eligible Actor selector remains available.
  2. Select 30G Operator D · Tenant A.
  3. Click Assign Admin.
  4. Verify the Tenant returns to ACTIVE_READY and 1 of 2 assignments / (1 active Actor).

Pass criteria

  • Zero active assignments is accepted as a transitional state.
  • The UI clearly reports lack of administrative coverage.
  • Restoring one administrator returns the Tenant to ready state.

Test 10 — Ordinary multi-Tenant Person access is not prohibited by the administrator cardinality rule

Purpose

Confirm Bite 30H restricts privileged TENANT_ADMIN authority, not ordinary Person membership or self-service across Tenants.

Steps

  1. Confirm Identity A has no TENANT_ADMIN Role Grant in either Tenant after prior cleanup.
  2. Sign out of the Application Administrator account.
  3. Sign in as:
[email protected]

using:

Manual-30C-Password!
  1. Verify Identity A can select/access Byte 28A Manual Test.
  2. Verify Identity A can access its Person/self-service information for Tenant A.
  3. Switch to Default Tenant.
  4. Verify the tenant switch succeeds.
  5. Verify Identity A can access its Person/self-service information for Tenant B.
  6. Verify the account is not blocked merely because the same global Person has memberships/Actors in two Tenants.
  7. Verify Tenant Administrator-only capabilities are not present unless a TENANT_ADMIN grant actually exists.
  8. Sign out.
  9. Sign back in as Application Administrator.
  10. Reopen Tenant A and Tenant B and verify final baseline:
Tenant Expected final state
Tenant A Operator D only, 1/2, 1 active Actor, ACTIVE_READY
Tenant B Operator E only, 1/2, 1 active Actor, ACTIVE_READY

Pass criteria

  • The Person 0..1-across-Tenants rule applies only to active TENANT_ADMIN authority.
  • Ordinary multi-Tenant membership and self-service remain functional.
  • Both fixture Tenants are restored to their original administrator state.

5. Manual-suite promotion decision

The UI manual suite passes only if all of the following are true:

  • Tenant A and Tenant B initially expose 1/2 administrator capacity.
  • A second distinct Person can be assigned.
  • A third administrator is blocked.
  • The same global Person cannot administer Tenant A and Tenant B concurrently through separate Actors.
  • The cross-Tenant restriction does not incorrectly block normal non-TENANT_ADMIN delegated roles.
  • Actor deactivation does not release a Tenant Administrator slot.
  • Explicit Role Grant revocation does release the slot.
  • An inactive unassigned Actor is not eligible for new Tenant Administrator assignment.
  • A two-administrator overlap supports safe handoff.
  • Zero administrators is allowed but visibly reports ACTIVE_NO_TENANT_ADMIN.
  • Ordinary multi-Tenant Person access remains intact.
  • The deterministic starting administrator state is restored at the end.

If any of these fail, do not promote Bite 30H.


6. Technical promotion gate

Run this only after the UI manual suite passes.

6.1 Canonical automated gate

make local-check

Expected: pass.

6.2 Full Playwright

cd frontend
npx playwright test
cd ..

Expected: all tests pass.

The two E2E scenarios that genuinely create disposable Tenant Administrators must explicitly revoke their Role Grants during teardown. Actor deactivation alone is not sufficient cleanup under Bite 30H.

6.3 Clean migration chain through Bite 30H

Use a disposable database:

rm -f /tmp/ers-bite30h-migration.db
DB_PATH=/tmp/ers-bite30h-migration.db ./scripts/db-migrate.sh

Verify migration 000062_tenant_administrator_cardinality.up.sql is applied:

sqlite3 /tmp/ers-bite30h-migration.db \
  "SELECT filename FROM schema_migrations WHERE filename LIKE '000062%';"

Expected:

000062_tenant_administrator_cardinality.up.sql

Then:

sqlite3 /tmp/ers-bite30h-migration.db "PRAGMA foreign_key_check;"

Expected: no rows.

Remove the disposable database when finished:

rm -f /tmp/ers-bite30h-migration.db

6.4 Read-only cardinality verifier on the manual-test database

List active Tenant Administrator assignments with canonical global Person

sqlite3 -header -column backend/data/app.db <<'SQL'
SELECT
  g.tenant_id,
  a.actor_key,
  a.display_name,
  a.active AS actor_active,
  m.person_id AS global_person_id,
  g.id AS grant_id
FROM authz_actor_role_grants g
JOIN authz_roles r
  ON r.id = g.role_id
JOIN authz_actors a
  ON a.id = g.actor_id
JOIN auth_account_actors aa
  ON aa.actor_id = g.actor_id
 AND aa.scope_type = 'TENANT'
 AND aa.tenant_id = g.tenant_id
JOIN person_tenant_memberships m
  ON m.id = aa.membership_id
 AND m.tenant_id = aa.tenant_id
WHERE r.code = 'TENANT_ADMIN'
  AND g.active = 1
ORDER BY g.tenant_id, a.actor_key;
SQL

At final restored state, expect exactly the deterministic Tenant A Operator D and Tenant B Operator E assignments.

Tenant 0..2 violation check

sqlite3 -header -column backend/data/app.db <<'SQL'
SELECT
  g.tenant_id,
  COUNT(*) AS active_tenant_admins
FROM authz_actor_role_grants g
JOIN authz_roles r ON r.id = g.role_id
WHERE r.code = 'TENANT_ADMIN'
  AND g.active = 1
GROUP BY g.tenant_id
HAVING COUNT(*) > 2;
SQL

Expected: no rows.

Person 0..1-across-Tenants violation check

sqlite3 -header -column backend/data/app.db <<'SQL'
SELECT
  m.person_id AS global_person_id,
  COUNT(DISTINCT g.tenant_id) AS administered_tenants
FROM authz_actor_role_grants g
JOIN authz_roles r ON r.id = g.role_id
JOIN auth_account_actors aa
  ON aa.actor_id = g.actor_id
 AND aa.scope_type = 'TENANT'
 AND aa.tenant_id = g.tenant_id
JOIN person_tenant_memberships m
  ON m.id = aa.membership_id
 AND m.tenant_id = aa.tenant_id
WHERE r.code = 'TENANT_ADMIN'
  AND g.active = 1
GROUP BY m.person_id
HAVING COUNT(DISTINCT g.tenant_id) > 1;
SQL

Expected: no rows.

Distinct-Person-per-Tenant violation check

sqlite3 -header -column backend/data/app.db <<'SQL'
SELECT
  g.tenant_id,
  m.person_id AS global_person_id,
  COUNT(*) AS active_grants_for_same_person
FROM authz_actor_role_grants g
JOIN authz_roles r ON r.id = g.role_id
JOIN auth_account_actors aa
  ON aa.actor_id = g.actor_id
 AND aa.scope_type = 'TENANT'
 AND aa.tenant_id = g.tenant_id
JOIN person_tenant_memberships m
  ON m.id = aa.membership_id
 AND m.tenant_id = aa.tenant_id
WHERE r.code = 'TENANT_ADMIN'
  AND g.active = 1
GROUP BY g.tenant_id, m.person_id
HAVING COUNT(*) > 1;
SQL

Expected: no rows.

Canonical Person-binding violation check

sqlite3 -header -column backend/data/app.db <<'SQL'
SELECT
  g.id AS grant_id,
  g.tenant_id,
  g.actor_id
FROM authz_actor_role_grants g
JOIN authz_roles r ON r.id = g.role_id
WHERE r.code = 'TENANT_ADMIN'
  AND g.active = 1
  AND NOT EXISTS (
    SELECT 1
    FROM auth_account_actors aa
    JOIN person_tenant_memberships m
      ON m.id = aa.membership_id
     AND m.tenant_id = aa.tenant_id
    WHERE aa.actor_id = g.actor_id
      AND aa.scope_type = 'TENANT'
      AND aa.tenant_id = g.tenant_id
      AND TRIM(COALESCE(m.person_id, '')) <> ''
  );
SQL

Expected: no rows.

Bite 30H trigger presence

sqlite3 -header -column backend/data/app.db <<'SQL'
SELECT name
FROM sqlite_master
WHERE type = 'trigger'
  AND name IN (
    'trg_tenant_admin_global_person_insert',
    'trg_tenant_admin_global_person_update',
    'trg_tenant_admin_tenant_limit_insert',
    'trg_tenant_admin_tenant_limit_update',
    'trg_tenant_admin_distinct_person_insert',
    'trg_tenant_admin_distinct_person_update',
    'trg_tenant_admin_person_cross_tenant_insert',
    'trg_tenant_admin_person_cross_tenant_update'
  )
ORDER BY name;
SQL

Expected: 8 rows.

Foreign-key integrity

sqlite3 backend/data/app.db "PRAGMA foreign_key_check;"

Expected: no rows.


7. DevTools requirements

None.

No DevTools JavaScript helper is required for Bite 30H promotion. All functional cardinality behavior should be verifiable through the supported UI. The SQL above belongs only to the final technical promotion gate.


8. Final promotion checklist

Promote Bite 30H only when all are checked:

  • Tests 01–10 pass from the deterministic fixture.
  • Tenant A final state is Operator D only, 1/2, ACTIVE_READY.
  • Tenant B final state is Operator E only, 1/2, ACTIVE_READY.
  • make local-check passes.
  • Full Playwright passes.
  • Clean migration chain applies 000062 successfully.
  • Tenant >2 verifier returns no rows.
  • Person cross-Tenant verifier returns no rows.
  • Same-Person/same-Tenant duplicate verifier returns no rows.
  • Canonical Person-binding verifier returns no rows.
  • All eight Bite 30H triggers are present.
  • PRAGMA foreign_key_check returns no rows.
  • No unresolved UI observability/cardinality defect remains.

If every item passes, Bite 30H is ready for promotion.