Parent-skill auto-approval: race fix + reconciliation
Shipped 2026-05-31
Members who passed every child skill under a parent could be left without the parent skill — permanently, with no self-heal — when concurrent child approvals raced the synchronous auto-approval. Fixed on three layers:
- Serialised the live path — a per-member named lock on a single
pooled connection (the previous lock leaked across pool connections and
never actually serialised). That lock is the real serialisation
guarantee: the logbook keeps every skill re-attempt, so there’s no
unique key to lean on. The logbook-create duplicate guard was also
folded into a single
INSERT … WHERE NOT EXISTS, which narrows the race window for the routine’s other (unlocked) callers. - Serialised the UIs — admin bulk approvals run sequentially (not
Promise.all) and per-row actions are disabled while one is in flight; the member portal blocks a second submit until the first resolves. - Reconciliation safety net — a one-shot back-fill of currently-stuck
members plus an hourly cron (
APP_RECONCILE_PARENT_SKILLS) that re-derives and assigns any missed parent skills, cascading up the tree.
Ships the API’s first forward-only SQL migrations (api/sql/migrations/),
applied by the runner from #649/#650. The runner tracks the API’s
migrations in a dedicated api_schema_migrations table so it can’t
collide with the assistant’s schema_migrations in the shared IBA schema.
See member lifecycle → parent-skill auto-approval.
- Jira: TNBUGS-1728
- PR: #648
Last updated on