A explicação diz que o array e cada item são novos, então alterar o resultado não deveria afetar a entrada. Mas, quando o chamador converte um label para maiúsculas, o original também muda. A cópia superficial preservou a referência ao objeto aninhado.

Baixe o laboratório de prévia de encomendas. Código, dados e explicação deliberadamente incorreta foram construídos do zero para o tutorial. Use Node.js 22 ou posterior. Não há instalação, credenciais ou rede.

Mantenha a afirmação ao lado do código

Confira se alterar o label de um parcel copiado também muda a entrada. Leia a função de cópia junto com seu chamador.

Abra parcel-preview-lab no Yenpo. Use Command-P (Go to File) para abrir claims.md, src/copy-parcels.mjs e src/preview.mjs em abas. Compare a afirmação com a cópia e seu chamador. Search All Files encontra row.label.text; examine a condição uppercase ao redor, sem tratar uma ocorrência como prova de que toda chamada muda a entrada.

Esta leitura não exige modelo nem servidor de linguagem. Obtenha o Yenpo para Mac para abrir o exemplo ou consulte a ajuda de busca de arquivos e texto se perder o lugar. Execute o observador e as verificações no Terminal e registre as evidências.

Divida a explicação em afirmações

Esta é a explicação didática a conferir:

copyParcels cria um array novo e um objeto novo para cada encomenda. Os rótulos são cópias independentes, então preview pode convertê-los para maiúsculas sem alterar a entrada.

A primeira frase contém duas afirmações. A segunda acrescenta uma sobre propriedade e uma conclusão comportamental. Tratar tudo como “certo ou errado” perderia os acertos junto do erro.

ID Afirmação O que a confirma ou refuta?
C1 O array retornado é novo. Comparar identidades dos arrays de entrada e saída.
C2 Cada objeto externo de encomenda é novo. Comparar identidades das encomendas correspondentes.
C3 Cada rótulo aninhado é independente. Comparar identidades dos rótulos, não só o texto.
C4 Converter a prévia para maiúsculas preserva a entrada. Alcançar a ramificação e examinar a entrada do chamador depois.
C5 Qualquer mutação da entrada é um bug de negócio. Encontrar requisito ou contrato de propriedade; o código não o fornece sozinho.

C5 é uma conclusão que o leitor poderia acrescentar, não parte da citação. Separe-a para que refutar C4 não decida silenciosamente o requisito do produto.

O escopo é um array denso de objetos mutáveis comuns, cada um com um objeto label simples e string text. O laboratório não cobre getters, proxies, instâncias de classes ou I/O externo. Mantenha esse escopo junto à explicação, sem generalizar a execução para todo valor JavaScript.

Siga a referência através da função

Abra src/copy-parcels.mjs:

export function copyParcels(parcels) {
  return parcels.map((parcel) => ({ ...parcel }));
}

O literal cria um objeto externo novo em cada callback. Copia o valor da propriedade label, que é uma referência a objeto. Não cria um novo rótulo aninhado. A MDN documenta a distinção em sintaxe spread e cópia rasa.

Agora abra o chamador real, src/preview.mjs:

import { copyParcels } from './copy-parcels.mjs';

export function preview(parcels, { uppercase = false } = {}) {
  const rows = copyParcels(parcels);
  if (uppercase) {
    for (const row of rows) {
      row.label.text = row.label.text.toUpperCase();
    }
  }
  return rows;
}

A gravação importante é em row.label.text. Reatribuir row.id afetaria o objeto externo novo; atribuir através de row.label alcança o objeto compartilhado. Na fixture, a estrutura de referências logo após copiar é:

input ──> original array ──> original parcel ──┐
                                             ├──> one label object
rows  ──> new array ──────> new parcel ────────┘

Encontrar a atribuição não basta para explicar uma execução específica. O caminho padrão a ignora. Para testar C4, forneça uppercase: true e texto cuja forma em maiúsculas seja diferente.

Execute a ramificação que pode contradizer a explicação

Descompacte, entre em parcel-preview-lab e execute:

node observe.mjs

O observador cria uma fixture nova para cada ramificação. Salva o texto original como string antes de chamar preview, depois imprime identidades e valores. Os resultados são:

Observação uppercase: false uppercase: true
Mesmo array? false false
Mesma encomenda externa? false false
Mesmo rótulo aninhado? true true
Texto salvo antes da chamada fragile fragile
Texto da entrada após a chamada fragile FRAGILE
Texto da prévia após a chamada fragile FRAGILE

É um contraexemplo alcançável a C4: o chamador fornece a entrada, escolhe a ramificação de maiúsculas e observa a alteração do próprio texto aninhado. C1 e C2 continuam verdadeiras. C3 já é refutada pela identidade compartilhada, inclusive no caminho sem atribuição.

Um rótulo já em maiúsculas é uma sonda mais fraca: a gravação ocorre, mas o valor não muda. Um array vazio não entra no laço. Essas entradas passam em “o texto não mudou” sem estabelecer propriedade independente. Os testes de evidência incluem ambos os controles.

Não confunda valores iguais com objetos independentes

Antes da gravação, esta asserção passa:

assert.deepEqual(rows, input);

Ela informa que estrutura e valores comparados coincidem, não que objetos aninhados correspondentes tenham identidades distintas. Estas três asserções também passam simultaneamente:

assert.notStrictEqual(rows, input);
assert.notStrictEqual(rows[0], input[0]);
assert.strictEqual(rows[0].label, input[0].label);

O laboratório importa assert de node:assert/strict. Veja a semântica na referência de asserções do Node. Preserve ambos os tipos de evidência: identidades descrevem compartilhamento; valores antes/depois mostram a consequência da gravação alcançável.

Não use const before = input como estado “anterior”. A variável ainda apontaria para o mesmo array e também poderia mostrar a mutação. O observador salva a string primitiva beforeText antes da chamada. Um valor capturado e uma referência mutável respondem a perguntas diferentes.

Transforme o resultado em uma explicação revisada

Execute os comandos separadamente; o primeiro deve falhar deliberadamente:

node --test checks/original-claims.test.mjs
node --test checks/evidence.test.mjs

A execução das afirmações originais tem dois testes passando e dois falhando e sai com código não zero. C1 e C2 passam; independência dos rótulos e preservação da entrada falham. A execução de evidências tem seis testes passando e sai com zero. A documentação do executor do Node descreve a execução. Nenhum código da aplicação muda entre elas: as asserções expressam afirmações diferentes sobre o mesmo programa.

Registre o veredito e seu limite, não apenas verde ou vermelho:

ID Veredito Evidência e limite
C1, C2 Sustentadas no escopo Array e objetos externos novos: map, literal e verificações de identidade concordam.
C3 Refutada Rótulos aninhados compartilhados: entrada e saída têm a mesma referência.
C4 Refutada Maiúsculas mudam a entrada: fragile vira FRAGILE no caminho true.
C5 Indeterminada Não foi estabelecida violação de requisito: nenhum contrato de propriedade foi fornecido.

Uma explicação corrigida seria:

copyParcels cria um array e objetos externos novos, preservando cada referência de rótulo aninhado. Com uppercase: false, preview não atribui texto. Com uppercase: true, escreve no rótulo compartilhado. Na fixture fragile, a entrada do chamador vira FRAGILE. Se a mutação é aceitável depende do contrato de propriedade.

O ZIP inclui esse registro em claims.md, observador, ambos os testes e manifesto de hashes.

Leve as evidências de volta à leitura

Guarde a versão do código, a explicação verificada e a entrada e saída do teste. Depois pergunte: “Os dois labels compartilham um objeto, e converter para maiúsculas muda o original. Reescreva a explicação considerando isso”. Confira a nova resposta no mesmo código.

O AI Code Reader do Yenpo permite navegar pelo código local, fazer perguntas específicas e abrir referências enquanto segue chamadores. Execute o laboratório e suas verificações no Terminal ou nas ferramentas habituais; o Yenpo não executa comandos shell nem testes por você.

Se a próxima decisão for mudar o comportamento, estabeleça primeiro o requisito. Para um patch proposto, continue com revisão de código de IA usando contraexemplo. Para registrar comportamento desconhecido antes de refatorar, use testes de caracterização.