Synsemadocsv0.6.xENES

Agents & coordination

Observability

observability.syn
-- 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 takes a full expression (it's also an expression). Under serve, log/print reach the terminal with a [serve] prefix.

log "Processing order " + order_id
log is a statement, not a function. log "msg" works; log("msg") parses with check but crashes at runtime.

Logs under serve (request logging)§

serve does not write an access log for you — by default the terminal is quiet, which makes development hard. Add a log in your handlers to see traffic live:

route "GET /:lang/:version/:slug"
    log "GET " + (path of request)
    give render_page(...)

Each request then prints in the server's terminal as [serve] [LOG] GET /…. print works there too (same [serve] prefix). (This docs site logs its own requests this way.)

Markers — trace / measure / checkpoint (decorative)§

These run their body but the timing/snapshot instrumentation is currently a stub — they do not persist anything. trace/measure take a literal name; checkpoint takes an expression.

measure "db_query"
    run_query(sql)

For real crash-resume / step tracking, use the progress builtins, not checkpoint — see Memory & state.

Capability audit (--audit json)§

Every capability check the runtime makes — granted or denied — can be streamed as JSON, one line per check, on run/test/conform/serve (engine v0.6.14+):

synsema run --audit json        program.syn   # to stderr
synsema run --audit ./audit.jsonl program.syn # to a file
synsema run --audit fd:3        program.syn   # to a file descriptor (Unix only)

Each line is {ts, context, capability, granted, source, reason, origin, file, line}:

A final line summarizes: {"summary": {"granted": N, "denied": M, "exit": code}}. Secret values never appear — capability scopes are names (secret("STRIPE_KEY")), never the value. This is the same audit the WebAssembly run() returns as r.audit (fields on the WASM page); the native stream just adds ts/context/file/line. run_program returns its child's audit as r["audit"] — a value, not a log to parse.

Error diagnostics (opt-in)§

Plain run prints a stable one-liner (Runtime error: file:line:col: msg). For a rich report — source context, call stack, visible variables, classification, suggestions — opt in:

synsema run --explain program.syn                # human-readable, on stderr
synsema run --explain --format json program.syn  # structured, for tools/agents

The exit code is unchanged (1 on failure, 0 on success) either way.