Codex use case
Migrar um módulo de validação
Atualize um módulo e comprove que seu comportamento público permanece o mesmo.
Forneça ao Codex instruções de migração com escopo delimitado, fixtures para os testes de contrato e um ambiente de nuvem já preparado. Peça ao Codex que atualize um único módulo de validação e entregue a alteração, as evidências de equivalência de comportamento, as diferenças que persistirem e as instruções para reverter a mudança.
Best for
- Equipes de engenharia que estão migrando um componente de validação legado.
- Migrações com contratos explícitos e testes reproduzíveis.
- Equipes que precisam de uma pequena alteração revisada enquanto os demais módulos continuam funcionando.
Contents
Migrar um módulo de validação
Atualize um módulo e comprove que seu comportamento público permanece o mesmo.
Forneça ao Codex instruções de migração com escopo delimitado, fixtures para os testes de contrato e um ambiente de nuvem já preparado. Peça ao Codex que atualize um único módulo de validação e entregue a alteração, as evidências de equivalência de comportamento, as diferenças que persistirem e as instruções para reverter a mudança.
Forneça ao Codex instruções de migração com escopo delimitado, fixtures para os testes de contrato e um ambiente de nuvem já preparado. Peça ao Codex que atualize um único módulo de validação e entregue a alteração, as evidências de equivalência de comportamento, as diferenças que persistirem e as instruções para reverter a mudança.
Related links
Best for
- Equipes de engenharia que estão migrando um componente de validação legado.
- Migrações com contratos explícitos e testes reproduzíveis.
- Equipes que precisam de uma pequena alteração revisada enquanto os demais módulos continuam funcionando.
Starter prompt
Quando usar este fluxo de trabalho
Use este fluxo de trabalho quando a migração tiver um escopo bem delimitado e houver testes que comparem o comportamento anterior com o novo. Forneça ao Codex as instruções da migração, o módulo e o código que o chama, as fixtures de contrato congeladas e um ambiente de nuvem já preparado. Peça uma única alteração de escopo restrito, com evidências e instruções para revertê-la.
O exemplo migra um modelo de validação de um serviço de pedidos da v1 para a v2 do Pydantic. Ele inclui a implementação original, uma etapa de preparação para garantir a compatibilidade e o módulo final. Os testes exercitam o roteador real do FastAPI, o tratador de erros, a serialização das respostas, a rota legada adjacente e o documento OpenAPI público. O exemplo não inclui migração de banco de dados nem implantação.
Prepare o ambiente de nuvem
-
Escolha Trabalhar em > Nuvem e selecione um ambiente publicado.
-
Para criar um ambiente, selecione Criar ambiente, escolha os repositórios do GitHub e peça ao Codex que prepare o ambiente de execução, as dependências e os serviços de teste isolados.
-
Revise o relatório de configuração, corrija os problemas apontados pelas verificações e selecione Publicar antes de iniciar uma nova tarefa.
- Siga o guia de ambientes de nuvem.
- Mantenha as orientações do repositório no arquivo AGENTS.md.
Se as dependências ou a configuração dos serviços mudarem, edite o ambiente reutilizável, teste-o e selecione Republicar.
Valide o ambiente atualizado em uma nova tarefa. A atualização do repositório preserva os caches de dependências sem executar novamente os comandos de instalação ou inicialização. As tarefas existentes mantêm seu próprio estado.
Configure separadamente os destinos de rede necessários e as credenciais.
Em cada etapa, registre a referência do ambiente e de sua configuração, o commit do código-fonte, a versão do Python até o nível de patch, as versões do framework e das dependências, a versão do gerenciador de pacotes e o hash do arquivo de lock. Confira essas versões dentro da nova tarefa antes de usar os resultados.
Execute o exemplo
Baixe o exemplo completo de migração. Ele inclui três projetos uv independentes, os respectivos arquivos uv.lock com as dependências resolvidas, as implementações do aplicativo, um adaptador compartilhado para erros e schema, as fixtures HTTP congeladas e o executor dos testes de contrato.
Use o Python 3.11, 3.12 ou 3.13, instale o uv e execute os comandos a seguir:
unzip sample.zip
cd migrate-a-validation-module
uv run --project support/target --python python3 --frozen python run.py --compare-stages --output evidence
A comparação cria um .venv separado para cada etapa e instala as dependências com uv sync --frozen. Todas as etapas usam o interpretador Python que iniciou a execução e as mesmas fixtures de HTTP e de schema. As versões das dependências permanecem fixas.
O exemplo exclui o Python 3.14 e as versões posteriores porque a camada de compatibilidade com o Pydantic v1 não oferece suporte a essas versões. Uma execução bem-sucedida termina com esta saída:
PASS: legacy, prepared, target HTTP/schema/serialization contracts match
Native model schemas differ; the existing public schema adapter is preserved.
Leia evidence/legacy.json, prepared.json, target.json e summary.json. Esses arquivos contêm as respostas observadas, as versões do ambiente de execução, os hashes do código-fonte, das fixtures e dos arquivos de lock, os comandos, os códigos de saída e os caminhos dos logs brutos.
O executor testa os endpoints HTTP do aplicativo de exemplo usando o cliente de testes do FastAPI, o TestClient, que roda no mesmo processo. Execute os testes na tarefa da nuvem que você escolheu e guarde as evidências dessa tarefa.
Execute python3 run.py sem --compare-stages apenas para verificar o modelo final em um ambiente com o Pydantic v2 instalado. Esse modo independente verifica sete casos de validação e serialização. Ele não executa verificações de HTTP nem compara os resultados entre as etapas. Para verificar uma migração, use o comando de comparação completa acima.
A comparação completa verifica 23 casos HTTP em cada etapa, o documento OpenAPI público e a serialização das respostas. Todos os testes passaram localmente nas versões 3.11, 3.12 e 3.13 do Python. O arquivo README.md incluído no download registra as versões testadas e o histórico de execução. Execute a comparação atual no seu ambiente de nuvem para registrar os resultados obtidos nele.
Mantenha separadas as etapas com suas respectivas dependências
| Etapa | Python / FastAPI / Pydantic |
Escopo da migração |
|---|---|---|
| Legada | 3.11–3.13 / 0.119.0 / 1.10.22 |
Os modelos existentes fazem importações de pydantic. |
| Base preparada | 3.11–3.13 / 0.119.0 / 2.12.3 |
Os dois modelos usam pydantic.v1. Registre a versão exata da v1 incluída no pacote. |
| Final | 3.11–3.13 / 0.119.0 / 2.12.3 |
Apenas OrderInput migra para a v2. A rota adjacente mantém seu próprio modelo v1. |
Os arquivos de lock das dependências são idênticos nas etapas preparada e final. A migração do módulo altera apenas o modelo e o método que ele usa diretamente para serialização. O ambiente de dependências permanece o mesmo.
A preparação é uma alteração revisada separadamente. Nessa etapa, os adaptadores das rotas v1 restantes também passam a usar dict(by_alias=True) para retornar dicionários com os aliases como chaves antes da validação da resposta. Retornar o modelo v1 diretamente em um ambiente que usa a v2 pode causar a perda dos aliases públicos.
Na etapa final, o adaptador de pedidos usa model_dump(by_alias=True), e a rota v1 adjacente mantém o adaptador da etapa de preparação.
Segundo o guia de migração do FastAPI, o suporte temporário à coexistência de rotas v1 e v2 abrange as versões de 0.119.0 a 0.127.x. A versão 0.128.0 removeu esse suporte.
O exemplo fixa a versão 0.119.0 para demonstrar essa transição. Em um aplicativo com uma versão mais recente do FastAPI, use exclusivamente a v2 em todo o escopo da migração. Defina quem será responsável pela camada temporária de compatibilidade e em que condição ela deverá ser removida. Não aninhe modelos v1 e v2. Essa camada não permite migrar para o Python 3.14.
Revise os testes de contrato
Os casos HTTP congelados abrangem solicitações bem-sucedidas, observação omitida ou definida explicitamente como nula, IDs ausentes e inválidos, vários campos desconhecidos, combinações de erros de campo, observações inválidas, JSON malformado, tipo de conteúdo incorreto, corpo ausente, aliases, corpos em formato de array ou string e a rota legada adjacente. Os testes comparam os códigos de status, o cabeçalho Content-Type e o conteúdo completo das respostas após a análise do JSON, sem remover campos de erro nem alterar os tipos esperados.
Desde a etapa legada, o aplicativo define seu próprio formato de resposta de erro e seu documento OpenAPI público. Os adaptadores de erros e de schema continuam em uso. A implementação final preserva a ordem dos erros da etapa legada. Primeiro vêm os erros dos campos declarados e, em seguida, as chaves desconhecidas em ordem alfabética.
Os testes comparam o array error.fields inteiro na ordem recebida, sem reordenar as respostas observadas. O executor registra separadamente o schema nativo de cada modelo e verifica que os schemas nativos são diferentes. A equivalência do schema público não implica que os schemas gerados pela dependência sejam idênticos.
O serviço preserva o comportamento legado para os dados de entrada. Ele aceita como objeto um array de pares, por exemplo, [["order_id", "ord_123"]]. Tanto [] quanto uma string JSON vazia são convertidos em objetos vazios e geram um erro de ID ausente. Uma string não vazia continua sendo um corpo inválido. O modelo final mantém as conversões em que dict(value) é bem-sucedido. Para aceitar apenas objetos como entrada, é preciso que o responsável decida alterar o contrato. Alterar o resultado esperado do teste não substitui essa decisão.
Antes de editar o código do seu repositório, congele as respostas que ele realmente retorna. Preserve os campos opcionais com valores padrão explícitos, mantenha o rigor da validação e o tratamento de campos desconhecidos e teste o tratador de erros real e a serialização das respostas.
Os erros brutos de validação variam entre as versões. O guia Pydantic: migração entre versões também descreve mudanças nos aliases, na coerção de tipos, nos campos opcionais e nas funções de validação personalizadas. A v2 não converte um TypeError lançado dentro dessas funções em ValidationError.
Examine o executor dos testes de contrato
Este trecho vem do arquivo run.py incluído no download. Extraia o ZIP e execute os arquivos completos. O pacote inclui as etapas com versões de dependências fixas e as fixtures HTTP.
Compare as três etapas
class OrderInput(BaseModel):
internal_order_id: StrictStr = Field(alias="order_id")
customer_note: StrictStr | None = None
model_config = ConfigDict(extra="forbid", populate_by_name=False)
@model_validator(mode="before")
@classmethod
def preserve_legacy_mapping(cls, value):
# v1 tries dict(value), so pairs, [] and "" can become objects.
# Preserve successful conversions without changing failed-input errors.
if not isinstance(value, dict):
try:
return dict(value)
except (TypeError, ValueError):
return value
return value
def serialize_order(payload):
return OrderInput(**payload).model_dump(by_alias=True)
def preserve_legacy_error_order(fields):
# v1 reports declared-field errors first and unknown keys alphabetically.
# v2 keeps unknown keys in input order; preserve the existing HTTP contract.
known = [field for field in fields if field["code"] != "extra_forbidden"]
extra = sorted(
(field for field in fields if field["code"] == "extra_forbidden"),
key=lambda field: field["path"],
)
return known + extra
def check_target_model():
for payload, expected in (
({"order_id": "ord_123"}, {"order_id": "ord_123", "customer_note": None}),
(
{"order_id": "ord_123", "customer_note": "Leave at desk"},
{"order_id": "ord_123", "customer_note": "Leave at desk"},
),
(
{"order_id": "ord_123", "customer_note": None},
{"order_id": "ord_123", "customer_note": None},
),
):
assert serialize_order(payload) == expected
for payload, expected in (
({}, [("order_id", "missing")]),
({"order_id": 123}, [("order_id", "string_type")]),
(
{"order_id": "ord_123", "customer_note": 17},
[("customer_note", "string_type")],
),
(
{"order_id": "ord_123", "unexpected": True},
[("unexpected", "extra_forbidden")],
),
):
try:
OrderInput(**payload)
except ValidationError as error:
assert [
(item["loc"][0], item["type"]) for item in error.errors()
] == expected
else:
raise AssertionError(f"Accepted invalid order: {payload}")
print("PASS: target model validation and serialization (7 cases)")
print("Model-only checks; use --compare-stages for HTTP migration parity.")
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--output", type=Path, default=ROOT / "evidence")
parser.add_argument("--compare-stages", action="store_true")
args = parser.parse_args()
if not args.compare_stages:
check_target_model()
return
args.output.mkdir(parents=True, exist_ok=True)
reports = []
commands = []
for stage in ("legacy", "prepared", "target"):
project = Path("support") / stage
stage_commands = [
[
"uv",
"sync",
"--project",
str(project),
"--python",
sys.executable,
"--frozen",
],
[
"uv",
"run",
"--project",
str(project),
"--python",
sys.executable,
"--frozen",
"python",
"test/run_stage.py",
stage,
str(args.output / f"{stage}.json"),
],
]
for command in stage_commands:
completed = subprocess.run(
command, cwd=ROOT, capture_output=True, text=True
)
label = f"{stage}-{len(commands)}"
(args.output / f"{label}.log").write_text(
completed.stdout + completed.stderr
)
commands.append(
{
"command": command,
"exit_code": completed.returncode,
"log": f"{label}.log",
}
)
(args.output / "commands.json").write_text(
json.dumps(commands, indent=2, sort_keys=True) + "\n"
)
if completed.returncode:
print(completed.stdout + completed.stderr, file=sys.stderr)
raise SystemExit(completed.returncode)
print(completed.stdout, end="")
reports.append(json.loads((args.output / f"{stage}.json").read_text()))
assert all(report["python"] == platform.python_version() for report in reports)
for report in reports[1:]:
for key in (
"observations",
"public_openapi",
"direct_call",
"fixture_sha256",
"schema_fixture_sha256",
):
assert report[key] == reports[0][key], f"Parity failed: {key}"
assert reports[1]["lock_sha256"] == reports[2]["lock_sha256"]
native_schema_differs = (
reports[0]["native_model_schema"] != reports[2]["native_model_schema"]
)
assert native_schema_differs, "The example should expose native schema drift"
manifest = {
str(path.relative_to(ROOT)): digest(path)
for path in sorted(ROOT.rglob("*"))
if path.is_file()
and not path.resolve().is_relative_to(args.output.resolve())
and not any(
part in {".venv", "__pycache__", "evidence", ".ruff_cache"}
for part in path.relative_to(ROOT).parts
)
}
summary = {
"status": "pass",
"scope": "synthetic in-process FastAPI TestClient execution",
"stages": [report["stage"] for report in reports],
"http_cases_per_stage": reports[0]["http_cases"],
"native_model_schema_differs": native_schema_differs,
"public_http_and_schema_parity": True,
"commands": commands,
"source_sha256": manifest,
}
(args.output / "summary.json").write_text(
json.dumps(summary, indent=2, sort_keys=True) + "\n"
)
print("PASS: legacy, prepared, target HTTP/schema/serialization contracts match")
print(
"Native model schemas differ; the existing public schema adapter is preserved."
)Execute o fluxo de trabalho no seu repositório
- Escreva as instruções da migração. Identifique o módulo, o código que o chama diretamente, o responsável, as versões aprovadas, os testes de contrato e o código fora do escopo. Inclua as instruções de reversão. Interrompa a migração se ela exigir uma atualização mais ampla das dependências ou alterar o comportamento público.
- Estabeleça a base de comparação. Peça ao Codex que leia o
AGENTS.md, as instruções da migração, as orientações oficiais de migração, os arquivos de lock, o código que chama o módulo e os testes. Registre o commit inicial e as versões instaladas. Explique ou corrija as falhas nas verificações iniciais antes de afirmar que os comportamentos são equivalentes. - Prepare a compatibilidade separadamente. Se dependências compartilhadas vincularem o módulo a outras partes do código, revise primeiro essa preparação. Demonstre que os testes com as mesmas fixtures passam tanto na implementação original quanto na preparada.
- Migre uma parte por vez. Use o prompt inicial desta página. Altere o módulo e seu adaptador direto. Preserve os módulos que não fazem parte da migração e identifique qualquer código de compatibilidade que ainda exista.
- Execute os mesmos testes. Compare as fixtures de HTTP e de schema da implementação de referência com as da implementação final sem alterar os resultados esperados. Execute as verificações de integração, lint e tipos afetadas pela mudança e salve a saída com uma referência ao estado final do código-fonte e das dependências.
- Revise o material antes de entregá-lo. Examine o diff, as evidências da execução, as diferenças ainda não resolvidas, a camada de compatibilidade restante e as instruções de reversão. Crie um pull request em rascunho somente se as ferramentas e as permissões do repositório permitirem. Deixe o merge, a implantação e a migração da próxima parte do código a cargo de quem fizer a revisão.
Registre o que foi verificado
Use um registro de evidências para cada critério:
Criterion: [behavior and test]
Environment: [setup reference; actual runtime and dependency versions]
Before: [commit/diff ID; command; exit code; observed result; evidence path]
After: [final commit/diff ID; command; exit code; observed result; evidence path]
Status: [pass / fail / blocked / not run]
Limits: [untested behavior, missing prerequisite, or required owner decision]
Uma importação bem-sucedida, uma verificação de lint sem erros ou um snapshot regenerado não comprovam a equivalência da API. Registre separadamente as diferenças intencionais e repita as verificações se o código-fonte final mudar. Ao adaptar o exemplo, inclua nos testes de contrato o código que faz as chamadas e os serviços do seu aplicativo.
Related use cases
Investigue um incidente e teste uma correção
Forneça ao Codex Cloud um dossiê do incidente sem dados sensíveis e um ambiente de teste...
Crie jogos de navegador
Use o Codex para transformar a descrição do jogo em um plano bem definido e, em seguida, em...
Crie uma CLI que o Codex possa usar
Peça ao Codex que crie uma CLI que ele possa executar de qualquer pasta, combinar com...