Impact, without, and time
Three questions an agent asks before a change: what else does this touch, what if this were gone, and what holds on a given day.
# fleet > environment > host. The fleet says which day it is: pure evaluation has no clock.{ yen, lib, ... }:let inherit (yen.lib) same maybe;in{ yen.kinds.fleet = { fields.today = lib.types.str; spawns.environment = _: yen.environment; sinks.out.reduce = yen.lib.reduce.merge; sinks.dns.out = { dns, ... }: { inherit dns; }; }; yen.kinds.environment = { spawns.host = s: lib.filterAttrs (_: h: same h.environment s && yen.lib.validAt yen.fleet.main.today h) yen.host; sinks.backends.dns = { environment, backends, ... }: { ${environment.name} = backends; }; }; yen.kinds.host.fields = { environment = yen.kinds.environment; validFrom = maybe lib.types.str; # null: open, valid since always validTo = maybe lib.types.str; # null: open, valid until always };
yen.fleet.main.today = "2026-11-15"; yen.environment.prod = { }; yen.host.web-old = { environment = yen.environment.prod; validTo = "2026-10-31"; }; # retired yen.host.web = { environment = yen.environment.prod; validFrom = "2026-10-01"; }; yen.host.web-next = { environment = yen.environment.prod; validFrom = "2026-12-01"; }; # not yet yen.flake = { out, ... }: out;}{ yen, ... }:{ yen.aspects.backend = { at = yen.kinds.host; backends = { host, ... }: host.name; };}What does this scope reach?
Section titled “What does this scope reach?”r.impact id { hops; sinks; max; } follows routed elements from a scope, by depth. web gives a backend
to its environment, and the environment gives DNS to the fleet:
{ hops = 3; reached = [ { depth = 1; scope = "fleet:main/environment:prod"; via = "backends"; } { depth = 2; scope = "fleet:main"; via = "dns"; } ]; truncated = { of = 2; shown = 2; };}What if it were gone?
Section titled “What if it were gone?”yen.lib.without { files; path; args; } evaluates the fleet without the files that declare path.
A whole file goes: there is no hook to remove part of one.
{ dns = null; dropped = [ "backend.nix" ]; errors = [ ];}Valid time
Section titled “Valid time”Pure evaluation has no clock, so the date is data. An instance with validFrom and validTo holds at the date
the caller passes: yen.lib.validAt date scope. Here the environment spawns only the hosts valid today,
so web-old and web-next have no scope:
[ "fleet:main" "fleet:main/environment:prod" "fleet:main/environment:prod/host:web"]Transaction time
Section titled “Transaction time”yen.lib.asOf { url; rev; module; } evaluates the fleet at a git revision. yen.lib.history { url; revs; module; }
lists each revision with the field changes from the one before (diffData over r.data). The engine’s own tests
run both on this repository: see tests/default.nix.