AbstractPhil commited on
Commit
564e4dd
·
verified ·
1 Parent(s): 2b94430

mini-beatrix-3/code: re-cut at amoe-lora 0.2.11 (9bae609: the compiled adapter chain's recompile budget and shape gate) + the stop / install / relaunch / guard scripts of the 2026-09-28 relaunch at 122,000; the pod's mission file as running

Browse files
mini-beatrix-3/code/README.md CHANGED
@@ -1,17 +1,23 @@
1
  # mini-beatrix-3 — the training code, pinned to the commits that train it
2
 
3
- This folder is a verbatim snapshot of the two libraries and the pod files behind the `mini-beatrix-3` run, taken on
4
- 2026-09-27 while the run was at step ~106,600 of 245,670. The libraries are copies of their public GitHub commits;
5
- the pod files are the launch, resume, stop and shipping scripts as they run on the training machine.
 
6
 
7
  | part | what it is | where it comes from |
8
  |---|---|---|
9
  | `alephllm/` | the model, the trainer, the data plane, the instruments, the arm program and the mission loop (`geolip.alephllm`, version 0.10.5) | [github.com/AbstractEyes/alephllm](https://github.com/AbstractEyes/alephllm) @ `e8b7440` |
10
- | `amoe-lora/` | the arm library: the relay adapters, attach / detach / mask handles, recompute-in-backward, the compiled adapter chain, the anchor file format (`amoe.__version__` 0.2.10; its `pyproject.toml` still says 0.10.0) | [github.com/AbstractEyes/amoe-lora](https://github.com/AbstractEyes/amoe-lora) @ `f3db5d4` (branch `experimental`) |
11
  | `pod/mission.json` | the mission file: the craft, the data plane, the stage arms, the knobs; the trainer re-reads the refreshable keys at every phase boundary | the pod, as running |
12
  | `pod/launch_mission.sh` | the launch: `torchrun` data-parallel over every card on the machine, resume-first | the pod |
13
  | `pod/relaunch_106000.sh` | the one-command relaunch used on 2026-09-27: sets the knobs, checks the installed versions, launches, restarts the sidecar, prints the proving log lines | the pod |
14
  | `pod/stop_at_106000.sh` | a clean manual stop at a 2,000-step checkpoint (the resume state is exactly that checkpoint) | the pod |
 
 
 
 
 
15
  | `pod/arm_anchor_sidecar.py` | ships every attached stage arm as a safetensors anchor at each checkpoint (the trainer's own boundary anchors ship at phase ends) | the pod |
16
  | `pod/write_mission_json.py` | how the mission file was written from the pre-flight screens' verdicts | the pod |
17
  | `pod/env.sh` | the shell environment (the token comes from the container's own environment; nothing secret is in the file) | the pod |
@@ -39,7 +45,7 @@ the pod files are the launch, resume, stop and shipping scripts as they run on t
39
  | `arm_spec_certified` | a gate: a phase that attaches an arm opens only when this is true; setting it false ends the session cleanly at the next boundary | true |
40
  | `abstain_chunks` | abstention (quiet-term) chunks per step; `null` = one per task chunk (16 of 16); an integer = that many, each scaled so the term's weight per step is unchanged | 4 since step 106,000 |
41
  | `adapter_recompute` | the arms' intermediates are recomputed in the backward instead of kept (about 0.3 GB per arm instead of 3 GB at this shape) | true since 106,000 |
42
- | `adapter_compile` | the per-block adapter chain runs through `torch.compile` with inductor's precision-cast emulation (the plain compile produced a non-finite backward on the address stage) | true since 106,000 |
43
  | `minted_lexicon` | the curriculum's word pool for the rule-chain stages (216 minted words), its share of stage 3 and the pair rate | see the file |
44
  | `head_birth` | how the aleph head is born (`revival_birth` fits it to the first batch at step 0) | `revival_birth` |
45
  | `l6_ledger` | the in-run guard certification record (the rank-collapse guard halts the run on its own when certified) | `v3_preflight/l6_guards/...` |
@@ -63,3 +69,18 @@ sd = load_file("arms/s1_perspective_step106000.safetensors") # keys blocks.<bl
63
 
64
  Anchors before 2026-09-25 were converted in place from a torch-serialised form to real safetensors; `load_anchor` reads
65
  either form by content.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # mini-beatrix-3 — the training code, pinned to the commits that train it
2
 
3
+ This folder is a verbatim snapshot of the two libraries and the pod files behind the `mini-beatrix-3` run, first taken
4
+ on 2026-09-27 at step ~106,600 of 245,670 and re-cut on 2026-09-28 at step 122,000 when the arm library moved to 0.2.11
5
+ (the section on the compiled chain below). The libraries are copies of their public GitHub commits; the pod files are the
6
+ launch, resume, stop, install and shipping scripts as they run on the training machine.
7
 
8
  | part | what it is | where it comes from |
9
  |---|---|---|
10
  | `alephllm/` | the model, the trainer, the data plane, the instruments, the arm program and the mission loop (`geolip.alephllm`, version 0.10.5) | [github.com/AbstractEyes/alephllm](https://github.com/AbstractEyes/alephllm) @ `e8b7440` |
11
+ | `amoe-lora/` | the arm library: the relay adapters, attach / detach / mask handles, recompute-in-backward, the compiled adapter chain, the anchor file format (`amoe.__version__` 0.2.11; its `pyproject.toml` still says 0.10.0) | [github.com/AbstractEyes/amoe-lora](https://github.com/AbstractEyes/amoe-lora) @ `9bae609` (branch `experimental`; `f3db5d4` = 0.2.10 trained steps 106,000 to 122,000) |
12
  | `pod/mission.json` | the mission file: the craft, the data plane, the stage arms, the knobs; the trainer re-reads the refreshable keys at every phase boundary | the pod, as running |
13
  | `pod/launch_mission.sh` | the launch: `torchrun` data-parallel over every card on the machine, resume-first | the pod |
14
  | `pod/relaunch_106000.sh` | the one-command relaunch used on 2026-09-27: sets the knobs, checks the installed versions, launches, restarts the sidecar, prints the proving log lines | the pod |
15
  | `pod/stop_at_106000.sh` | a clean manual stop at a 2,000-step checkpoint (the resume state is exactly that checkpoint) | the pod |
16
+ | `pod/stop_at_checkpoint.sh` | the same stop generalized to any checkpoint step (used at 122,000 on 2026-09-28) | the pod |
17
+ | `pod/install_0211.sh` | installs the arm library at the pinned 0.2.11 commit and reads the versions back; refuses while a trainer is alive | the pod |
18
+ | `pod/relaunch_3arms.sh` | the relaunch used on 2026-09-28: the running form, the version gate, the proving log lines, the recompile-warning count | the pod |
19
+ | `pod/go_relaunch_0211.sh` | stop at a named checkpoint, install, relaunch, each stage gated on the previous one's report line | the pod |
20
+ | `pod/compile_guard.sh` | polls the log for torch's recompile-limit warning (the sign of the compiled chain falling back to eager) and arms the stop-and-relaunch at the next checkpoint | the pod |
21
  | `pod/arm_anchor_sidecar.py` | ships every attached stage arm as a safetensors anchor at each checkpoint (the trainer's own boundary anchors ship at phase ends) | the pod |
22
  | `pod/write_mission_json.py` | how the mission file was written from the pre-flight screens' verdicts | the pod |
23
  | `pod/env.sh` | the shell environment (the token comes from the container's own environment; nothing secret is in the file) | the pod |
 
45
  | `arm_spec_certified` | a gate: a phase that attaches an arm opens only when this is true; setting it false ends the session cleanly at the next boundary | true |
46
  | `abstain_chunks` | abstention (quiet-term) chunks per step; `null` = one per task chunk (16 of 16); an integer = that many, each scaled so the term's weight per step is unchanged | 4 since step 106,000 |
47
  | `adapter_recompute` | the arms' intermediates are recomputed in the backward instead of kept (about 0.3 GB per arm instead of 3 GB at this shape) | true since 106,000 |
48
+ | `adapter_compile` | the per-block adapter chain runs through `torch.compile` with inductor's precision-cast emulation (the plain compile produced a non-finite backward on the address stage); since 0.2.11 the library also raises torch's recompile budget and routes only the training shapes through the compiled chain (the section below) | true since 106,000 |
49
  | `minted_lexicon` | the curriculum's word pool for the rule-chain stages (216 minted words), its share of stage 3 and the pair rate | see the file |
50
  | `head_birth` | how the aleph head is born (`revival_birth` fits it to the first batch at step 0) | `revival_birth` |
51
  | `l6_ledger` | the in-run guard certification record (the rank-collapse guard halts the run on its own when certified) | `v3_preflight/l6_guards/...` |
 
69
 
70
  Anchors before 2026-09-25 were converted in place from a torch-serialised form to real safetensors; `load_anchor` reads
71
  either form by content.
72
+
73
+ ## The compiled chain and its recompile budget (0.2.11)
74
+
75
+ The adapter chain is compiled with static shapes, so every distinct (mask pattern, input shape) pair is one entry in torch's
76
+ compile cache for that function. Torch's default budget is 8 recompiles per function, and when it is exceeded torch does
77
+ not run that one call eagerly: it marks the function to bypass the compiler for the rest of the process, with a single
78
+ warning line. The stage 2 boundary evaluation fed the armed model probe prompts of many different lengths, spent that budget
79
+ in seconds, and from the stage 3 attach on every step ran the arms eager (10.5 s per step at three arms instead of about
80
+ 7.5) until the run was stopped at 122,000 and relaunched on 0.2.11. The fix lives in the library, not in a launch flag:
81
+ `compiled_chain()` raises the budget (64 per function, 4,096 accumulated) before the first compile, and `chain_for(h)`
82
+ routes only the first two distinct (batch, length) shapes seen through the compiled chain, every other shape through the
83
+ eager chain, which is the same function. The unit test `tests/test_compiled_chain_gate.py` in the GitHub repository stacks
84
+ three arms, runs the training shape armed and with each member masked, then forty prompt lengths, with torch configured
85
+ to raise on any budget hit. `pod/compile_guard.sh` watches the log for the warning and arms a stop-and-relaunch if it ever
86
+ returns.
mini-beatrix-3/code/amoe-lora/src/amoe/__init__.py CHANGED
@@ -1,24 +1,24 @@
1
- """amoe — aleph mixture-of-experts adapters (train/attach/align/detach).
2
-
3
- The productized adapter system from the geolip-aleph research line.
4
- "LoRA-style" refers to the attach/detach usage pattern, not the math:
5
- these are aleph-addressed patch heads, not low-rank matrices.
6
- """
7
- from .core.adapter import AdapterSpec, RelayPatchwork, BlockWithAdapter
8
- from .core.address import AlephAddress
9
- from .core.dispatch import AnchorDispatch, BlockWithDispatch, set_mask
10
- from .io.checkpoint import (AnchorCheckpoint, DispatchCheckpoint,
11
- load_anchor, load_dispatch,
12
- import_legacy_keys)
13
- from .runtime.attach import attach, detach, AttachHandle
14
- from .train.config import TrainConfig, AlignConfig
15
- from .train.trainer import train
16
- from .train.aligner import align
17
- from .train.quiet import QuietSpec, kl_abstain, quiet_step
18
- from . import laws
19
-
20
- __version__ = "0.2.10"
21
-
22
- # The diffusion subsystem (amoe.diffusion) is imported lazily — its verbs
23
- # live under `import amoe.diffusion as ad`. Core is pure torch; diffusers
24
- # is only touched by bindings/samplers/data that need it.
 
1
+ """amoe — aleph mixture-of-experts adapters (train/attach/align/detach).
2
+
3
+ The productized adapter system from the geolip-aleph research line.
4
+ "LoRA-style" refers to the attach/detach usage pattern, not the math:
5
+ these are aleph-addressed patch heads, not low-rank matrices.
6
+ """
7
+ from .core.adapter import AdapterSpec, RelayPatchwork, BlockWithAdapter
8
+ from .core.address import AlephAddress
9
+ from .core.dispatch import AnchorDispatch, BlockWithDispatch, set_mask
10
+ from .io.checkpoint import (AnchorCheckpoint, DispatchCheckpoint,
11
+ load_anchor, load_dispatch,
12
+ import_legacy_keys)
13
+ from .runtime.attach import attach, detach, AttachHandle
14
+ from .train.config import TrainConfig, AlignConfig
15
+ from .train.trainer import train
16
+ from .train.aligner import align
17
+ from .train.quiet import QuietSpec, kl_abstain, quiet_step
18
+ from . import laws
19
+
20
+ __version__ = "0.2.11"
21
+
22
+ # The diffusion subsystem (amoe.diffusion) is imported lazily — its verbs
23
+ # live under `import amoe.diffusion as ad`. Core is pure torch; diffusers
24
+ # is only touched by bindings/samplers/data that need it.
mini-beatrix-3/code/amoe-lora/src/amoe/core/adapter.py CHANGED
@@ -1,210 +1,254 @@
1
- """RelayPatchwork adapter + the hybrid-safe block wrapper.
2
-
3
- The certified geometry (16 slots x D=4 through a 64-atom aleph address,
4
- squared-ReLU patch head, sigmoid gate initialized at -3): ~261k params
5
- at d=1024. Output head zero-initialized so a fresh anchor is inert
6
- (modulo the documented LayerNorm-bias leak).
7
-
8
- BlockWithAdapter carries an `enabled` switch: when False, the forward
9
- returns the block output untouched — the single-anchor half of the
10
- toggle law. It also carries a `recompute` switch (0.2.7): when True,
11
- the adapter's intermediates are not kept for the backward pass but
12
- recomputed from the block output when the backward reaches them
13
- (torch.utils.checkpoint, non-reentrant). Same math, one extra adapter
14
- forward per backward, a fraction of the activation memory: at d=1024
15
- on 4096-token rows the kept intermediates run ~96 MB per block per
16
- adapter per two-row micro-batch, which is what walls a 32-block trunk
17
- with several stacked arms on a 32 GB card. A stack of wrappers on one
18
- block recomputes as one chain (0.2.8), keeping one residual copy per
19
- block for the whole stack.
20
- """
21
- from __future__ import annotations
22
-
23
- import contextlib
24
- from dataclasses import dataclass
25
-
26
- import torch
27
- import torch.nn as nn
28
- import torch.nn.functional as F
29
- from torch.utils.checkpoint import checkpoint
30
-
31
- from .address import AlephAddress
32
-
33
-
34
- @dataclass
35
- class AdapterSpec:
36
- n_slots: int = 16
37
- K: int = 64
38
- D: int = 4
39
- tau: float = 0.1
40
- hidden: int = 178
41
- gate_init: float = -3.0
42
- zero_init_head: bool = True
43
-
44
-
45
- class SquaredReLU(nn.Module):
46
- def forward(self, x):
47
- return F.relu(x) ** 2
48
-
49
-
50
- class RelayPatchwork(nn.Module):
51
- def __init__(self, d: int, spec: AdapterSpec | None = None):
52
- super().__init__()
53
- s = spec or AdapterSpec()
54
- self.spec = s
55
- self.n_slots = s.n_slots
56
- self.proj = nn.Linear(d, s.n_slots * s.D, bias=False)
57
- nn.init.orthogonal_(self.proj.weight)
58
- self.addr = AlephAddress(s.K, s.D, s.tau)
59
- self.consume = nn.Sequential(
60
- nn.Linear(s.n_slots * s.D, s.hidden), SquaredReLU(),
61
- nn.LayerNorm(s.hidden), nn.Linear(s.hidden, d))
62
- if s.zero_init_head:
63
- nn.init.zeros_(self.consume[-1].weight)
64
- self.gate = nn.Parameter(torch.tensor(float(s.gate_init)))
65
-
66
- def forward(self, x):
67
- B, n, _ = x.shape
68
- slots = self.proj(x).view(B, n, self.n_slots, self.spec.D)
69
- feats = self.addr.m_hat(slots).reshape(B, n, -1)
70
- return x + torch.sigmoid(self.gate) * self.consume(feats)
71
-
72
-
73
- def _apply_chain(h, *adapters):
74
- for a in adapters:
75
- h = a(h)
76
- return h
77
-
78
-
79
- def _autocast_uncached(device_type: str):
80
- """The active autocast region re-entered with its weight-cast cache OFF
81
- (0.2.10), for the checkpointed chain. Autocast caches the low-precision
82
- copies of parameters it casts; when an earlier pass inside the same
83
- autocast region (a no_grad pass with a member masked, as the abstention
84
- term runs) already cast a member's weights, the recorded forward of the
85
- checkpointed chain holds no cast ops for them, while the recompute,
86
- which re-enters autocast fresh, records the casts, and the checkpoint's
87
- operator-list check fails ('different metadata'; torch 2.8). With the
88
- cache off inside the chain both passes record the same ops. The cost is
89
- one bf16 cast of each live adapter's weights per block call."""
90
- try:
91
- enabled = torch.is_autocast_enabled(device_type)
92
- except TypeError: # older signature: cuda only
93
- enabled = device_type == "cuda" and torch.is_autocast_enabled()
94
- if not enabled:
95
- return contextlib.nullcontext()
96
- try:
97
- dtype = torch.get_autocast_dtype(device_type)
98
- except (TypeError, AttributeError):
99
- dtype = torch.get_autocast_gpu_dtype() if device_type == "cuda" else torch.bfloat16
100
- return torch.autocast(device_type=device_type, dtype=dtype, cache_enabled=False)
101
-
102
-
103
- # The compiled chain (0.2.9). The aleph address's compiled backward is
104
- # non-finite under bf16 autocast unless inductor rounds its fused
105
- # intermediates to bf16 the way eager does (measured 2026-09: of the
106
- # adapter's stages compiled alone, only addr.m_hat fails; pure fp32 and
107
- # emulated casts are clean), so the switch below is set when the chain
108
- # is first compiled. It is process-global; set it False before the first
109
- # compile to opt out (not recommended for bf16 training).
110
- COMPILE_EMULATE_CASTS = True
111
- COMPILE_MODE = "default" # reduce-overhead (CUDA graphs) fails inside a checkpointed chain
112
- _COMPILED_CHAIN = None
113
-
114
-
115
- def compiled_chain():
116
- global _COMPILED_CHAIN
117
- if _COMPILED_CHAIN is None:
118
- if COMPILE_EMULATE_CASTS:
119
- import torch._inductor.config as ic
120
- ic.emulate_precision_casts = True
121
- _COMPILED_CHAIN = torch.compile(_apply_chain, dynamic=False,
122
- mode=COMPILE_MODE)
123
- return _COMPILED_CHAIN
124
-
125
-
126
- class BlockWithAdapter(nn.Module):
127
- """Wraps one decoder block; hybrid-safe (tuple or tensor output).
128
-
129
- `recompute=True` (0.2.7) routes the adapter call through a
130
- non-reentrant checkpoint whenever autograd is recording: the slot
131
- projection, the address features and the consume MLP's hidden
132
- activations are dropped after the forward and rebuilt from the block
133
- output during the backward. The head is deterministic and
134
- dropout-free, so the RNG state is not snapshotted (no device sync per
135
- call) and the recomputed values are the values the forward produced.
136
- Inference paths (no_grad, prefill, step) never checkpoint.
137
-
138
- Stacked wrappers (0.2.8; an arm attached over an arm nests a wrapper
139
- around a wrapper) recompute as ONE chain: the outermost wrapper with
140
- recompute on runs the core block once and applies every enabled
141
- adapter of the stack, innermost first, inside a single checkpoint, so
142
- one residual copy per block is kept for the whole stack instead of
143
- one per adapter. Each wrapper's `enabled` switch is read where it
144
- sits, so masks behave exactly as in the kept path.
145
-
146
- `compile_chain=True` (0.2.9) runs that same chain through
147
- torch.compile (default mode, one graph per mask pattern, shared by
148
- every block since the adapters enter as arguments), with or without
149
- the recompute; see COMPILE_EMULATE_CASTS above for the precision
150
- switch it needs. The compiled and the eager chain agree to bf16
151
- rounding, not bit for bit: a training run that switches it on owes a
152
- gradient census on its own card first.
153
- """
154
-
155
- def __init__(self, block: nn.Module, adapter: RelayPatchwork,
156
- recompute: bool = False, compile_chain: bool = False):
157
- super().__init__()
158
- self.block = block
159
- self.adapter = adapter
160
- self.enabled = True
161
- self.recompute = bool(recompute)
162
- self.compile_chain = bool(compile_chain)
163
-
164
- def stack(self):
165
- """(wrappers innermost first, the core block) of the stack this
166
- wrapper tops."""
167
- wraps, core = [], self
168
- while isinstance(core, BlockWithAdapter):
169
- wraps.append(core)
170
- core = core.block
171
- wraps.reverse()
172
- return wraps, core
173
-
174
- def forward(self, *args, **kwargs):
175
- if self.compile_chain or (self.recompute and torch.is_grad_enabled()):
176
- wraps, core = self.stack()
177
- out = core(*args, **kwargs)
178
- live = [w.adapter for w in wraps if w.enabled]
179
- if not live:
180
- return out
181
- h = out[0] if isinstance(out, tuple) else out
182
- fn = compiled_chain() if self.compile_chain else _apply_chain
183
- if self.recompute and torch.is_grad_enabled():
184
- with _autocast_uncached(h.device.type):
185
- y = checkpoint(fn, h, *live, use_reentrant=False,
186
- preserve_rng_state=False)
187
- else:
188
- y = fn(h, *live)
189
- return (y,) + out[1:] if isinstance(out, tuple) else y
190
- out = self.block(*args, **kwargs)
191
- if not self.enabled:
192
- return out
193
- if isinstance(out, tuple):
194
- return (self.adapter(out[0]),) + out[1:]
195
- return self.adapter(out)
196
-
197
- # Incremental-decode passthroughs (trunks with a cached decode path,
198
- # e.g. AlephLM prefill/step). The patch head is position-wise, so
199
- # applying it to the single new position is exact.
200
- def prefill(self, *args, **kwargs):
201
- out, cache = self.block.prefill(*args, **kwargs)
202
- return (self.adapter(out) if self.enabled else out), cache
203
-
204
- def step(self, *args, **kwargs):
205
- out = self.block.step(*args, **kwargs)
206
- if not self.enabled:
207
- return out
208
- if isinstance(out, tuple):
209
- return (self.adapter(out[0]),) + out[1:]
210
- return self.adapter(out)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """RelayPatchwork adapter + the hybrid-safe block wrapper.
2
+
3
+ The certified geometry (16 slots x D=4 through a 64-atom aleph address,
4
+ squared-ReLU patch head, sigmoid gate initialized at -3): ~261k params
5
+ at d=1024. Output head zero-initialized so a fresh anchor is inert
6
+ (modulo the documented LayerNorm-bias leak).
7
+
8
+ BlockWithAdapter carries an `enabled` switch: when False, the forward
9
+ returns the block output untouched — the single-anchor half of the
10
+ toggle law. It also carries a `recompute` switch (0.2.7): when True,
11
+ the adapter's intermediates are not kept for the backward pass but
12
+ recomputed from the block output when the backward reaches them
13
+ (torch.utils.checkpoint, non-reentrant). Same math, one extra adapter
14
+ forward per backward, a fraction of the activation memory: at d=1024
15
+ on 4096-token rows the kept intermediates run ~96 MB per block per
16
+ adapter per two-row micro-batch, which is what walls a 32-block trunk
17
+ with several stacked arms on a 32 GB card. A stack of wrappers on one
18
+ block recomputes as one chain (0.2.8), keeping one residual copy per
19
+ block for the whole stack.
20
+ """
21
+ from __future__ import annotations
22
+
23
+ import contextlib
24
+ from dataclasses import dataclass
25
+
26
+ import torch
27
+ import torch.nn as nn
28
+ import torch.nn.functional as F
29
+ from torch.utils.checkpoint import checkpoint
30
+
31
+ from .address import AlephAddress
32
+
33
+
34
+ @dataclass
35
+ class AdapterSpec:
36
+ n_slots: int = 16
37
+ K: int = 64
38
+ D: int = 4
39
+ tau: float = 0.1
40
+ hidden: int = 178
41
+ gate_init: float = -3.0
42
+ zero_init_head: bool = True
43
+
44
+
45
+ class SquaredReLU(nn.Module):
46
+ def forward(self, x):
47
+ return F.relu(x) ** 2
48
+
49
+
50
+ class RelayPatchwork(nn.Module):
51
+ def __init__(self, d: int, spec: AdapterSpec | None = None):
52
+ super().__init__()
53
+ s = spec or AdapterSpec()
54
+ self.spec = s
55
+ self.n_slots = s.n_slots
56
+ self.proj = nn.Linear(d, s.n_slots * s.D, bias=False)
57
+ nn.init.orthogonal_(self.proj.weight)
58
+ self.addr = AlephAddress(s.K, s.D, s.tau)
59
+ self.consume = nn.Sequential(
60
+ nn.Linear(s.n_slots * s.D, s.hidden), SquaredReLU(),
61
+ nn.LayerNorm(s.hidden), nn.Linear(s.hidden, d))
62
+ if s.zero_init_head:
63
+ nn.init.zeros_(self.consume[-1].weight)
64
+ self.gate = nn.Parameter(torch.tensor(float(s.gate_init)))
65
+
66
+ def forward(self, x):
67
+ B, n, _ = x.shape
68
+ slots = self.proj(x).view(B, n, self.n_slots, self.spec.D)
69
+ feats = self.addr.m_hat(slots).reshape(B, n, -1)
70
+ return x + torch.sigmoid(self.gate) * self.consume(feats)
71
+
72
+
73
+ def _apply_chain(h, *adapters):
74
+ for a in adapters:
75
+ h = a(h)
76
+ return h
77
+
78
+
79
+ def _autocast_uncached(device_type: str):
80
+ """The active autocast region re-entered with its weight-cast cache OFF
81
+ (0.2.10), for the checkpointed chain. Autocast caches the low-precision
82
+ copies of parameters it casts; when an earlier pass inside the same
83
+ autocast region (a no_grad pass with a member masked, as the abstention
84
+ term runs) already cast a member's weights, the recorded forward of the
85
+ checkpointed chain holds no cast ops for them, while the recompute,
86
+ which re-enters autocast fresh, records the casts, and the checkpoint's
87
+ operator-list check fails ('different metadata'; torch 2.8). With the
88
+ cache off inside the chain both passes record the same ops. The cost is
89
+ one bf16 cast of each live adapter's weights per block call."""
90
+ try:
91
+ enabled = torch.is_autocast_enabled(device_type)
92
+ except TypeError: # older signature: cuda only
93
+ enabled = device_type == "cuda" and torch.is_autocast_enabled()
94
+ if not enabled:
95
+ return contextlib.nullcontext()
96
+ try:
97
+ dtype = torch.get_autocast_dtype(device_type)
98
+ except (TypeError, AttributeError):
99
+ dtype = torch.get_autocast_gpu_dtype() if device_type == "cuda" else torch.bfloat16
100
+ return torch.autocast(device_type=device_type, dtype=dtype, cache_enabled=False)
101
+
102
+
103
+ # The compiled chain (0.2.9). The aleph address's compiled backward is
104
+ # non-finite under bf16 autocast unless inductor rounds its fused
105
+ # intermediates to bf16 the way eager does (measured 2026-09: of the
106
+ # adapter's stages compiled alone, only addr.m_hat fails; pure fp32 and
107
+ # emulated casts are clean), so the switch below is set when the chain
108
+ # is first compiled. It is process-global; set it False before the first
109
+ # compile to opt out (not recommended for bf16 training).
110
+ COMPILE_EMULATE_CASTS = True
111
+ COMPILE_MODE = "default" # reduce-overhead (CUDA graphs) fails inside a checkpointed chain
112
+ # The recompile budget and the shape gate (0.2.11). The chain is compiled
113
+ # static (dynamic=False), so every (mask pattern x input shape) pair is one
114
+ # cache entry of `_apply_chain`, and when torch's per-code recompile limit
115
+ # (8 by default) is exceeded dynamo does not merely run that call eagerly:
116
+ # it marks the code object SKIP and the chain runs eager for the rest of the
117
+ # process, with a single warning. An evaluation that feeds the armed model
118
+ # short inputs of many lengths (probe items, chat documents) spends that
119
+ # budget in seconds. Two guards: the dynamo limits are raised to the floors
120
+ # below when the chain is first compiled, and only the first
121
+ # COMPILE_MAX_SHAPES distinct (batch, length) shapes are routed through the
122
+ # compiled chain; any other shape runs the eager chain (exact; the compile's
123
+ # gain is in the training shape, which is the first shape a training run
124
+ # presents). The budget then holds the mask patterns of the training shapes
125
+ # only (m + 1 per shape at m arms).
126
+ COMPILE_RECOMPILE_LIMIT = 64
127
+ COMPILE_ACCUMULATED_LIMIT = 4096
128
+ COMPILE_MAX_SHAPES = 2
129
+ _COMPILED_CHAIN = None
130
+ _COMPILED_SHAPES: set = set()
131
+
132
+
133
+ def _raise_dynamo_limits():
134
+ import torch._dynamo.config as dc
135
+ for names, floor in ((("recompile_limit", "cache_size_limit"),
136
+ COMPILE_RECOMPILE_LIMIT),
137
+ (("accumulated_recompile_limit",
138
+ "accumulated_cache_size_limit"),
139
+ COMPILE_ACCUMULATED_LIMIT)):
140
+ for name in names: # 2.8 carries both spellings; older torch one
141
+ if hasattr(dc, name):
142
+ setattr(dc, name, max(int(getattr(dc, name)), int(floor)))
143
+
144
+
145
+ def compiled_chain():
146
+ global _COMPILED_CHAIN
147
+ if _COMPILED_CHAIN is None:
148
+ if COMPILE_EMULATE_CASTS:
149
+ import torch._inductor.config as ic
150
+ ic.emulate_precision_casts = True
151
+ _raise_dynamo_limits()
152
+ _COMPILED_CHAIN = torch.compile(_apply_chain, dynamic=False,
153
+ mode=COMPILE_MODE)
154
+ return _COMPILED_CHAIN
155
+
156
+
157
+ def chain_for(h):
158
+ """The chain to run on an input of h's shape (0.2.11): the compiled
159
+ chain for the first COMPILE_MAX_SHAPES distinct (batch, length) shapes
160
+ seen, the eager chain for every other shape."""
161
+ key = (int(h.shape[0]), int(h.shape[1]))
162
+ if key in _COMPILED_SHAPES:
163
+ return compiled_chain()
164
+ if len(_COMPILED_SHAPES) < COMPILE_MAX_SHAPES:
165
+ _COMPILED_SHAPES.add(key)
166
+ return compiled_chain()
167
+ return _apply_chain
168
+
169
+
170
+ class BlockWithAdapter(nn.Module):
171
+ """Wraps one decoder block; hybrid-safe (tuple or tensor output).
172
+
173
+ `recompute=True` (0.2.7) routes the adapter call through a
174
+ non-reentrant checkpoint whenever autograd is recording: the slot
175
+ projection, the address features and the consume MLP's hidden
176
+ activations are dropped after the forward and rebuilt from the block
177
+ output during the backward. The head is deterministic and
178
+ dropout-free, so the RNG state is not snapshotted (no device sync per
179
+ call) and the recomputed values are the values the forward produced.
180
+ Inference paths (no_grad, prefill, step) never checkpoint.
181
+
182
+ Stacked wrappers (0.2.8; an arm attached over an arm nests a wrapper
183
+ around a wrapper) recompute as ONE chain: the outermost wrapper with
184
+ recompute on runs the core block once and applies every enabled
185
+ adapter of the stack, innermost first, inside a single checkpoint, so
186
+ one residual copy per block is kept for the whole stack instead of
187
+ one per adapter. Each wrapper's `enabled` switch is read where it
188
+ sits, so masks behave exactly as in the kept path.
189
+
190
+ `compile_chain=True` (0.2.9) runs that same chain through
191
+ torch.compile (default mode, one graph per mask pattern, shared by
192
+ every block since the adapters enter as arguments), with or without
193
+ the recompute; see COMPILE_EMULATE_CASTS above for the precision
194
+ switch it needs. The compiled and the eager chain agree to bf16
195
+ rounding, not bit for bit: a training run that switches it on owes a
196
+ gradient census on its own card first.
197
+ """
198
+
199
+ def __init__(self, block: nn.Module, adapter: RelayPatchwork,
200
+ recompute: bool = False, compile_chain: bool = False):
201
+ super().__init__()
202
+ self.block = block
203
+ self.adapter = adapter
204
+ self.enabled = True
205
+ self.recompute = bool(recompute)
206
+ self.compile_chain = bool(compile_chain)
207
+
208
+ def stack(self):
209
+ """(wrappers innermost first, the core block) of the stack this
210
+ wrapper tops."""
211
+ wraps, core = [], self
212
+ while isinstance(core, BlockWithAdapter):
213
+ wraps.append(core)
214
+ core = core.block
215
+ wraps.reverse()
216
+ return wraps, core
217
+
218
+ def forward(self, *args, **kwargs):
219
+ if self.compile_chain or (self.recompute and torch.is_grad_enabled()):
220
+ wraps, core = self.stack()
221
+ out = core(*args, **kwargs)
222
+ live = [w.adapter for w in wraps if w.enabled]
223
+ if not live:
224
+ return out
225
+ h = out[0] if isinstance(out, tuple) else out
226
+ fn = chain_for(h) if self.compile_chain else _apply_chain
227
+ if self.recompute and torch.is_grad_enabled():
228
+ with _autocast_uncached(h.device.type):
229
+ y = checkpoint(fn, h, *live, use_reentrant=False,
230
+ preserve_rng_state=False)
231
+ else:
232
+ y = fn(h, *live)
233
+ return (y,) + out[1:] if isinstance(out, tuple) else y
234
+ out = self.block(*args, **kwargs)
235
+ if not self.enabled:
236
+ return out
237
+ if isinstance(out, tuple):
238
+ return (self.adapter(out[0]),) + out[1:]
239
+ return self.adapter(out)
240
+
241
+ # Incremental-decode passthroughs (trunks with a cached decode path,
242
+ # e.g. AlephLM prefill/step). The patch head is position-wise, so
243
+ # applying it to the single new position is exact.
244
+ def prefill(self, *args, **kwargs):
245
+ out, cache = self.block.prefill(*args, **kwargs)
246
+ return (self.adapter(out) if self.enabled else out), cache
247
+
248
+ def step(self, *args, **kwargs):
249
+ out = self.block.step(*args, **kwargs)
250
+ if not self.enabled:
251
+ return out
252
+ if isinstance(out, tuple):
253
+ return (self.adapter(out[0]),) + out[1:]
254
+ return self.adapter(out)
mini-beatrix-3/code/pod/compile_guard.sh ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # THE COMPILED-CHAIN GUARD (the standing rule: the run never continues on the eager chain). Polls the mission log every 2 min for
3
+ # the dynamo recompile-limit warning (the signature of the chain falling back to eager) written after this guard's start and for a
4
+ # trainer that runs slower than the eager line; on either, it writes an alert file, logs it, and arms go_relaunch_0211.sh for the
5
+ # next 2,000-step checkpoint (stop, reinstall 0.2.11, relaunch compiled) unless a go sequence is already running. Bounded by hours.
6
+ # Usage: setsid nohup bash /root/v3/tools/compile_guard.sh [hours=48] > /root/v3/logs/compile_guard.out 2>&1 < /dev/null &
7
+ HOURS="${1:-48}"
8
+ LOG=/root/v3/logs/mission.log
9
+ OUT=/root/v3/logs/compile_guard.log
10
+ ALERT=/root/v3/logs/COMPILED_CHAIN_LOST
11
+ EAGER_LINE=9.8 # s/step: the compiled three-arm pace is ~8.7, the eager 10.4-10.8; later stages raise both, so the line is re-read from the log's own history
12
+ log() { echo "[guard $(date -u '+%H:%M:%SZ')] $*" >> "$OUT"; }
13
+ START_BYTES=$(stat -c %s "$LOG")
14
+ log "armed for $HOURS h from byte $START_BYTES of the mission log"
15
+ END=$(( $(date +%s) + HOURS * 3600 ))
16
+ while [ "$(date +%s)" -lt "$END" ]; do
17
+ sleep 120
18
+ NOW_BYTES=$(stat -c %s "$LOG")
19
+ if [ "$NOW_BYTES" -lt "$START_BYTES" ]; then START_BYTES=0; fi
20
+ N=$(tail -c +$((START_BYTES + 1)) "$LOG" | grep -ac "recompile_limit")
21
+ if [ "$N" -gt 0 ]; then
22
+ if [ -f "$ALERT" ]; then continue; fi
23
+ date -u +%FT%TZ > "$ALERT"
24
+ log "ALERT: $N recompile-limit warning(s) since the guard's start: the chain has fallen back to eager"
25
+ tail -c +$((START_BYTES + 1)) "$LOG" | grep -a "recompile_limit" | head -n 3 | cut -c1-200 >> "$OUT"
26
+ if [ "$(pgrep -fc 'go_relaunch_021[1]')" != "0" ]; then log "a go sequence is already running; not arming another"; continue; fi
27
+ STEP=$(tail -c 1500 "$LOG" | tr "\r" "\n" | grep -aoE "[0-9]+/245670" | tail -n 1 | cut -d/ -f1)
28
+ NEXT=$(( (STEP / 2000 + 1) * 2000 ))
29
+ NEXTC=$(printf "%'d" "$NEXT")
30
+ log "arming the stop + reinstall + relaunch at the next checkpoint $NEXTC (the standing rule: never eager)"
31
+ setsid nohup bash /root/v3/tools/go_relaunch_0211.sh "$NEXTC" 0 > /root/v3/logs/go_relaunch_0211.out 2>&1 < /dev/null &
32
+ disown
33
+ fi
34
+ done
35
+ log "guard window ended"
mini-beatrix-3/code/pod/go_relaunch_0211.sh ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # THE WHOLE SEQUENCE: stop at the named checkpoint -> install amoe-lora 0.2.11 -> relaunch (the running form).
3
+ # Usage: setsid nohup bash /root/v3/tools/go_relaunch_0211.sh <step, e.g. 119,496> [ships expected, default 3] > /root/v3/logs/go_relaunch_0211.out 2>&1 &
4
+ STEP="${1:?step with commas}"
5
+ SHIPS="${2:-3}"
6
+ OUT=/root/v3/logs/go_relaunch_0211.log
7
+ log() { echo "[go $(date -u '+%H:%M:%SZ')] $*" | tee -a "$OUT"; }
8
+ log "armed for the $STEP checkpoint"
9
+ bash /root/v3/tools/stop_at_checkpoint.sh "$STEP" "$SHIPS"
10
+ if ! grep -aq "STOPPED" "/root/v3/logs/stop_${STEP//,/}.log" 2>/dev/null; then log "ERROR: the stop did not report STOPPED; nothing installed, nothing launched"; exit 2; fi
11
+ log "stopped; installing"
12
+ bash /root/v3/tools/install_0211.sh
13
+ if ! grep -aq "INSTALLED" /root/v3/logs/install_0211.log 2>/dev/null; then log "ERROR: the install did not report INSTALLED; nothing launched"; exit 3; fi
14
+ log "installed; relaunching"
15
+ bash /root/v3/tools/relaunch_3arms.sh 1 1 4
16
+ log "done: $(tail -n 1 /root/v3/logs/relaunch_3arms.log)"
mini-beatrix-3/code/pod/install_0211.sh ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # INSTALL amoe-lora 0.2.11 (the compiled chain's recompile budget + shape gate; commit 9bae609 on experimental) into /root/venv.
3
+ # Refuses while a trainer process is alive. alephllm stays at 0.10.5 (e8b7440). Reads the versions back.
4
+ OUT=/root/v3/logs/install_0211.log
5
+ log() { echo "[install $(date -u '+%H:%M:%SZ')] $*" | tee -a "$OUT"; }
6
+ LEFT=$(ps -eo cmd | grep -E "torch.distributed.run|geolip.alephllm.train.mission" | grep -v grep | wc -l)
7
+ if [ "$LEFT" != "0" ]; then log "REFUSED: $LEFT trainer processes alive"; exit 3; fi
8
+ SHA=9bae60922c83f5af242c5516fa369b9232afd2f3
9
+ log "installing amoe-lora 0.2.11 ($SHA)"
10
+ /root/venv/bin/pip install --no-deps --force-reinstall -q "git+https://github.com/AbstractEyes/amoe-lora@$SHA" >> "$OUT" 2>&1 && log "amoe-lora installed" || { log "amoe-lora install FAILED"; exit 4; }
11
+ echo "$SHA" > /root/v3/amoe_ref.txt
12
+ V=$(/root/venv/bin/python -c 'import amoe, geolip.alephllm as a; print(amoe.__version__, a.__version__)' 2>&1 | tail -n 1)
13
+ log "versions read back: amoe-lora / alephllm = $V"
14
+ case "$V" in "0.2.11 0.10.5") log "INSTALLED";; *) log "ERROR: expected 0.2.11 0.10.5"; exit 5;; esac
15
+ /root/venv/bin/python -c 'from amoe.core import adapter as A; print("gate constants:", A.COMPILE_RECOMPILE_LIMIT, A.COMPILE_ACCUMULATED_LIMIT, A.COMPILE_MAX_SHAPES)' | tee -a "$OUT"
mini-beatrix-3/code/pod/mission.json CHANGED
@@ -82,5 +82,5 @@
82
  "adapter_recompute": true,
83
  "adapter_compile": true,
84
  "abstain_chunks": 4,
85
- "_note": "the mission file as it runs on the pod at the time of this snapshot (2026-09-27 UTC): resumed from the 106,000 checkpoint with adapter_recompute, adapter_compile and abstain_chunks=4 set; the trainer re-reads the refreshable keys at every phase boundary"
86
  }
 
82
  "adapter_recompute": true,
83
  "adapter_compile": true,
84
  "abstain_chunks": 4,
85
+ "_note": "the mission file as it runs on the pod at the time of this snapshot (2026-09-28 UTC): resumed from the 122,000 checkpoint on amoe-lora 0.2.11 with adapter_recompute, adapter_compile and abstain_chunks=4 set; the trainer re-reads the refreshable keys at every phase boundary"
86
  }
mini-beatrix-3/code/pod/relaunch_3arms.sh ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # THE RELAUNCH ON amoe-lora 0.2.11 (the 106,000 relaunch form generalized). Usage: bash relaunch_3arms.sh [recompute:0|1] [compile:0|1] [abstain_chunks:int|none]
3
+ # Defaults = the running form (recompute on, the compiled chain on, 4 abstention chunks). Keeps arm_spec_certified true, refuses if a
4
+ # trainer process is alive or a card holds memory or the installed libraries are not 0.2.11 / 0.10.5, launches launch_mission.sh (a
5
+ # resume-first launch: the resume state of the stop), restarts the arm sidecar, and prints the proving lines: versions, the attach
6
+ # lines with 'recompute on, compile on', the 'N abstention chunks per step' line, the first steps, the cards.
7
+ RECOMPUTE="${1:-1}"
8
+ COMPILE="${2:-1}"
9
+ ABST="${3:-4}"
10
+ OUT=/root/v3/logs/relaunch_3arms.log
11
+ log() { echo "[relaunch $(date -u '+%H:%M:%SZ')] $*" | tee -a "$OUT"; }
12
+ LEFT=$(ps -eo cmd | grep -E "torch.distributed.run|geolip.alephllm.train.mission" | grep -v grep | wc -l)
13
+ if [ "$LEFT" != "0" ]; then log "REFUSED: $LEFT trainer processes alive"; exit 3; fi
14
+ BUSY=$(nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits | awk '$1 > 2000' | wc -l)
15
+ if [ "$BUSY" != "0" ]; then log "REFUSED: a card still holds memory"; nvidia-smi --query-gpu=index,memory.used --format=csv,noheader | tee -a "$OUT"; exit 4; fi
16
+ V=$(/root/venv/bin/python -c 'import amoe, geolip.alephllm as a; print(amoe.__version__, a.__version__)' 2>&1 | tail -n 1)
17
+ log "installed: amoe-lora / alephllm = $V"
18
+ case "$V" in "0.2.11 0.10.5") ;; *) log "REFUSED: expected 0.2.11 0.10.5 (the compiled chain's recompile budget + shape gate)"; exit 5;; esac
19
+ RESUME=$(/root/venv/bin/python -c 'import torch; s = torch.load("/root/v3/runs/mission/mini-beatrix-3/resume/latest.pt", map_location="cpu", weights_only=False); print(s.get("step", "?"))' 2>/dev/null | tail -n 1)
20
+ log "resume state step: $RESUME"
21
+ cp /root/v3/mission.json "/root/v3/mission.json.bak_relaunch_$(date -u +%m%d_%H%M)"
22
+ /root/venv/bin/python - "$RECOMPUTE" "$COMPILE" "$ABST" "$RESUME" <<'PYEOF'
23
+ import json, os, sys
24
+ p = "/root/v3/mission.json"
25
+ C = json.load(open(p))
26
+ C["adapter_recompute"] = bool(int(sys.argv[1]))
27
+ C["adapter_compile"] = bool(int(sys.argv[2]))
28
+ C["abstain_chunks"] = None if sys.argv[3].lower() in ("none", "null", "") else int(sys.argv[3])
29
+ C["arm_spec_certified"] = True
30
+ C["_halt_note"] = ("relaunched from the %s resume state on amoe-lora 0.2.11 after the compiled chain fell back to eager at the stage 2 close "
31
+ "(dynamo's recompile limit hit by the boundary evaluation's variable-length inputs); the form unchanged: recompute + the "
32
+ "compiled chain + 4 abstention chunks; arm_spec_certified true" % sys.argv[4])
33
+ tmp = p + ".tmp"
34
+ json.dump(C, open(tmp, "w"), indent=2)
35
+ os.replace(tmp, p)
36
+ print("mission.json:", {k: C[k] for k in ("adapter_recompute", "adapter_compile", "abstain_chunks", "arm_spec_certified", "micro_batch", "grad_accum")})
37
+ PYEOF
38
+ log "launching"
39
+ cd /root/v3 || exit 9
40
+ bash /root/v3/launch_mission.sh | tee -a "$OUT"
41
+ for i in $(seq 1 90); do
42
+ if tr "\r" "\n" < /root/v3/logs/mission.log | tail -n 400 | grep -aq "\[train\] stage arms"; then break; fi
43
+ sleep 10
44
+ done
45
+ tr "\r" "\n" < /root/v3/logs/mission.log | tail -n 400 | grep -aE "\[mission\] alephllm|\[arms\] attached|\[arms\] restored|\[resume\]|\[train\] phase|\[train\] stage arms|Traceback|Error" | tail -n 14 | cut -c1-200 | tee -a "$OUT"
46
+ export $(tr "\0" "\n" < /proc/$(pgrep -f "geolip.alephllm.train.mission" | head -n 1)/environ | grep "^HF_TOKEN=")
47
+ nohup /root/venv/bin/python tools/arm_anchor_sidecar.py --poll 120 >> logs/arm_sidecar.out 2>&1 < /dev/null &
48
+ disown
49
+ sleep 2
50
+ log "sidecar pid $(pgrep -f 'python.*arm_anchor_sidecar.py' | tail -n 1) restarted"
51
+ sleep 240
52
+ log "steps after 4 min: $(tail -c 1500 /root/v3/logs/mission.log | tr "\r" "\n" | grep -aE "s/step" | tail -n 1 | cut -c1-100)"
53
+ log "recompile-limit warnings in the log since the launch: $(tail -c 300000 /root/v3/logs/mission.log | grep -ac 'recompile_limit')"
54
+ log "cards: $(nvidia-smi --query-gpu=index,memory.used --format=csv,noheader | tr '\n' ';')"
55
+ log "RELAUNCHED"
mini-beatrix-3/code/pod/stop_at_checkpoint.sh ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # THE MANUAL STOP AT A CHECKPOINT (the 106,000 recipe of 2026-09-26, generalized). Usage: bash stop_at_checkpoint.sh <step, e.g. 119,496> [ships expected, default 3]
3
+ # Waits for the trainer's checkpoint line for that step (a full resume state, saved AND uploaded), then for the sidecar's arm ships
4
+ # from that state (one per attached arm; cap 6 min), then kills the torchrun tree while the loop is back in plain compute (no writes
5
+ # in flight): the resume lands exactly at that step and only the seconds after it are lost. The two-rank KeyboardInterrupt path is
6
+ # NOT used: torchrun treats a worker's exit as a failure and SIGTERMs the other mid-save.
7
+ STEP="${1:?step with commas, e.g. 119,496}"
8
+ SHIPS="${2:-3}"
9
+ LOG=/root/v3/logs/mission.log
10
+ SLOG=/root/v3/logs/arm_sidecar.log
11
+ OUT="/root/v3/logs/stop_${STEP//,/}.log"
12
+ log() { echo "[stop $(date -u '+%H:%M:%SZ')] $*" >> "$OUT"; }
13
+ log "armed: waiting for the [ckpt] step $STEP line ($SHIPS sidecar ships expected)"
14
+ for i in $(seq 1 2160); do
15
+ if tr "\r" "\n" < "$LOG" | grep -aq "\[ckpt\] step $STEP:.*saved & uploaded"; then break; fi
16
+ sleep 10
17
+ done
18
+ if ! tr "\r" "\n" < "$LOG" | grep -aq "\[ckpt\] step $STEP:.*saved & uploaded"; then log "ERROR: no checkpoint line within 6 h; nothing done"; exit 2; fi
19
+ log "checkpoint line seen: $(tr "\r" "\n" < "$LOG" | grep -a "\[ckpt\] step $STEP:" | tail -n 1 | cut -c1-140)"
20
+ for i in $(seq 1 36); do
21
+ n=$(grep -ac "step $STEP: shipped" "$SLOG")
22
+ if [ "$n" -ge "$SHIPS" ]; then break; fi
23
+ sleep 10
24
+ done
25
+ log "sidecar ships for $STEP: $(grep -ac "step $STEP: shipped" "$SLOG") ($SHIPS expected)"
26
+ sleep 20
27
+ LAST=$(tail -c 1500 "$LOG" | tr "\r" "\n" | grep -aE "s/step" | tail -n 1 | cut -c1-100)
28
+ log "last bar line before the kill: $LAST"
29
+ PIDS=$(ps -eo pid,cmd | grep -E "torch.distributed.run|geolip.alephllm.train.mission" | grep -v grep | awk '{print $1}' | tr "\n" " ")
30
+ log "killing the torchrun tree: $PIDS"
31
+ kill -9 $PIDS 2>/dev/null
32
+ sleep 5
33
+ LEFT=$(ps -eo pid,cmd | grep -E "torch.distributed.run|geolip.alephllm.train.mission" | grep -v grep | wc -l)
34
+ log "processes left: $LEFT; cards: $(nvidia-smi --query-gpu=index,memory.used --format=csv,noheader | tr "\n" ";")"
35
+ for SPID in $(pgrep -f "python.*arm_anchor_sidecar.py"); do kill "$SPID" 2>/dev/null; log "sidecar $SPID stopped (the relaunch restarts it)"; done
36
+ log "resume state: $(ls -la --time-style=+%H:%M:%S /root/v3/runs/mission/mini-beatrix-3/resume/latest.pt | awk '{print $5, $6}')"
37
+ log "STOPPED at the $STEP point"
mini-beatrix-3/code/requirements.txt CHANGED
@@ -1,4 +1,4 @@
1
- # the pod's environment for the mini-beatrix-3 mission (2026-09-27); CUDA 12.8 wheels
2
  torch==2.8.0
3
  torchvision==0.23.0
4
  torchaudio==2.8.0
@@ -8,6 +8,6 @@ huggingface_hub==1.32.0
8
  datasets==5.0.1
9
  numpy==2.1.2
10
  pyarrow==25.0.1
11
- # the two libraries, pinned to the commits that trained this run (the snapshots in this folder are these commits):
12
- # pip install --no-deps 'git+https://github.com/AbstractEyes/amoe-lora@f3db5d4' # amoe 0.2.10
13
  # pip install --no-deps 'git+https://github.com/AbstractEyes/alephllm@e8b7440' # geolip.alephllm 0.10.5
 
1
+ # the pod's environment for the mini-beatrix-3 mission (2026-09-28); CUDA 12.8 wheels
2
  torch==2.8.0
3
  torchvision==0.23.0
4
  torchaudio==2.8.0
 
8
  datasets==5.0.1
9
  numpy==2.1.2
10
  pyarrow==25.0.1
11
+ # the two libraries, pinned to the commits that train this run (the snapshots in this folder are these commits):
12
+ # pip install --no-deps 'git+https://github.com/AbstractEyes/amoe-lora@9bae609' # amoe 0.2.11 (0.2.10 @f3db5d4 trained steps 106,000-122,000)
13
  # pip install --no-deps 'git+https://github.com/AbstractEyes/alephllm@e8b7440' # geolip.alephllm 0.10.5