Também em: English, Español, 日本語 · Todos os guias em português
Aviso: desenvolvemos o Carbon Forge na 100monkeys.ai, então leia isto como o relato de quem o fabrica. Este guia foi redigido por um dos nossos agentes de IA a partir da documentação pública, principalmente Working in lanes e Code and files on the substrate, e revisado pela empresa; esta versão em português é uma tradução do guia original em inglês. Cada chamada é o exemplo da própria documentação, e as citações ficam em inglês com a tradução ao lado. Onde a documentação deixa uma pergunta em aberto, a última seção diz isso.
Por que um único changeset
A página inicial da documentação define a unidade: "A changeset is many writes landed as one: every page, atom, code tree and library entry it names is written in one transaction under one intent, the changeset's intent, or nothing is written" (um changeset são muitas escritas integradas como uma: cada página, átomo, árvore de código e entrada de biblioteca que ele nomeia é escrita numa transação sob uma intenção, a do changeset, ou nada é escrito; Welcome to Carbon). Código e prosa são entidades de um mesmo registro, então quem lê nunca encontra o código novo ao lado da página velha, e uma reversão desfaz os dois juntos.
Você precisa de um token que possa escrever lanes e árvores; o preset SCM agent é o que a documentação indica para esse trabalho.
Passo 1. Descubra se o trunk está protegido
workspaces.get_settings {}
→ { "workspaceId": "…", "trunkProtected": true, "trunkProtectedEntities": false } "With trunkProtected, code trees, library entries and changesets go through a lane; with trunkProtectedEntities as well, page and atom edits do too." (Com trunkProtected, árvores de código, entradas de biblioteca e changesets passam por uma lane; com trunkProtectedEntities também, as edições de páginas e átomos passam igualmente.) De todo modo, uma lane é a forma certa aqui: a documentação recorre a uma quando "a change should be seen whole before it lands" (uma mudança deve ser vista inteira antes de ser integrada).
Passo 2. Abra uma lane para a mudança
lanes.create { "label": "agent/7/fix-login" } Um rótulo são segmentos em minúsculas separados por barras, único entre as lanes abertas. "A lane costs nothing to open and holds no copy, so one per task, or one per agent, is the expected use." (Abrir uma lane não custa nada e ela não guarda cópia, então o esperado é uma por tarefa ou uma por agente.) Passe owner para que só você possa escrever, resolver e integrar na lane.
Passo 3. Mude o código na lane
trees.apply_patch { "path": "app", "operations": [ { "op": "patch", "entry_path": "src/login.ts", "old_string": "password", "new_string": "magicLink" } ], "intent": "fix: accept magic links", "lane": "agent/7/fix-login" } Passe o rótulo como lane e "the write is held on the lane as a patch against the trunk" (a escrita fica retida na lane como um patch contra o trunk): "the trunk is not written" (o trunk não é escrito). trees.apply_patch envia só as suas operações (uma substituição dentro de um arquivo, uma escrita, uma remoção ou uma movimentação); trees.put envia a lista inteira de entradas da árvore. De qualquer forma, "the lane keeps only what differs" (a lane guarda só o que difere).
Passo 4. Mude a página que o documenta, na mesma lane
pages.apply_patch { "pathOrId": "guides/login", "old_string": "Sign in with a password.", "new_string": "Sign in with a magic link.", "lane": "agent/7/fix-login" } Mesmo rótulo, então a edição da página espera ao lado do código. Se a mudança também renomeia uma página, pages.move aceita lane igualmente, e "a rename belongs to the change that needs it" (uma renomeação pertence à mudança que precisa dela). Uma captura de tela entra do mesmo jeito, com library.put_at_path e o rótulo.
Passo 5. Leia o que a lane produziria
Passe a mesma lane a uma leitura e "the answer is the trunk as it stands now with the lane's patches applied, composed on the call" (a resposta é o trunk como está agora com os patches da lane aplicados, composto na chamada): pages.read para a página, trees.get, trees.diff ou trees.grep para o código. Depois revise o todo:
lanes.diff { "label": "agent/7/fix-login" }
lanes.get { "label": "agent/7/fix-login" } lanes.diff responde "entity by entity, the trunk's section and the lane's" (entidade por entidade, a seção do trunk e a da lane); lanes.get responde os patches, os conflitos e landable: "open, at least one live patch, no conflict" (aberta, pelo menos um patch vivo, nenhum conflito). No app, /<workspace>/lanes/<label> mostra o mesmo, com cada arquivo de texto alterado como um diff unificado. Quem revisa lendo lanes.export e aprova no hash de composição dela "has approved exactly that change" (aprovou exatamente essa mudança).
Passo 6. Responda a qualquer conflito
Se enquanto isso alguém editou no trunk as mesmas linhas da página ou do arquivo, o patch está em conflito, e "nothing merges by itself" (nada se mescla sozinho). lanes.conflicts dá o patch, a seção como era, a seção atual do trunk e os changesets que a moveram. Responda com lanes.resolve ou descarte o patch com lanes.drop_patch. Uma substituição dentro de um arquivo "replays on the trunk's current file, so two agents changing different parts of one file both land" (é reaplicada sobre o arquivo atual do trunk, então dois agentes mudando partes diferentes de um arquivo são integrados os dois).
Passo 7. Integre uma vez, com uma intenção
lanes.land { "label": "agent/7/fix-login", "intent": "fix: accept magic links", "dry_run": true }
lanes.land { "label": "agent/7/fix-login", "intent": "fix: accept magic links" } Rode primeiro o dry run e depois a integração. "Every live patch is replayed as one changeset whose intent is lane agent/7/fix-login: fix: accept magic links" (cada patch vivo é reaplicado como um changeset cuja intenção é lane agent/7/fix-login: fix: accept magic links), verificado de novo dentro da sua própria transação e "refused whole, with every conflict named, if anything moved" (recusado por inteiro, com cada conflito nomeado, se algo se moveu). Comece a intenção com feat, fix, docs, chore ou revert e as notas de versão a agrupam sob essa palavra. Envie uma idempotency_key e uma nova tentativa após um timeout "answers the first landing's result instead of landing twice" (devolve o resultado da primeira integração em vez de integrar duas vezes).
Passo 8. Leia a integração de volta
Abra /<workspace>/changes/<id>: mostra a intenção inteira, o ator, a lane de onde veio, o selo e as entradas, o arquivo de código e a página juntos. O blame da linha alterada agora responde com a intenção que você escreveu: trees.blame dá "each line's revision, changeset, intent, actor and landing lane" (a revisão, o changeset, a intenção, o ator e a lane de integração de cada linha; Code and files). Para desfazer os dois de uma vez, changesets.revert escreve um changeset novo que restaura cada entidade que a integração escreveu.
O que a documentação deixa sem resposta
- Um caminho de uma só chamada sem lane. O guia de lanes mostra
changesets.applycom"targets": [ … ]mas não detalha ali a forma de um target, então este guia usa uma lane. - Qual página documenta qual código. Nada acima cria esse vínculo; declará-lo é para o que servem os governed seams (veja o guia relacionado abaixo).