agent: make the OOM killer prefer a subagent over the agent's own turn
Both units ran at OOMScoreAdjust=0, so under container memory pressure the kernel picked purely on footprint — and the agent's own claude is often the fattest process in the container, which means the session supervising the work died before the work did. The sign is the load-bearing part and is easy to invert: a HIGHER OOMScoreAdjust means MORE likely to be killed, because the kernel adds it to the badness score it derives from the process's memory footprint and then kills the highest scorer. So hive-subagent-daemon gets +500 (first in line) and hive-agent gets -500 (last in line). Written backwards this makes the reported bug worse rather than better, so module-eval pins the order as an inequality. Both values are inherited by the nested claude each unit spawns as a child, so ordering the units orders the sessions underneath them. -500 rather than -1000 on the harness: fully exempting it would leave the kernel nothing to kill in a container whose only large process is the harness. Refs #4316
This commit is contained in:
parent
4daab7efe4
commit
d249468db2
3 changed files with 39 additions and 0 deletions
|
|
@ -303,6 +303,16 @@ in
|
|||
SyslogIdentifier = binary;
|
||||
Restart = "on-failure";
|
||||
RestartSec = 2;
|
||||
# A LOWER OOMScoreAdjust means LESS likely to be killed, so this
|
||||
# negative value puts the harness — and the agent's own `claude`,
|
||||
# which it spawns as a child and which therefore inherits the
|
||||
# value — last in line under memory pressure, behind
|
||||
# `hive-subagent-daemon`'s `+500` (see ./mcp.nix). Losing a
|
||||
# subagent costs one restartable task; losing this costs the
|
||||
# session that was supervising it. Not `-1000`, which would
|
||||
# exempt the harness entirely and leave the kernel with nothing
|
||||
# to kill in a container whose only large process is this one.
|
||||
OOMScoreAdjust = -500;
|
||||
# Per-service runtime dir owned by `User=` below; the harness
|
||||
# writes its regenerated claude-{mcp-config,settings,system-prompt}
|
||||
# files here (`paths::config_dir`). Separate from /run/hive,
|
||||
|
|
|
|||
|
|
@ -363,6 +363,18 @@ in
|
|||
# `start`/`continue`/`status`/`interrupt` with no stdio fallback.
|
||||
Restart = "always";
|
||||
RestartSec = 3;
|
||||
# A HIGHER OOMScoreAdjust means MORE likely to be killed: the
|
||||
# kernel adds it to the badness score it derives from the
|
||||
# process's memory footprint, then kills the highest scorer. So
|
||||
# the positive value here and the negative one on `hive-agent`
|
||||
# put this unit first in line — a subagent turn can be retried,
|
||||
# the agent's own turn is the thing everything else hangs off.
|
||||
# `+500` against `hive-agent`'s `-500` is a full half of the
|
||||
# score range apart, enough that a fat subagent outranks a fatter
|
||||
# harness rather than merely tying with it. Every nested `claude`
|
||||
# inherits the value from the daemon, so the ordering covers the
|
||||
# whole subtree and not just this process.
|
||||
OOMScoreAdjust = 500;
|
||||
User = userName;
|
||||
Group = userName;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1385,6 +1385,23 @@ let
|
|||
name = "an agent with no byte-valued memory cap renders no subagent ceiling";
|
||||
ok = !((agentSubagentDaemon agentUncapped).serviceConfig ? MemoryHigh);
|
||||
}
|
||||
{
|
||||
# The sign is the whole property, and it is easy to write backwards:
|
||||
# a HIGHER OOMScoreAdjust is a MORE likely victim. So the subagent
|
||||
# daemon must be strictly above zero and the harness strictly below
|
||||
# it — swap the two and the kernel takes the agent's own turn first,
|
||||
# which is worse than setting nothing at all. Both nested `claude`
|
||||
# processes inherit their unit's value, so ordering the units orders
|
||||
# the sessions. Held as an inequality rather than two constants: what
|
||||
# must not drift is the order, not the magnitudes.
|
||||
name = "the OOM killer prefers a subagent over the agent's own session";
|
||||
ok =
|
||||
let
|
||||
sub = (agentSubagentDaemon agentUncapped).serviceConfig.OOMScoreAdjust;
|
||||
own = (agentHarness agentUncapped).serviceConfig.OOMScoreAdjust;
|
||||
in
|
||||
sub > 0 && own < 0 && sub > own;
|
||||
}
|
||||
{
|
||||
# The doctrine three glue files state, as a property a rewrite has to
|
||||
# keep: a client is defined by holding a certificate the store accepts,
|
||||
|
|
|
|||
Loading…
Reference in a new issue