# TeamController::destroy does not write an activity log

**Filed:** Batch 53, 2026-08-02.
**Status:** deferred to post-demo. Incident that triggered this ticket is documented below.

## Problem

Staff (teams) rows can be soft-deleted with no audit trail. There is no
`activity_logs` entry recording who did it, when, or why. Session records,
`program_student_assignments` rows and other references to the deleted
staff are silently orphaned from live surfaces (the recorder's Staff picker
excludes soft-deleted staff, so any card or session referencing them either
disappears from the picker or renders with a blank/space Staff line).

Every other destructive action in the app writes to `activity_logs`
(Team `portal_*` actions, Program soft-delete via bulk actions, Invoice
voiding, Lead archival). `TeamController::destroy` does not.

## Incident that surfaced this

On 2026-08-02 at 14:36:46, `teams.id=538` (Kerthiga Manokumar) was
soft-deleted (`isDelete=1`). Diagnostic:

```sql
-- teams row
SELECT id, teamFirstName, teamLastName, isDelete, created_at, updated_at
  FROM teams WHERE id = 538;
-- returns: 538, Kerthiga, Manokumar, 1, 2026-07-27 19:38:54, 2026-08-02 14:36:46

-- users row unchanged
SELECT id, status, isDelete, updated_at FROM users WHERE staffId = 538;
-- returns: 283, active, 0, 2026-07-27 19:38:55

-- activity_logs — zero entries for this entity
SELECT COUNT(*) FROM activity_logs
 WHERE entityType IN ('Team','team','Staff','staff') AND entityId = 538;
-- returns: 0
```

The result:
- Kerthiga is the seed's designated ABA staff. Every seeded psa row
  and session record references her staffId.
- The recorder's Staff picker excludes her because `teams.isDelete=1`.
- Today's ABA Sessions cards render an empty Staff line (space) instead
  of her name (Batch 51 §1 fixed the empty-line render, but the missing
  data is still missing data).
- Nobody knows who deleted her, why, or when to reverse it.

Kerthiga was NOT restored — that's a data decision, not ours to make
silently. Amanda Cruz was given the ABA Staff role instead as the demo's
working ABA staff. Kerthiga stays soft-deleted, but with no explanation.

## Fix

Add an `activity_logs` write in `TeamController::destroy` mirroring the
existing pattern used by Team portal actions:

```php
DB::table('activity_logs')->insert([
    'logType'    => 'system',
    'entityType' => 'Team',
    'entityId'   => $id,
    'action'     => 'soft_deleted',
    'description' => sprintf('Soft-deleted staff %s %s', $team->teamFirstName ?? '', $team->teamLastName ?? ''),
    'metadata'   => json_encode([
        'staff_id' => $id,
        'user_id'  => optional(DB::table('users')->where('staffId', $id)->first())->id,
        'active_psa_rows' => DB::table('program_student_assignments')->where('staffId', $id)->where('isDelete', 0)->count(),
        'session_records' => DB::table('session_data_records')->where('staffId', $id)->where('isDelete', 0)->count(),
    ]),
    'ipAddress'  => request()->ip(),
    'userAgent'  => request()->userAgent(),
    'created_by' => Auth::id(),
    'created_at' => now(),
    'updated_at' => now(),
]);
```

Extend to any other destructive team-lifecycle endpoints
(`suspend`, `restore`, `hardDelete`) with the same shape.

Also worth adding when the fix lands: block the delete (or require
`?force=1`) when the staff has active `program_student_assignments` or
non-deleted `session_data_records`. Silent orphaning is the deeper
problem — the audit log addresses "who did it"; the guard addresses
"could this have been prevented".

## Related

- `docs/post-demo/session-progress-access-check.md` — parallel ticket for
  scoped-role access on session progress reads.
- `docs/post-demo/aba-staff-designation.md` — the Batch 53 §2 workaround
  making an explicit `teams.is_aba_staff` boolean the post-demo target
  instead of role-name inference.
