Skip to Content

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