| … | … | @@ -4267,6 +4267,60 @@ injected per layer: |
| 4267 | 4267 | Stopped, moves on (a loop starts the next set beside it: two drills on one tree), and SIGHUPs it on |
| 4268 | 4268 | exit. Drills run under a Python `subprocess.run` runner, and sets pause between runs via a pause file. |
| 4269 | 4269 | - A `ps | grep "[d]rill.py"` inside a shell whose own command line contains "drill.py" self-matches. |
| 4270 | - **R4c-4d DESIGN (2026-10-07; path chosen by the user 2026-10-07: facade first).** Core moves from l2 onto the |
| 4271 | async hc::storage in JS's async shape: the Promise mutex, the async open, and every JS await a co_await. The |
| 4272 | `Sync` frontier moves up to core's callers (store, repl, bee, blobs, wakeup, tools, tests). |
| 4273 | - **Research record:** `docs/research/r4c4d-inv.md` (keet-daemon f2b4bf1). Its sections: A (core's remaining l2 |
| 4274 | uses, with hc::storage signatures), B (every async JS function: its awaits in order and the coroutine it |
| 4275 | becomes), C (the frontier, each caller classed wait / scope / chain / net) and D (hazards D1–D11), all at |
| 4276 | keetd bb707dd. It corrects the R4c-4 inventory's mark-bitfield line numbers (off by +161). |
| 4277 | - **Order** (every sub-step builds, ctest green, every gate identical to the previous commit): |
| 4278 | - **E0. Frontier scopes first; core is unchanged.** Each frontier call gets a `Sync`: `wait` where the result |
| 4279 | is used at once, a bare scope around callback calls whose result is read inline, and nothing around network |
| 4280 | waits. While core still completes inline, these scopes are no-ops. They make E2's first delayed hop safe. |
| 4281 | A drill set removes a frontier scope and expects red. |
| 4282 | - **E1. An async facade over l2, with hc::storage's exact API** (temporary; widens hc/merkle_l2.h). CoreRX |
| 4283 | getters queue and resolve at `tryFlush`; `CoreTX::flush`, the session ops, close, `resumeCore`, |
| 4284 | `createCore`, `createGroup`, compact and `Atom::flush` / `flushed` return `js::Promise`. With it, E2–E4 |
| 4285 | compile against the final API without touching store/. |
| 4286 | - **E2. The Promise mutex** (lib/mutex.js literally, `_destroying` included). `locked_` and the callback |
| 4287 | users `co_await` the lock. The 8 "mutex held" throws become a `Sync` wait on the lock: unreachable while |
| 4288 | frontier entries are atomic, and a NoProgress, not a keetd throw, if one were reached. `lockExclusive` |
| 4289 | returns a promise. `Mutex::cancel` (no JS counterpart) is decided here (D1). |
| 4290 | - **E3. The async Core open:** `opening = _open()`, no I/O in the constructor, `ready()`, and JS's `opened` |
| 4291 | guard in `checkIfIdle`. `Session`'s open awaits `core.ready()` (ix:390). Constructor sites outside core |
| 4292 | `wait` on `ready()` in their E0 scope. |
| 4293 | - **E4. Per JS function group, bottom-up, each on the facade.** Each commit deletes the internal `Sync` |
| 4294 | scopes inside the functions it converts (D4: no coroutine may call a function that still waits, in an |
| 4295 | inner scope, on a mutex or flush the coroutine holds). The groups, in order: |
| 4296 | 1. write primitives (`flush`, `storeBitfieldRange` in JS's batch shape, `_truncate`, `flushHints`); |
| 4297 | 2. session-state.js ops; |
| 4298 | 3. core.js (`_verifyExclusive` / `_verifyShared` with `_verifies` batching, `checkConflict` / |
| 4299 | `_repairTreeNodes`, audit.js, copy-prologue.js, `close` / `destroy`); |
| 4300 | 4. mark-bitfield.js; |
| 4301 | 5. index.js; |
| 4302 | 6. fully-remote-proof.js. |
| 4303 | |
| 4304 | As each group converts, its frontier sites move from a bare scope to `wait` or `.then`. Each group's drill |
| 4305 | set is re-anchored and run red in the same commit. |
| 4306 | - **E5. The storage-type switch, in ONE commit.** The facade and merkle_l2.h are deleted. The CoreRX→`merkle::Rx` |
| 4307 | bridge (tools/mt's `StorageRx` / `StorageTree`) moves into core. Streams are consumed as JS consumes them |
| 4308 | (`for await` with the body's awaits interleaved). store/, the r3c / m3c tests, core-fixture, core-ops, |
| 4309 | store-ops, keetd-replicate and m4d switch in the same commit: they share the store and its lock. The M4d |
| 4310 | hook moves into rn's write completion (`rn::native::write`); BOUNDARY stays valid on the new write |
| 4311 | numbering. Only `tools/l2-ops` and l2's own gates stay on l2 until R4c-4e. |
| 4312 | - **E6. The core gate's `hops` mode and the R4c-4b concurrency scripts,** compared line for line with Bare at |
| 4313 | pool = 1. Any difference is a finding, fixed in core. |
| 4314 | - **Rules carried from the hazards:** |
| 4315 | - **Timer-resumed continuations open their own `Sync`.** `waitForRecovery`'s tail holds the state mutex, and |
| 4316 | a timer callback is a frontier entry (D4). |
| 4317 | - **No by-reference parameters in coroutines.** Each suspended member coroutine pins its object |
| 4318 | (`shared_from_this` or an `alive_` check after each `co_await`) (D6). |
| 4319 | - **Storage close = JS's sites exactly.** Destructors close nothing. Every CoreRX ends in `tryFlush` or |
| 4320 | `destroy` (D3). |
| 4321 | - **The deviations that become observable are ported** (D10): markBlock's `Promise.all` under the mark |
| 4322 | mutex, the MarkPage inserted before its read, awaited `preappend` / `postappend`, `setManifest`'s |
| 4323 | lock-first order, `compact`'s `Promise.all`, the userData flush awaited before 'ready'. |
| 4270 | 4324 | - **Then 4e-4** (`createProtocolStream` / `replicate`): the first gate that composes the stack |
| 4271 | 4325 | is a COMPOSED trace against JS (secret-stream + protomux + replicator), now meaningful. |
| 4272 | 4326 | |