From b869ba9e2d67f5049a739588582e8181aafb5ab7 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 18:54:32 +0700 Subject: [PATCH] t27os: swarm_pulse -- the hourly watch decides one action (clean disk, request keys, start a free-tier lane) from the pulse numbers (Closes #11348) Co-Authored-By: Claude Opus 5.5 --- .trinity/seals/t27os_T27osSwarmPulse.json | 28 ++++++ specs/t27os/swarm_pulse.t27 | 112 ++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 .trinity/seals/t27os_T27osSwarmPulse.json create mode 100644 specs/t27os/swarm_pulse.t27 diff --git a/.trinity/seals/t27os_T27osSwarmPulse.json b/.trinity/seals/t27os_T27osSwarmPulse.json new file mode 100644 index 0000000000..0df9ad1554 --- /dev/null +++ b/.trinity/seals/t27os_T27osSwarmPulse.json @@ -0,0 +1,28 @@ +{ + "built_by": "t27c-bootstrap@0.5.2+24a3abcad", + "closure": [], + "config": "gen=c,rust,verilog,zig;tests=zig", + "gen_hash_c": "sha256:1c8c7a0ab694e36ce7b45f4f5d39507a7c0e05d3ecb3173ee22c4a5f306223f1", + "gen_hash_rust": "sha256:db89d95d7cac5981f484d96a3e22c5ec8bf7cb6c48f71777887b40a84a32da1b", + "gen_hash_verilog": "sha256:e67c25f6f31f6a7f5de455246134f0b909fedcf04c1a81afc59e96e7bdb42ce3", + "gen_hash_zig": "sha256:5a341440e343e700df7522a34be89cdd06b4963c5a83c238f114a418442f7cd9", + "module": "T27osSwarmPulse", + "ring": 12, + "seal_schema": 2, + "sealed_at": "2026-10-20T11:54:31Z", + "sealed_by": "t27c-bootstrap@0.5.2", + "spec_hash": "sha256:57e4bf04a42c6a85e0326a28d377b37837f2c9dac8cd988c792655aae12044ad", + "spec_path": "specs/t27os/swarm_pulse.t27", + "tests": { + "failed": 0, + "failing": [], + "forced": false, + "passed": 6, + "total": 6, + "vacuous": 0 + }, + "toolchain": { + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", + "zig": "0.16.0" + } +} \ No newline at end of file diff --git a/specs/t27os/swarm_pulse.t27 b/specs/t27os/swarm_pulse.t27 new file mode 100644 index 0000000000..88ffb91d95 --- /dev/null +++ b/specs/t27os/swarm_pulse.t27 @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/t27os/swarm_pulse.t27 -- the hourly watch of the swarm: from the numbers the pulse collects (bee jobs running +; and queued, swarm issues waiting, agent lanes on this machine, free-tier keys, free disk) it decides ONE action. +; Owner, 2026-10-20: "switch on a cron that watches that everyone works at maximum efficiency", "show bee load and +; token stock online so there is no idle time", "all logic is written in .t27". The collector that reads the numbers +; is plumbing (gh, security without -w, df); every threshold and every decision is here, tested. +; Refs epic #11275; issue #11331 family (swarm work under the T27 OS epic). ASCII only (L3), English only (LANG-EN). +; -- +; THE ORDER of the rules is the order of harm: no disk stops every build (clean first); no keys stops every lane +; (request keys); idle capacity with work waiting wastes time (start one free-tier lane); otherwise nothing. +; A tick takes at most ONE action, so a watch never floods the machine or the owner. +; phi^2 + 1/phi^2 = 3 | TRINITY +module T27osSwarmPulse; + +/// Below this many free gigabytes no cargo, t27b app or nextpnr build starts (peer rule, 2026-10-20). +pub const MIN_DISK_GB : u32 = 8; +/// Fewer usable z.ai free keys than this, or no OpenRouter key, is a key shortage. +pub const MIN_ZAI_KEYS : u32 = 3; +pub const MIN_OPENROUTER_KEYS : u32 = 1; +/// The minute of the hour the watch fires (off the :00 and :30 marks). +pub const TICK_MINUTE : u32 = 17; + +pub const ST_BUSY : u8 = 0; +pub const ST_HUNGRY : u8 = 1; +pub const ST_IDLE : u8 = 2; + +pub const ACT_NONE : u8 = 0; +pub const ACT_CLEAN_DISK : u8 = 1; +pub const ACT_REQUEST_KEYS : u8 = 2; +pub const ACT_START_LANE : u8 = 3; + +/// IDLE: no bee job runs and none waits. HUNGRY: nothing queued while swarm issues wait. BUSY otherwise. +pub fn state_of(running: u32, queued: u32, waiting: u32) -> u8 { + if (running == 0 and queued == 0) { return ST_IDLE; } + if (queued == 0 and waiting > 0) { return ST_HUNGRY; } + return ST_BUSY; +} + +pub fn builds_allowed(disk_gb: u32) -> bool { + return disk_gb >= MIN_DISK_GB; +} + +pub fn keys_short(zai: u32, openrouter: u32) -> bool { + return zai < MIN_ZAI_KEYS or openrouter < MIN_OPENROUTER_KEYS; +} + +/// One more free-tier lane is worth starting: nothing queued, work waiting, and the machine below its lane cap +/// (the cap is kernel.t27 agent_cap for this machine, passed in). +pub fn lane_wanted(queued: u32, waiting: u32, lanes: u32, cap: u32) -> bool { + return queued == 0 and waiting > 0 and lanes < cap; +} + +/// The one action of a tick, in the order of harm. +pub fn action_of(running: u32, queued: u32, waiting: u32, lanes: u32, cap: u32, zai: u32, openrouter: u32, disk_gb: u32) -> u8 { + if (!builds_allowed(disk_gb)) { return ACT_CLEAN_DISK; } + if (keys_short(zai, openrouter)) { return ACT_REQUEST_KEYS; } + if (lane_wanted(queued, waiting, lanes, cap)) { return ACT_START_LANE; } + return ACT_NONE; +} + +pub fn action_name(a: u8) -> string { + if (a == ACT_CLEAN_DISK) { return "clean disk, no builds"; } + if (a == ACT_REQUEST_KEYS) { return "request keys from the owner"; } + if (a == ACT_START_LANE) { return "start one free-tier lane"; } + return "none"; +} + +pub fn state_name(s: u8) -> string { + if (s == ST_IDLE) { return "IDLE"; } + if (s == ST_HUNGRY) { return "HUNGRY"; } + return "BUSY"; +} + +// --- tests --------------------------------------------------------------------------------------------------- + +test the_first_real_pulse_reads_hungry_and_wants_a_lane { + // 2026-10-20 19:05Z: 3 jobs running, 0 queued, 16 swarm issues filed, 1 lane of cap 3 + assert state_of(3, 0, 16) == ST_HUNGRY; + assert action_of(3, 0, 16, 1, 3, 7, 1, 8) == ACT_START_LANE; +} + +test idle_needs_both_zero { + assert state_of(0, 0, 0) == ST_IDLE; + assert state_of(0, 1, 5) == ST_BUSY; + assert state_of(2, 4, 5) == ST_BUSY; +} + +test disk_comes_before_keys_and_lanes { + assert action_of(0, 0, 9, 0, 3, 0, 0, 6) == ACT_CLEAN_DISK; + assert builds_allowed(8) and !builds_allowed(7); +} + +test keys_come_before_lanes { + assert action_of(0, 0, 9, 0, 3, 2, 1, 20) == ACT_REQUEST_KEYS; + assert action_of(0, 0, 9, 0, 3, 7, 0, 20) == ACT_REQUEST_KEYS; + assert !keys_short(3, 1); +} + +test no_lane_over_the_cap_or_without_work { + assert action_of(0, 0, 9, 3, 3, 7, 1, 20) == ACT_NONE; + assert action_of(0, 0, 0, 0, 3, 7, 1, 20) == ACT_NONE; + assert action_of(0, 2, 9, 0, 3, 7, 1, 20) == ACT_NONE; +} + +test names_are_stable { + assert action_name(ACT_START_LANE) == "start one free-tier lane"; + assert state_name(ST_HUNGRY) == "HUNGRY"; +} + +invariant a_tick_takes_at_most_one_action { + assert action_of(0, 0, 9, 0, 3, 0, 0, 0) == ACT_CLEAN_DISK; +}