También en: English, Português, 日本語 · Todas las guías en español
Aviso: construimos Carbon Forge en 100monkeys.ai, así que lee esto como el relato de quien lo fabrica. Esta guía la redactó uno de nuestros agentes de IA a partir de la documentación pública, sobre todo Working in lanes y Code and files on the substrate, y la revisó la empresa; esta versión en español es una traducción de la guía original en inglés. Cada llamada es el ejemplo de la propia documentación, y las citas van en inglés con la traducción al lado. Donde la documentación deja una pregunta abierta, la última sección lo dice.
Por qué un solo changeset
La portada de la documentación define la unidad: "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" (un changeset son muchas escrituras integradas como una: cada página, átomo, árbol de código y entrada de biblioteca que nombra se escribe en una transacción bajo una intención, la del changeset, o no se escribe nada; Welcome to Carbon). El código y la prosa son entidades de un mismo registro, así que quien lee nunca encuentra el código nuevo junto a la página vieja, y una reversión deshace ambos a la vez.
Necesitas un token que pueda escribir lanes y árboles; el preset SCM agent es el que la documentación nombra para ese trabajo.
Paso 1. Averigua si el 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." (Con trunkProtected, los árboles de código, las entradas de biblioteca y los changesets pasan por una lane; si además está trunkProtectedEntities, también las ediciones de páginas y átomos.) En cualquier caso, una lane es la forma correcta aquí: la documentación recurre a una cuando "a change should be seen whole before it lands" (un cambio debe verse completo antes de integrarse).
Paso 2. Abre una lane para el cambio
lanes.create { "label": "agent/7/fix-login" } Una etiqueta son segmentos en minúsculas separados por barras, única entre las lanes abiertas. "A lane costs nothing to open and holds no copy, so one per task, or one per agent, is the expected use." (Abrir una lane no cuesta nada y no guarda ninguna copia, así que lo esperado es una por tarea o una por agente.) Pasa owner para que solo tú puedas escribir, resolver e integrar en la lane.
Paso 3. Cambia el código en la 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" } Pasa la etiqueta como lane y "the write is held on the lane as a patch against the trunk" (la escritura queda en la lane como un parche contra el trunk): "the trunk is not written" (el trunk no se escribe). trees.apply_patch envía solo sus operaciones (una sustitución dentro de un archivo, una escritura, un borrado o un movimiento); trees.put envía la lista completa de entradas del árbol. En ambos casos "the lane keeps only what differs" (la lane guarda solo lo que difiere).
Paso 4. Cambia la página que lo documenta, en la misma 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" } Misma etiqueta, así que la edición de la página espera junto al código. Si el cambio también renombra una página, pages.move acepta lane igualmente, y "a rename belongs to the change that needs it" (un cambio de nombre pertenece al cambio que lo necesita). Una captura de pantalla entra igual, con library.put_at_path y la etiqueta.
Paso 5. Lee lo que produciría la lane
Pasa la misma lane a una lectura y "the answer is the trunk as it stands now with the lane's patches applied, composed on the call" (la respuesta es el trunk tal como está ahora con los parches de la lane aplicados, compuesto en la llamada): pages.read para la página, trees.get, trees.diff o trees.grep para el código. Después revisa el conjunto:
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" (entidad por entidad, la sección del trunk y la de la lane); lanes.get responde los parches, los conflictos y landable: "open, at least one live patch, no conflict" (abierta, al menos un parche vivo, ningún conflicto). En la app, /<workspace>/lanes/<label> muestra lo mismo, con cada archivo de texto cambiado como un diff unificado. Quien revisa leyendo lanes.export y aprueba con su hash de composición "has approved exactly that change" (ha aprobado exactamente ese cambio).
Paso 6. Responde a cualquier conflicto
Si mientras tanto alguien editó en el trunk las mismas líneas de la página o del archivo, el parche está en conflicto, y "nothing merges by itself" (nada se fusiona por sí solo). lanes.conflicts da el parche, la sección como estaba, la sección actual del trunk y los changesets que la movieron. Responde con lanes.resolve o descarta el parche con lanes.drop_patch. Una sustitución dentro de un archivo "replays on the trunk's current file, so two agents changing different parts of one file both land" (se vuelve a aplicar sobre el archivo actual del trunk, así que dos agentes que cambian partes distintas de un archivo se integran los dos).
Paso 7. Integra una vez, con una intención
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" } Ejecuta primero la prueba en seco y después la integración. "Every live patch is replayed as one changeset whose intent is lane agent/7/fix-login: fix: accept magic links" (cada parche vivo se vuelve a aplicar como un changeset cuya intención es lane agent/7/fix-login: fix: accept magic links), se comprueba de nuevo dentro de su propia transacción y se "refused whole, with every conflict named, if anything moved" (rechaza entero, con cada conflicto nombrado, si algo se movió). Empieza la intención con feat, fix, docs, chore o revert y las notas de versión la agruparán bajo esa palabra. Envía una idempotency_key y un reintento tras un timeout "answers the first landing's result instead of landing twice" (devuelve el resultado de la primera integración en lugar de integrar dos veces).
Paso 8. Lee la integración de vuelta
Abre /<workspace>/changes/<id>: muestra la intención completa, el actor, la lane de la que se integró, su sello y sus entradas, el archivo de código y la página juntos. El blame de la línea cambiada responde ahora con la intención que escribiste: trees.blame da "each line's revision, changeset, intent, actor and landing lane" (la revisión, el changeset, la intención, el actor y la lane de integración de cada línea; Code and files). Para deshacer ambos a la vez, changesets.revert escribe un changeset nuevo que restaura cada entidad que escribió la integración.
Lo que la documentación deja sin responder
- Una vía de una sola llamada sin lane. La guía de lanes muestra
changesets.applycon"targets": [ … ]pero no detalla ahí la forma de un target, así que esta guía usa una lane. - Qué página documenta qué código. Nada de lo anterior crea ese vínculo; declararlo es para lo que sirven los governed seams (consulta la guía relacionada de abajo).