Agentes y coordinación
Observabilidad
-- Doc example: observability. `log` is real; `trace`/`measure`/`checkpoint` are
-- decorative markers — they RUN their body but don't persist timing/snapshots.
intent: "doc example: observability"
let answer be 0
measure "demo"
set answer to 41 + 1
print("measure ran → answer = " + text(answer))
test "measure runs its body (the timing instrumentation is decorative)"
let x be 0
measure "compute"
set x to 41 + 1
assert_eq(x, 42)Logging (real)§
log toma una expresión completa (también es una expresión). Bajo serve, log/print llegan a la terminal con prefijo [serve].
log "Procesando orden " + order_id
loges una sentencia, no una función.log "msg"funciona;log("msg")parsea concheckpero revienta en runtime.
Logs bajo serve (logging de requests)§
serve no escribe un access log por vos — por defecto la terminal está callada, lo que dificulta el desarrollo. Agregá un log en tus handlers para ver el tráfico en vivo:
route "GET /:lang/:version/:slug"
log "GET " + (path of request)
give render_page(...)
Cada request entonces aparece en la terminal del server como [serve] [LOG] GET /…. print también funciona ahí (mismo prefijo [serve]). (Este sitio de docs loguea sus propios requests así.)
Marcadores — trace / measure / checkpoint (decorativos)§
Estos corren su cuerpo pero la instrumentación de timing/snapshot hoy es un stub — no persisten nada. trace/measure toman un nombre literal; checkpoint toma una expresión.
measure "db_query"
run_query(sql)
Para crash-resume / tracking de pasos real, usá los builtins de progress, no checkpoint — ver Memoria y estado.
Audit de capabilities (--audit json)§
Cada chequeo de capability que hace el runtime — concedido o denegado — se puede streamear como JSON, una línea por chequeo, en run/test/conform/serve (engine v0.6.14+):
synsema run --audit json program.syn # a stderr
synsema run --audit ./audit.jsonl program.syn # a un archivo
synsema run --audit fd:3 program.syn # a un file descriptor (solo Unix)
Cada línea es {ts, context, capability, granted, source, reason, origin, file, line}:
context— qué CapabilitySet (program,agent,request,worker, un framesandbox:/tool:).origin—"program"(unrequireo una llamada que hizo el código) o"runtime"(un grant ambiente:stdout/time/llm, oservedesde--port).reason—Granted by <grant>,No matching grant found,above host ceiling (--sandbox/--cap-set),Explicitly denied by …,auto-granted by the runtime(un grant ambiente que tuvo éxito ahora deja rastro), obundled asset (part of the program)(una lectura desde un bundle desynsema build).source— el builtin que disparó el chequeo (read_file(),run(), …), oceiling/ambient/secret-builtin.file/line— dónde en el programa (onull).
Una línea final resume: {"summary": {"granted": N, "denied": M, "exit": code}}. Los valores de secretos nunca aparecen — los scopes de capability son nombres (secret("STRIPE_KEY")), nunca el valor. Este es el mismo audit que el run() de WebAssembly devuelve como r.audit (campos en la página WASM); el stream nativo solo agrega ts/context/file/line. run_program devuelve el audit de su hijo como r["audit"] — un valor, no un log para parsear.
Diagnósticos de error (opt-in)§
El run plano imprime una línea estable (Runtime error: file:line:col: msg). Para un reporte rico — contexto de fuente, call stack, variables visibles, clasificación, sugerencias — optá por:
synsema run --explain program.syn # legible, en stderr
synsema run --explain --format json program.syn # estructurado, para tools/agentes
El exit code no cambia (1 al fallar, 0 al tener éxito) en cualquier caso.