238 lines
13 KiB
Markdown
238 lines
13 KiB
Markdown
# DGR-031 evidence — the project-owned `ShardEngine` interface
|
|
|
|
**Completed:** 2026-07-23
|
|
**Branch:** `ralph/distributed-gguf-runtime`
|
|
**Authority:** `.scratch/distributed-gguf-runtime/prd.json`
|
|
**Dependencies:** DGR-021 (`evidence/DGR-021/README.md` — versioned activation
|
|
envelope, `NamedTensor`/`ActivationEnvelope` as the project-owned wire-envelope
|
|
layer), DGR-025 (`evidence/DGR-025/README.md` — exact artifact/runtime recipe
|
|
identity; both read before changing code).
|
|
|
|
## Objective
|
|
|
|
Isolate worker/protocol code from llama.cpp internals behind a stable
|
|
project-owned engine contract, so a fake fixture engine (DGR-032) and a real
|
|
llama.cpp-backed engine (DGR-037) are interchangeable subclasses of one
|
|
interface.
|
|
|
|
## What was found live before changing code
|
|
|
|
Per RALPH-CONTEXT, legacy pass states were not trusted; the live surrounding
|
|
contracts were read and exercised before designing this one:
|
|
|
|
- `packages/node/meshnet_node/shard_lifecycle.py` (DGR-022) already defines a
|
|
versioned RPC/session lifecycle contract — `StructuredStatus`, `StatusCode`,
|
|
`CacheExpectation`, `CacheResult`, `LifecycleState`, `SessionLifecycle` — but
|
|
it is explicitly the *wire RPC* contract "consumed by a future generated
|
|
gRPC binding," not an execution-engine boundary.
|
|
- `packages/node/meshnet_node/native_backend.py` (DGR-025) is the identity
|
|
boundary for the native GGUF artifact — it derives and attests a
|
|
`ShardIdentity`, but does not define an execution contract either.
|
|
- `packages/node/meshnet_node/protocol.py` (DGR-021) defines a project-owned
|
|
`NamedTensor`/`ActivationEnvelope` for activation traffic *between shard
|
|
hops over the network*, distinct from the generated-protobuf wire ABI in
|
|
`native_protocol`.
|
|
- `packages/node/meshnet_node/shard_runtime_server.py` (DGR-024) is today a
|
|
real gRPC servicer that proves wire fidelity by checksumming and echoing
|
|
bytes — it has no execution engine behind it yet; that seam is exactly
|
|
where `ShardEngine` plugs in for DGR-037.
|
|
- `packages/node/meshnet_node/architecture_boundary.py` established the
|
|
precedent this story follows for tail output: `TailOutput.sampled_token()`
|
|
never exposes raw logits, only a sampled token id.
|
|
- No `ShardEngine` (or `shard_engine`) symbol existed anywhere in the
|
|
repository prior to this story (confirmed by
|
|
`grep -rn -i "shardengine\|shard_engine"` across `.py`/`.md`, which returned
|
|
only planning-document prose naming it as future work).
|
|
|
|
Live verification of the pre-existing dependency contracts before adding new
|
|
code: `PYTHONPATH=packages/node:packages/tracker .venv/bin/python3 -m pytest -q
|
|
tests/test_shard_lifecycle.py tests/test_activation_envelope.py
|
|
tests/test_architecture_boundary.py tests/test_native_shard_protocol.py
|
|
tests/test_shard_runtime_harness.py` → `95 passed, 3 skipped`.
|
|
|
|
## What was added (this story's change)
|
|
|
|
### `packages/node/meshnet_node/shard_engine.py` (new)
|
|
|
|
The `ShardEngine` boundary: an `abc.ABC` with eight abstract operations —
|
|
`load`, `capabilities`, `prefill`, `decode`, `cancel`, `release`, `health`,
|
|
`metrics` — matching the acceptance criterion's list exactly (`prefill`/
|
|
`decode` share one operation family; their shared result type is what the
|
|
criterion calls the "boundary/logits result"). Every request/result type is a
|
|
frozen dataclass built from plain `str`/`int`/`bytes`/`Mapping` values:
|
|
|
|
- `EngineTensor` / `BoundaryBundle` — the project-owned named-tensor
|
|
activation crossing a shard boundary (head/middle/tail-in). Deliberately a
|
|
*new*, minimal type distinct from both `native_protocol.pb.TensorBundle`
|
|
(generated-protobuf ABI) and `protocol.NamedTensor`/`ActivationEnvelope`
|
|
(wire-framing/fragmentation concerns irrelevant to model execution) — a
|
|
fourth, execution-facing layer underneath the three that already existed.
|
|
- `TokenOutput` — a tail shard's sampled result: a token id (+ optional
|
|
decoded text), never a raw logits tensor.
|
|
- `MtpHook` — reserved multi-token-prediction hook; its own `__post_init__`
|
|
raises if constructed with `enabled=True`, so the type exists (fixing its
|
|
field shape for DGR-051/DGR-066) without any code path being able to turn it
|
|
on before DGR-066, matching RALPH-CONTEXT's "MTP is reserved and off for
|
|
alpha."
|
|
- `ArchitectureAuxStateHook` — reserved per-shard architecture auxiliary state
|
|
(V4 CSA/HCA/SWA/indexer/compressor and similar); has no wire encoding and is
|
|
never embedded in a `BoundaryBundle`, matching RALPH-CONTEXT's "remain local
|
|
... never carried over the WAN seam."
|
|
- `LoadRequest`/`LoadResult`, `EngineCapabilities`, `PrefillRequest`/
|
|
`DecodeRequest` (exactly one of `token_ids`/`token_id` (head) or `input`
|
|
(middle/tail) required — enforced in `__post_init__`), `StepResult` (a
|
|
successful result must carry an output; `cache_result` reuses
|
|
`shard_lifecycle.CacheResult`), `HealthResult`, `MetricsResult`.
|
|
- Status vocabulary is reused, not reinvented: `StructuredStatus`/
|
|
`StatusCode`/`CacheExpectation`/`CacheResult` are imported from
|
|
`shard_lifecycle` (already project-owned and version-stable) rather than a
|
|
parallel enum living alongside it.
|
|
- The module imports nothing from `native_protocol`, `grpc`, or `ctypes` —
|
|
verified structurally, not just by convention (see tests below).
|
|
|
|
### `tests/shard_engine_contract.py` (new)
|
|
|
|
A reusable, non-`test_`-prefixed helper: `assert_shard_engine_contract(make_engine)`
|
|
takes a zero-arg engine factory and runs nine lifecycle checks — health before
|
|
load, load→capabilities range/MTP-off, prefill→decode determinism (byte-identical
|
|
output replayed on a fresh session), middle-shard boundary-bundle-in/out vs.
|
|
head/tail token-output, deterministic cache-miss on an unopened session,
|
|
stale-route-epoch rejection, cancel-then-decode rejection (+ cancel
|
|
idempotency), release-then-decode rejection (+ release idempotency), and
|
|
metrics reporting cancelled sessions. DGR-032's fixture and DGR-037's
|
|
llama.cpp binding are both expected to import this and pass it against their
|
|
own engine, proving identical lifecycle semantics without duplicating the
|
|
checks.
|
|
|
|
### `tests/test_shard_engine.py` (new)
|
|
|
|
- `_ReferenceEngine`: a minimal in-memory `ShardEngine` used only to prove the
|
|
shared contract is non-vacuous. It is explicitly *not* the DGR-032
|
|
deterministic fixture (no delay/memory-pressure/malformed/crash injection —
|
|
that is DGR-032's own, larger scope); the docstring says so to prevent this
|
|
story's evidence from being read as inherited completion credit for DGR-032.
|
|
- Dataclass validation tests: abstract-class instantiation refusal, tensor/
|
|
bundle/token-output field validation, MTP-hook enable refusal, exactly-one-
|
|
input-kind enforcement on `PrefillRequest`/`DecodeRequest`, `LoadRequest`
|
|
shard-range-vs-total-layers validation, `StepResult` output-required-on-OK.
|
|
- `test_shard_engine_module_imports_no_native_or_grpc_or_wire_abi_types`:
|
|
walks `vars(shard_engine_module)` and asserts no bound name's `__name__` is
|
|
`ctypes`, `grpc`, or `meshnet_node.native_protocol` — a structural check
|
|
(not a docstring-text grep, which produced a false positive on first draft
|
|
because the module's own docstring *names* `ggml_tensor` as an example of
|
|
what must never appear) that the ABI-isolation acceptance criterion holds.
|
|
|
|
### `.scratch/distributed-gguf-runtime/prd.json` / issue markdown
|
|
|
|
Marked `DGR-031.passes = true` with `completionNotes`; regenerated
|
|
`issues/031-introduce-the-project-owned-shardengine-interface.md` via
|
|
`scripts/ralph_prd_schema.py render` so it matches `prd.json` byte-for-byte.
|
|
|
|
## Acceptance criteria → evidence
|
|
|
|
1. **load/capabilities/prefill/decode/boundary-logits-result/cancel/release/
|
|
health/metrics** — `ShardEngine`'s eight abstract methods plus
|
|
`StepResult.output: BoundaryBundle | TokenOutput | None`. Verified by
|
|
`test_reference_engine_obeys_the_shared_shard_engine_contract` and the
|
|
middle-shard-vs-tail-shard assertion inside
|
|
`assert_shard_engine_contract`.
|
|
2. **No `ggml_tensor`/llama context/scheduler/ABI-owned structure** — every
|
|
type in `shard_engine.py` is a plain dataclass over `str`/`int`/`bytes`/
|
|
`Mapping`; no import of `native_protocol`, `grpc`, or `ctypes`. Verified by
|
|
`test_shard_engine_module_imports_no_native_or_grpc_or_wire_abi_types`.
|
|
3. **Reserved typed MTP/architecture-aux-state hooks, not enabled** —
|
|
`MtpHook.__post_init__` raises on `enabled=True`; `ArchitectureAuxStateHook`
|
|
carries opaque shard-local state with no wire path. Verified by
|
|
`test_mtp_hook_is_reserved_and_refuses_to_enable` and
|
|
`test_architecture_aux_state_hook_carries_opaque_shard_local_state`, plus
|
|
`assert_shard_engine_contract`'s `caps.supports_mtp is False` check.
|
|
4. **Contract tests proving fake and future llama implementations obey
|
|
identical lifecycle semantics** — `tests/shard_engine_contract.py` is
|
|
written to be imported by DGR-032 and DGR-037 against their own engines;
|
|
`test_shard_engine.py` proves it is real by running it against
|
|
`_ReferenceEngine`.
|
|
5. **Gates + this handoff** — below.
|
|
|
|
## Commands and results
|
|
|
|
```bash
|
|
PYTHONPATH=packages/node:packages/tracker .venv/bin/python3 -m pytest -q tests/test_shard_engine.py
|
|
```
|
|
```text
|
|
12 passed in 0.13s
|
|
```
|
|
|
|
```bash
|
|
PYTHONPATH=packages/node:packages/tracker .venv/bin/python3 -m pytest -q \
|
|
tests/test_shard_engine.py tests/test_shard_lifecycle.py \
|
|
tests/test_architecture_boundary.py tests/test_activation_envelope.py \
|
|
tests/test_native_shard_protocol.py tests/test_shard_runtime_harness.py
|
|
```
|
|
```text
|
|
95 passed, 3 skipped in 3.65s
|
|
```
|
|
|
|
```bash
|
|
.venv/bin/python3 -m compileall packages/node/meshnet_node/shard_engine.py tests/shard_engine_contract.py tests/test_shard_engine.py
|
|
```
|
|
```text
|
|
Compiling 'packages/node/meshnet_node/shard_engine.py'...
|
|
Compiling 'tests/shard_engine_contract.py'...
|
|
Compiling 'tests/test_shard_engine.py'...
|
|
```
|
|
|
|
```bash
|
|
git diff --check
|
|
```
|
|
```text
|
|
(no output — clean)
|
|
```
|
|
|
|
## Limitations
|
|
|
|
- `tests/` as a whole does not collect cleanly in this environment: 27
|
|
pre-existing test modules fail to import for missing optional dependencies
|
|
(`cryptography`, etc.) unrelated to this story. Reproduced identically with
|
|
`git stash` before this session's change (`27 errors during collection`),
|
|
so this is pre-existing environment state, not a regression introduced
|
|
here. This story's own gates were run as the targeted, scoped test set
|
|
above per the shared quality gates' own wording ("Targeted deterministic
|
|
tests pass").
|
|
- The contract in `shard_engine_contract.py` proves *lifecycle* semantics
|
|
(gating, cache-miss/stale-epoch/cancel/release, boundary-vs-token output
|
|
shape) are identical across implementations. It does not — and cannot yet
|
|
— prove numerical parity between a fake and a real engine; that is
|
|
DGR-036's explicit job once DGR-032 and DGR-037 both exist.
|
|
- `_ReferenceEngine` in `test_shard_engine.py` is intentionally minimal
|
|
(no delay/memory-pressure/malformed-output/crash injection). DGR-032's
|
|
acceptance criteria require those independently; nothing here should be
|
|
read as satisfying them.
|
|
- No gRPC/CMake/native-build changes were needed or made — this story is
|
|
pure Python interface/type definition (`evidenceClass: model-free`,
|
|
`hardware: none`), so the native CMake/CTest and patch-stack gates in the
|
|
shared quality-gate list do not apply here (consistent with DGR-021/DGR-025,
|
|
which record the same non-applicability for non-native stories).
|
|
|
|
## Dependency handoff
|
|
|
|
- **DGR-032** (fake `ShardEngine`): subclass `ShardEngine`, add delay/memory-
|
|
pressure/malformed-output/crash injection, and pass the *same*
|
|
`assert_shard_engine_contract` from `tests/shard_engine_contract.py`
|
|
against it — no new contract vocabulary should be needed.
|
|
- **DGR-034/DGR-035** (range-aware GGUF ownership, boundary I/O): `LoadRequest`
|
|
already carries `shard_start`/`shard_end`/`total_layers`/`recipe`; `capabilities()`
|
|
reports the authoritative range via `EngineCapabilities.is_head`/`is_tail`.
|
|
`BoundaryBundle.token_id_sideband` is reserved for the first-three-hash-
|
|
routed-layers V4 requirement RALPH-CONTEXT documents.
|
|
- **DGR-037** (bind llama.cpp to the worker): implement `ShardEngine` as a
|
|
thin wrapper around the native artifact from `native_backend.py`/
|
|
`runtime_recipe.py`; `shard_runtime_server.py`'s `Session`/`GetCapability`/
|
|
`Health`/`Cancel`/`Release` handlers become the translation layer between
|
|
`pb.*` wire messages and this module's request/result types — this story
|
|
intentionally does not touch `shard_runtime_server.py` itself, since that
|
|
wiring is DGR-037's scope.
|
|
- **DGR-051** (V4 `ShardEngine` adapter): `MtpHook`/`ArchitectureAuxStateHook`
|
|
fix the field shape now so the V4 adapter does not need a breaking change
|
|
to enable MTP after DGR-066 or to carry CSA/HCA/SWA/indexer/compressor
|
|
state.
|