okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
331aa22
main
Shared runtime helpers
clocks
albedo/clock.gleam owns Gleam clock access. Use system_ms or
system_seconds for durable timestamps and expiry, monotonic_ms for elapsed
time and deadlines, and os_system_ms only when OS-clock semantics are required.
Store slow-query timing uses clock.monotonic_ms. Reuse these functions instead
of adding local clock externals or wrappers. Erlang native time units are
VM-dependent; request an explicit unit rather than dividing native ticks.
Unicode scalar limits
albedo/text_scalars.gleam owns scalar counting and slicing. Its length,
take, drop, and tail scan UTF-8 without creating a whole codepoint list;
retained slices are copied so they release the source binary. Scalars are not
graphemes. Use these helpers for bounded previews and names, including from
Erlang through 'albedo@text_scalars':take(Text, Limit). The kernel job summary
retains at most 4096 scalars; truncating it must not allocate a list for the
entire command.
HTTP response previews and operation validation also use text_scalars.take;
their scalar-prefix adapters do not maintain a separate native implementation.
shared values
albedo/shared.gleam owns values every process reads without copying.
publish stores one in a persistent term and answers a Shared(a) handle;
read returns the literal, which a spawn or send does not copy (a sublist of
it copies cons cells only). Publishing and release each scan every process:
publish once per lifetime, release in the owner's stop. Reading a released
handle panics. runtime.Runtime holds its installed extensions this way, so
a runtime copied into an HTTP request or a turn's worker costs a few words;
test/harness/runtime_test.gleam fails when the handle or a selection over
the installed list carries a copy. A literal references module code, so the
daemon does not hot-load a module while a shared value from it exists.
native boundaries
Keep process state, ports, monitors, ETF, and native binary matching in their Erlang owners. Reuse Gleam owners for pure validation and text projection. A new Erlang shim should supply a native capability, not duplicate an existing Gleam helper. Keep typed values through internal projections and encode JSON at the wire boundary; see command state projections.
daemon/active_output.gleam owns unfinished response identities, limits, and
projection transitions. Its native helper owns buffered spill file I/O, range
reads, leases, and filesystem cleanup. Capture flushes by absolute offset so
capturing the same immutable projection cannot append its buffered tail twice.
Do not move projection policy into an opaque Erlang map or reconstruct output
from replay events. The session attachment contract defines
cursor consistency, commit replacement, and reference lifetimes.
session diagnostics
daemon/session_diagnostics.gleam owns named facts from session_state.State
for native probes: watcher owners, sequence, history residency, tool progress,
and the current run's worker. Erlang probes must call these typed accessors
instead of indexing the state tuple or searching its fields by constructor tag.
The memory inspector reports numbered fields and sizes; it must not mirror
Gleam record names or create atoms for field labels.
finite actor calls
albedo/actor_call.gleam owns monitored calls that return TimedOut or
CalleeDown and remove their monitor on every outcome. Reuse try_call for
finite waits instead of copying subject lookup, reply selection, and monitor
cleanup. Callers own deadlines and translate failures into their domain errors;
a timeout does not cancel accepted work.
Kernel upgrade completion keeps its repeated wait until completion or owner death. Session closure also waits for owner termination after the acknowledgment. These lifetime protocols need more than a finite request/reply call.
registry host lookup
daemon/registry.host owns the five-second runtime-host lookup. Callers retain
Result(Runtime, String) and map failures at the HTTP boundary. Reuse this
function rather than sending Host with a local copy of its timeout. Page
contributors construct page.Row directly; forwarding constructors add no policy.
closure registry tables
daemon/albedo_registry.erl creates closure-registry ETS tables lazily. A
registering caller monitors the creator and waits for a tagged readiness
acknowledgment or explicit startup failure before inserting. The winning owner
outlives the callers; concurrent losing creators acknowledge the existing
table and exit. Keep this lifetime independent of daemon boot so embedded
runtimes work too. Never replace readiness with a sleep or bounded polling.
1# Shared runtime helpers 2 3## clocks 4 5`albedo/clock.gleam` owns Gleam clock access. Use `system_ms` or 6`system_seconds` for durable timestamps and expiry, `monotonic_ms` for elapsed 7time and deadlines, and `os_system_ms` only when OS-clock semantics are required. 8Store slow-query timing uses `clock.monotonic_ms`. Reuse these functions instead 9of adding local clock externals or wrappers. Erlang native time units are 10VM-dependent; request an explicit unit rather than dividing native ticks. 11 12## Unicode scalar limits 13 14`albedo/text_scalars.gleam` owns scalar counting and slicing. Its `length`, 15`take`, `drop`, and `tail` scan UTF-8 without creating a whole codepoint list; 16retained slices are copied so they release the source binary. Scalars are not 17graphemes. Use these helpers for bounded previews and names, including from 18Erlang through `'albedo@text_scalars':take(Text, Limit)`. The kernel job summary 19retains at most 4096 scalars; truncating it must not allocate a list for the 20entire command. 21HTTP response previews and operation validation also use `text_scalars.take`; 22their scalar-prefix adapters do not maintain a separate native implementation. 23 24## shared values 25 26`albedo/shared.gleam` owns values every process reads without copying. 27`publish` stores one in a persistent term and answers a `Shared(a)` handle; 28`read` returns the literal, which a spawn or send does not copy (a sublist of 29it copies cons cells only). Publishing and `release` each scan every process: 30publish once per lifetime, release in the owner's `stop`. Reading a released 31handle panics. `runtime.Runtime` holds its installed extensions this way, so 32a runtime copied into an HTTP request or a turn's worker costs a few words; 33`test/harness/runtime_test.gleam` fails when the handle or a selection over 34the installed list carries a copy. A literal references module code, so the 35daemon does not hot-load a module while a shared value from it exists. 36 37## native boundaries 38 39Keep process state, ports, monitors, ETF, and native binary matching in their 40Erlang owners. Reuse Gleam owners for pure validation and text projection. 41A new Erlang shim should supply a native capability, not duplicate an existing 42Gleam helper. Keep typed values through internal projections and encode JSON at 43the wire boundary; see [command state projections](commands.md#contributing-commands). 44 45`daemon/active_output.gleam` owns unfinished response identities, limits, and 46projection transitions. Its native helper owns buffered spill file I/O, range 47reads, leases, and filesystem cleanup. Capture flushes by absolute offset so 48capturing the same immutable projection cannot append its buffered tail twice. 49Do not move projection policy into an opaque Erlang map or reconstruct output 50from replay events. The [session attachment contract](sessions.md) defines 51cursor consistency, commit replacement, and reference lifetimes. 52 53## session diagnostics 54 55`daemon/session_diagnostics.gleam` owns named facts from `session_state.State` 56for native probes: watcher owners, sequence, history residency, tool progress, 57and the current run's worker. Erlang probes must call these typed accessors 58instead of indexing the state tuple or searching its fields by constructor tag. 59The memory inspector reports numbered fields and sizes; it must not mirror 60Gleam record names or create atoms for field labels. 61 62## finite actor calls 63 64`albedo/actor_call.gleam` owns monitored calls that return `TimedOut` or 65`CalleeDown` and remove their monitor on every outcome. Reuse `try_call` for 66finite waits instead of copying subject lookup, reply selection, and monitor 67cleanup. Callers own deadlines and translate failures into their domain errors; 68a timeout does not cancel accepted work. 69 70Kernel upgrade completion keeps its repeated wait until completion or owner 71death. Session closure also waits for owner termination after the acknowledgment. 72These lifetime protocols need more than a finite request/reply call. 73 74## registry host lookup 75 76`daemon/registry.host` owns the five-second runtime-host lookup. Callers retain 77`Result(Runtime, String)` and map failures at the HTTP boundary. Reuse this 78function rather than sending `Host` with a local copy of its timeout. Page 79contributors construct `page.Row` directly; forwarding constructors add no policy. 80 81## closure registry tables 82 83`daemon/albedo_registry.erl` creates closure-registry ETS tables lazily. A 84registering caller monitors the creator and waits for a tagged readiness 85acknowledgment or explicit startup failure before inserting. The winning owner 86outlives the callers; concurrent losing creators acknowledge the existing 87table and exit. Keep this lifetime independent of daemon boot so embedded 88runtimes work too. Never replace readiness with a sleep or bounded polling.