- Extract calculateEffectiveGrants into n8n/shared/effective-grants.js
(single source of truth), inlined at build time into W1/W2/W3 via
gen_w*.js + inline_shared.js — removes the duplicated algorithm.
- W1 now provisions with a status branch:
suspended/deleted -> disable AList user + clear role scopes (soft delete)
active -> create/update user+role, RECOMPUTE desired scopes (never restore
stale), re-enable
- Loop guard corrected: check AList user LIVE disabled state via
GET /api/admin/user/get (sync_status alone breaks suspend->resume).
- W2/W3 rebuilt on shared lib (identical behaviour).
- Tests: test_effective_grants.js (15), test_w3_refactored.js (13),
test_w1_lifecycle.js (23) — all green.
- Drop superseded w2-compute.js / w3-compute.js / test_w3.js.