Final whole-branch review FIX 1 + FIX 5.
- setFrontTemplate / setModuleTail now reject a key that collides across the
front/tail halves (checked against the default tail, every configured
per-module tail, and the shared front respectively) instead of letting
milestoneTemplate silently compose a duplicate-key list.
- ensureProjectMilestones adds each inserted key to its own `existing` set
inside the loop, so a template that still ends up with a duplicate key
(a pre-existing bad setting) is deduped instead of hitting the
UNIQUE (client_module_id, key) violation a second time.
- GET /client-modules/:id/milestones now wraps ensureProjectMilestones +
listProjectMilestones in try/catch -> 400, matching its POST sibling,
instead of an unhandled async rejection that hung the request.
- advanceStatusForStep now stamps client_module.installed_on / trained_on
from the triggering milestone's own done_on the first time status
advances into 'installed' / 'trained' (never overwriting an existing
date), replacing the UI date-writers the Client Detail redesign removed
without a replacement. Reflected in the same audit row as the status
change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>