--- phase: quick-260729-d3k plan: 01 type: execute wave: 1 depends_on: [] files_modified: - apps/api/src/ldap/ldap.service.ts - apps/api/src/ldap/ldap.service.spec.ts - apps/web/src/app/(portal)/admin/ldap/page.tsx - apps/web/src/messages/de.json - apps/web/src/messages/en.json autonomous: true requirements: - 260729-d3k must_haves: truths: - "The Base-DN admin field accepts multiple DNs (one per line) and persists them as a single `\\n`-separated String — no Prisma schema change, no migration." - "LDAP sync searches EVERY configured base DN and merges/dedupes results by entry `dn` across all three search paths (collectSearchEntries, listGroups, searchUsers)." - "An empty groupFilterDns no longer blocks sync: with >=1 base DN, all users under the base DN(s) are synced with `sanitizedFilter` (no memberOf restriction)." - "groupFilterDns is now an OPTIONAL extra restriction: `ou=` entries are additional search bases (plain filter); non-`ou=` (group) DNs become a memberOf constraint applied to the base-DN search." - "CRITICAL SAFETY: the syncUsersForTenant early-return No-Op keys ONLY on the parsed base-DN list being EMPTY. An empty base-DN list is the sole condition that skips search + the deactivation loop; an empty groupFilterDns never triggers the No-Op and therefore never mass-deactivates users." - "i18n (de + en) describes the Base-DN(s) as the sync scope and the group filter as an optional additional restriction; the old '...nichts synchronisiert' / '...nothing is synced' wording is gone." artifacts: - "apps/api/src/ldap/ldap.service.ts — parseBaseDns() helper + multi-base collectSearchEntries/listGroups/searchUsers + base-DN-list-keyed deactivation guard." - "apps/api/src/ldap/ldap.service.spec.ts — empty-base-DN No-Op test (replaces the empty-groupFilterDns No-Op), multi-base merge/dedup test, adjusted exclude-list/groupFilter specs." - "apps/web/src/app/(portal)/admin/ldap/page.tsx — multi-line