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 dadefaultBranch. - Una lane con
workspace.worktreeha un branch e un worktree propri,afe/<ticket>/<scope>, tagliati dal branch di integrazione.scopeè il valoreworktreedella 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.readews.searchsono argv di tool POSIX standard (find -P,cat,grep) eseguiti tramite l'exec della sandbox, ews.writepassa daput. Il motore controlla comunque ogni percorso (relativo, niente.., niente symlink) prima di eseguirlo, quindi unProject.sandbox.imagearbitrario ha bisogno solo di quei tool.ws.editlegge il file, sostituisce il testo una volta e lo riscrive.ws.exec,ws.test, un nodoscripte un agenteacpgirano nella stessa sandbox. - Anche git gira dentro, irrobustito. Clone, worktree, commit e merge passano da
SandboxGitsenza hook, senzacore.fsmonitore senza configurazione di sistema o globale. Il clone arriva comegit bundleche 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. Ungit bundleincrementale diafe/*erefs/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
.gitdel suo ticket, quindi ilgitdel 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).