Fix the rollout checker for a column rename

Review the AI's codeMedium15 min

Your task

An agent renamed users.fullname to full_name with one migration and the matching code change in the same PR. During the rolling deploy, old and new app versions served traffic side by side, and the old ones failed on the missing column. Your team now checks every schema plan, step by step, before it runs.

An assistant wrote first_unsafe_step(initial_columns, initial_code, steps). It is in the editor. It has a bug. Find it and fix it.

Input and output:

  • initial_columns: the columns the table has now.
  • initial_code: the columns the running code reads, comma separated.
  • steps: the plan, in order, such as migrate:+full_name, migrate:-fullname, migrate:fullname>full_name or deploy:id,full_name.
  • Return the number of the first unsafe step, counting from 1, or 0 when every step is safe.

Rules the tests check:

  • Track the schema, the set of existing columns, which starts as initial_columns exactly as given, and the running code's columns. A comma separated list of columns, like initial_code or a deploy payload, is split on ,, each name trimmed, empty names ignored. Names in migrate steps are used exactly as written, with no trimming.
  • Split each step at its first : into a kind and a payload. A step with no :, or with a kind other than migrate or deploy, does nothing.
  • migrate:+col adds the column, if the name is not empty. It is always safe.
  • migrate:-col drops the column. It is unsafe when the running code reads it.
  • migrate:old>new, split at the first >, renames a column. It is unsafe when the running code reads old. Otherwise old is removed and new is added, if not empty. Any other migrate payload does nothing.
  • deploy:cols rolls out code that reads cols. During the rollout the old and new code both serve traffic, so it is unsafe when any column read by the running code or by the new code is missing from the schema. Afterwards the running code reads cols.

Examples

first_unsafe_step(initial_columns: list[str], initial_code: str, steps: list[str]) -> int
Example 1
Inputfirst_unsafe_step(["id", "fullname"], "id,fullname", ["migrate:fullname>full_name", "deploy:id,full_name"])
Output1
Example 2
Inputfirst_unsafe_step(["id", "fullname"], "id,fullname", ["migrate:+full_name", "deploy:id,fullname,full_name", "deploy:id,full_name", "migrate:-fullname"])
Output0
Example 3
Inputfirst_unsafe_step(["id", "fullname"], "id,fullname", ["deploy:id,full_name"])
Output1