Passa al contenuto principale

Workspace

Come un progetto folder o git dà i file a un agente, e cosa lo rende sicuro in una sandbox.

Il workspace folder​

kind: Project
metadata: {name: notes}
spec:
workspace:
type: folder
path: ./notes

Un agente con workspaceAccess legge la cartella tramite ws.tree, ws.read e ws.search. Quando è deep con workspaceAccess: write, la scrive anche tramite ws.write, ws.edit e ws.delete.

Ogni percorso è confinato. .. e i percorsi assoluti vengono rifiutati, e ogni componente del percorso viene aperto dalla radice senza seguire i symlink (O_NOFOLLOW). Un symlink in qualsiasi punto del percorso viene rifiutato, anche se punta dentro la radice, o se è creato nella sandbox mentre la chiamata gira.

Il workspace git​

kind: Project
metadata: {name: app}
spec:
workspace:
type: git
url: https://github.com/acme/app # oppure `path: /srv/app`
defaultBranch: main
tokenEnv: GITHUB_TOKEN # un repository privato
sandbox: {image: afe-sandbox:test, cpus: 2, memory: 2g, timeout: 300}
ci:
script: python3 -m pytest -q
test: python3 -m pytest -q # il comando dietro `ws.test`; default: `script`
  • Il progetto si clona una volta in <workDir>/repos/<project>, e si aggiorna prima di ogni ticket.
  • Un ticket lavora in un worktree di integrazione su afe/<ticket>/integration, tagliato da defaultBranch.
  • Una lane con workspace.worktree ha un branch e un worktree propri, afe/<ticket>/<scope>, tagliati dal branch di integrazione. scope è il valore worktree della lane, con i parametri della lane già sostituiti.
  • Riaprendo dopo un riavvio si ritrovano i worktree esistenti invece di ricrearli. Sul percorso host i worktree restano dopo la fine del ticket. Un comando di pulizia è nel backlog.

Quando il Runtime ha un plugin sandbox, il workspace di un progetto git vive dentro la sandbox del ticket. È una sandbox per ticket (un Pod, o un container) con un volume proprio, con il repository in /workspace/repo e i worktree in /workspace/worktrees/<scope>.

Il percorso sull'host qui sopra resta solo per un runtime senza plugin sandbox. afe run --local prende lo stesso percorso, così esecuzioni locali e su cluster condividono un solo percorso di codice, e l'immagine della sandbox deve contenere git (docker/sandbox lo fa). Su Kubernetes, un progetto folder viene rifiutato, perché il worker non ha storage condiviso.

  • I file tool girano dentro. ws.tree, ws.read e ws.search sono argv di tool POSIX standard (find -P, cat, grep) eseguiti tramite l'exec della sandbox, e ws.write passa da put. Il motore controlla comunque ogni percorso (relativo, niente .., niente symlink) prima di eseguirlo, quindi un Project.sandbox.image arbitrario ha bisogno solo di quei tool. ws.edit legge il file, sostituisce il testo una volta e lo riscrive. ws.exec, ws.test, un nodo script e un agente acp girano nella stessa sandbox.
  • Anche git gira dentro, irrobustito. Clone, worktree, commit e merge passano da SandboxGit senza hook, senza core.fsmonitor e senza configurazione di sistema o globale. Il clone arriva come git bundle che il worker taglia con il token, così nessun token entra mai nella sandbox.
  • Il workspace è durevole. Dopo ogni nodo che ha usato la sandbox, il motore ne fa il checkpoint. Un worktree sporco diventa un commit WIP su un ref separato (refs/afe-wip/<ticket>/<scope>), che lascia intatti i branch e l'index del ticket. Un git bundle incrementale di afe/* e refs/afe-wip/* viene salvato nello store, per ticket e step. Un checkpoint di un workspace invariato non salva nulla, e i checkpoint di un ticket girano uno alla volta.
  • Una sandbox persa viene ricreata, e il nodo interrotto riparte. Una sandbox che fallisce un exec perché non esiste più, o che non diventa pronta, viene rimossa con il suo volume, e il job torna in coda. Il worker successivo ripristina repository, branch e worktree dai bundle salvati, e riesegue il nodo interrotto dall'inizio (il suo turn token viene scartato). Un nodo completato non viene mai ripetuto, ma le chiamate pagate del nodo interrotto possono esserlo. I file ignorati da git (.venv, node_modules, output di build) non sono nei bundle e non vengono ripristinati: un flow deve poterli ricostruire.
  • Un volume per ticket è un cambio di fiducia. La shell dell'agente può raggiungere ogni worktree e la cartella .git del suo ticket, quindi il git del motore gira irrobustito come sopra. La pull request e la sua approvazione restano la barriera prima che qualcosa raggiunga il branch predefinito.
  • Ciclo di vita. Aprire la sandbox di un nodo è idempotente: lo stesso ticket e lo stesso scope riusano la stessa sandbox, e un cambio di firma (immagine, limiti, nomi delle env, egress) la ricrea. La sandbox viene rimossa quando il ticket finisce (done, failed, cancelled). Un ticket in pausa la tiene mentre aspetta (portare a zero il Pod di un ticket in pausa è backlog, V7c).

git.log e git.diff leggono il branch su cui lavora un nodo. Sono in sola lettura.

La cartella di lavoro​

Sul percorso host, i cloni e i worktree stanno sotto Runtime.spec.workDir (default ~/.afe-work). Per un progetto git con sandbox, il repository del ticket vive dentro la sandbox, e il worker tiene solo cloni e bundle temporanei sotto workDir per la durata di una chiamata.

Ogni worker usa la stessa cartella: la stessa macchina, o un volume condiviso. afe worker e afe run --local controllano all'avvio che esista o si possa creare, e che sia scrivibile. Altrimenti rifiutano di partire.

kind: Runtime
metadata: {name: prod}
spec:
store: {type: postgres, dsnEnv: AFE_POSTGRES_DSN}
workDir: /srv/afe-work
sandbox: {type: docker}

I file scritti​

Ogni file scritto da un agente è tracciato per ticket, round, nodo, attivazione, scope e percorso. Due rami paralleli dello stesso round che scrivono lo stesso percorso nello stesso scope sono un conflitto: il ticket va in needs_human. Lo stesso percorso in due worktree git sono due file diversi. Un nodo human con attachFiles: true elenca i file scritti finora nel ticket, nella sua richiesta.

Risoluzione dei problemi​

Un ticket va in needs_human per un conflitto di scrittura. Due rami paralleli (due lane, o una lane e il flow principale) hanno scritto lo stesso percorso nello stesso round e scope. Instrada il flow perché i percorsi non si sovrappongano, oppure fondi i rami prima che scrivano entrambi, poi rispondi alla richiesta in sospeso.

Un progetto folder viene rifiutato su Kubernetes Il worker non ha storage condiviso su Kubernetes, quindi non può servire una cartella semplice a ogni nodo. Usa invece un workspace git, anche per un repository senza remoto (path: /srv/app funziona senza un URL).

Vedi anche​