Files
paliad/internal/db
m a61c1490e3 feat(t-paliad-139): Phase 3 — derived_peer authority extension to t-138 approval gate
Wires DerivationService.EffectiveProjectRole into the t-paliad-138
approval ladder so partner-unit-derived members with derive_grants_authority=true
can act as approvers (per design §4.2). When they sign off, the audit row
records decision_kind='derived_peer' — a third value alongside the existing
'peer' and 'admin_override' — so the chronology discloses the derivation
chain.

Schema (migration 055 update)
-----------------------------
  - paliad.approval_requests.decision_kind CHECK extended to accept
    'derived_peer'. Down migration restores the t-138 two-value CHECK.
    Live SQL dry-run confirmed the new value is accepted.

Service layer
-------------
  - approval_levels.go: new constant DecisionKindDerivedPeer.
  - approval_service.go (4 sites widened with the derivation EXISTS branch):
      1. canApprove — third resolution step after global_admin + direct/
         ancestor team membership: matches partner-unit-derived members
         on path with derive_grants_authority=true and a unit_role whose
         approval_role_from_unit_role mapping meets the threshold.
         Returns DecisionKindDerivedPeer when this branch is the one that
         passed.
      2. hasQualifiedApprover (the deadlock-check at submit time) —
         widened so a project with no direct approvers but an authority-
         granting unit attachment is still submittable.
      3. ListPendingForApprover (the /inbox query) — third UNION ALL
         branch so derived authority sees their queue.
      4. PendingCountForUser (the bell-badge query) — same widening so
         derived authority sees the count tick.
  All four queries reuse paliad.approval_role_from_unit_role(text) added
  by Phase 2 of migration 055.

Frontend
--------
  - 2 i18n keys (DE+EN): approvals.decision_kind.derived_peer →
    "Genehmigt durch abgeleitetes Mitglied (Partner Unit)" / "Approved by
    derived member (Partner Unit)". Verlauf rendering of the third
    decision_kind value works through the existing translateEvent /
    decision_kind switch with no other change. 1606 keys total.

Strict-default unchanged
------------------------
Derived members are visibility-only by default. Authority requires the
project lead/admin to explicitly flip derive_grants_authority=true on the
project_partner_units row (UI on /projects/{id} Team tab, Phase 2). This
preserves the m-locked Q12 stance.

Phase 3 closes the t-paliad-139 implementation. m's bug closes (Phase 1),
the derivation schema is in place (Phase 2), and approval authority
flows through the new ladder (Phase 3).
2026-05-06 16:45:19 +02:00
..