okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

Aly Raffaufrefactor(text): use shared scalar truncation331aa22

main
4.7 KiB88 linesraw

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.