13 Commits

Author SHA1 Message Date
Dobromir Popov
79c9bbaf63 controller: record DGR-034 completion 2026-08-01 01:08:29 +03:00
Dobromir Popov
d339cfde25 story: DGR-034 Implement dense-Llama range-aware GGUF ownership 2026-08-01 01:08:28 +03:00
Dobromir Popov
27a0d89678 docs: align PRD source-of-truth status 2026-07-27 08:58:51 +03:00
Dobromir Popov
8c87fae1ac chore: restore canonical PRD metadata and projections 2026-07-27 08:53:16 +03:00
Dobromir Popov
4d530d702c chore: restore canonical DGR-033 metadata projection 2026-07-26 23:04:45 +03:00
Dobromir Popov
7473bb7e44 fix: DGR-033 repair native worker protocol per cross-review BLOCK
Address the Codex GPT-5.5 review of the standalone fake C++ gRPC Shard
worker. Four root protocol defects fixed:

- Fail closed before SessionOpen: a per-session `opened` flag gates
  chunk/decode so no activation bypasses lifecycle, cancellation, epoch
  or flow-control state (terminal ERROR_CODE_INTERNAL), even when an
  out-of-band Cancel created placeholder state.
- Strict flow-control negotiation: NegotiateFlow takes the strictest of
  peer-vs-worker bounds (mirrors codec.negotiate_flow_control) and the
  negotiated per-session max_chunk_bytes is enforced on every bundle
  instead of trusting the peer proposal.
- In-stream ReleaseSignal now erases session state immediately.
- SessionOpen rejects incompatible schema, fingerprint, and shard-range
  identity and reports the worker's own served fingerprint rather than
  echoing the caller.

Adds 9 regression tests (worker suite 18 -> 27). Real gates on the
rebuilt pinned-gRPC binary: cmake build exit 0; ctest 2/2; worker
pytest 27 passed; harness+protocol 63 passed; compileall 0; diff --check
clean; ldd/nm show 0 llama/ggml linkage. DGR-033 passes -> true.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 22:57:03 +03:00
Dobromir Popov
c073826374 chore: reblock DGR-033 after protocol review 2026-07-26 22:34:34 +03:00
Dobromir Popov
0c7d475335 chore: remove Ralph lane runtime artifacts 2026-07-26 22:33:37 +03:00
Dobromir Popov
84d75f4cd2 controller: record DGR-033 completion 2026-07-25 22:38:01 +03:00
Dobromir Popov
766e480ba5 story: DGR-033 Build a standalone fake C++ gRPC Shard worker 2026-07-25 22:38:00 +03:00
Dobromir Popov
25e53bfeab story: DGR-032 Implement deterministic fake ShardEngine 2026-07-23 11:09:16 +03:00
Dobromir Popov
c34ab059cc story: DGR-031 Introduce the project-owned ShardEngine interface 2026-07-23 11:00:33 +03:00
Dobromir Popov
fd742d35c0 story: DGR-030 Add accelerator build presets and native CI matrix 2026-07-23 10:51:08 +03:00
77 changed files with 9704 additions and 97347 deletions

1405
.fuse_hidden0002bd66000001f0 Normal file

File diff suppressed because it is too large Load Diff

1521
.fuse_hidden0002bd66000001f9 Normal file

File diff suppressed because it is too large Load Diff

1
.gitignore vendored
View File

@@ -12,6 +12,7 @@ dist/
# Ralph local runtime state
.ralph-tui/*
!.ralph-tui/config.toml
.ralph-lane/
.env

View File

@@ -1,5 +0,0 @@
{
"plugin": [
".opencode/plugins/graphify.js"
]
}

View File

@@ -1,30 +0,0 @@
// graphify OpenCode plugin
// Injects a knowledge graph reminder before bash tool calls when the graph exists.
//
// IMPORTANT: keep the reminder string free of backticks and $(...) constructs.
// The hook prepends `echo "<reminder>" && <cmd>` to the user's bash command;
// backticks inside the double-quoted echo trigger bash command substitution,
// which both corrupts tool output and silently executes the very graphify
// command we are only suggesting. Plain words render fine in opencode's TUI.
import { existsSync } from "fs";
import { join } from "path";
export const GraphifyPlugin = async ({ directory }) => {
let reminded = false;
return {
"tool.execute.before": async (input, output) => {
if (reminded) return;
if (!existsSync(join(directory, "graphify-out", "graph.json"))) return;
if (input.tool === "bash") {
// ';' not '&&' — Windows PowerShell 5.1 rejects '&&' as a statement
// separator, breaking the first bash command of the session (#1646).
output.args.command =
'echo "[graphify] knowledge graph at graphify-out/. For focused questions, run graphify query with your question (scoped subgraph, usually much smaller than GRAPH_REPORT.md) instead of grepping raw files. Read GRAPH_REPORT.md only for broad architecture context." ; ' +
output.args.command;
reminded = true;
}
},
};
};

View File

@@ -1 +0,0 @@
0.9.29

View File

@@ -1,694 +0,0 @@
---
name: graphify
description: "Use for any question about a codebase, its architecture, file relationships, or project content — especially when graphify-out/ exists, where the question should be treated as a graphify query first. Turns any input (code, docs, papers, images, videos) into a persistent knowledge graph with god nodes, community detection, and query/path/explain tools."
---
# /graphify
Turn any folder of files into a navigable knowledge graph with community detection, an honest audit trail, and three outputs: interactive HTML, GraphRAG-ready JSON, and a plain-language GRAPH_REPORT.md.
## Usage
```
/graphify # full pipeline on current directory (HTML viz; add --obsidian for a vault)
/graphify <path> # full pipeline on specific path
/graphify https://github.com/<owner>/<repo> # clone repo then run full pipeline on it
/graphify https://github.com/<owner>/<repo> --branch <branch> # clone a specific branch
/graphify <url1> <url2> ... # clone multiple repos, build each, merge into one cross-repo graph
/graphify <path> --mode deep # thorough extraction, richer INFERRED edges
/graphify <path> --update # incremental - re-extract only new/changed files
/graphify <path> --directed # build directed graph (preserves edge direction: source→target)
/graphify <path> --whisper-model medium # use a larger Whisper model for better transcription accuracy
/graphify <path> --cluster-only # rerun clustering on existing graph
/graphify <path> --no-viz # skip visualization, just report + JSON
/graphify <path> --html # (HTML is generated by default - this flag is a no-op)
/graphify <path> --svg # also export graph.svg (embeds in Notion, GitHub)
/graphify <path> --graphml # export graph.graphml (Gephi, yEd)
/graphify <path> --neo4j # generate graphify-out/cypher.txt for Neo4j
/graphify <path> --neo4j-push bolt://localhost:7687 # push directly to Neo4j
/graphify <path> --falkordb # generate graphify-out/cypher.txt for FalkorDB
/graphify <path> --falkordb-push falkordb://localhost:6379 # push directly to FalkorDB
/graphify <path> --mcp # start MCP stdio server for agent access
/graphify <path> --watch # watch folder, auto-rebuild on code changes (no LLM needed)
/graphify <path> --wiki # build agent-crawlable wiki (index.md + one article per community)
/graphify <path> --obsidian --obsidian-dir ~/vaults/my-project # write vault to custom path (e.g. existing vault)
/graphify add <url> # fetch URL, save to ./raw, update graph
/graphify add <url> --author "Name" # tag who wrote it
/graphify add <url> --contributor "Name" # tag who added it to the corpus
/graphify query "<question>" # BFS traversal - broad context
/graphify query "<question>" --dfs # DFS - trace a specific path
/graphify query "<question>" --budget 1500 # cap answer at N tokens
/graphify path "AuthModule" "Database" # shortest path between two concepts
/graphify explain "SwinTransformer" # plain-language explanation of a node
```
## What graphify is for
Drop any folder of code, docs, papers, images, or video into graphify and get a queryable knowledge graph. Persistent across sessions, honest audit trail (EXTRACTED/INFERRED/AMBIGUOUS), community detection surfaces cross-document connections you wouldn't think to ask about.
## What You Must Do When Invoked
If the user invoked `/graphify --help` or `/graphify -h` (with no other arguments), print the contents of the `## Usage` section above verbatim and stop. Do not run any commands, do not detect files, do not default the path to `.`. Just print the Usage block and return.
**Fast path — existing graph:** Before doing anything else, check whether `graphify-out/graph.json` exists. The expected location is `graphify-out/graph.json` relative to the **current working directory** (i.e. the project root where you are running commands). If it exists AND the user's request is a natural-language question about the codebase (e.g. "How does X work?", "What calls Y?", "Trace the data flow through Z") and NOT an explicit rebuild command (`--update`, `--cluster-only`, or a bare path/URL that implies fresh extraction): **skip Steps 15 entirely and jump straight to `## For /graphify query`.** Run `graphify query "<question>"` immediately. Do not run detect. Do not check corpus size. Do not ask the user to narrow. The graph is already built — use it.
If no path was given, use `.` (current directory). Do not ask the user for a path.
If the path argument starts with `https://github.com/` or `http://github.com/`, treat it as a GitHub URL - run Step 0 before anything else, then continue with the resolved local path.
Follow these steps in order. Do not skip steps.
### Step 0 - GitHub repos and multi-path merge (only if a URL or several paths)
Only when the path is one or more `https://github.com/...` URLs, or several local subfolders to merge. See `references/github-and-merge.md` for the clone, cross-repo merge, and monorepo flow, then continue with the resolved local path. A plain local path skips this step.
### Step 1 - Ensure graphify is installed
```bash
# Detect the correct Python interpreter (handles uv tool, pipx, venv, system installs)
PYTHON=""
GRAPHIFY_BIN=$(which graphify 2>/dev/null)
# 1. uv tool installs — most reliable on modern Mac/Linux
if [ -z "$PYTHON" ] && command -v uv >/dev/null 2>&1; then
_UV_PY=$(uv tool run --from graphifyy python -c "import sys; print(sys.executable)" 2>/dev/null)
if [ -n "$_UV_PY" ]; then PYTHON="$_UV_PY"; fi
fi
# 2. Read shebang from graphify binary (pipx and direct pip installs)
if [ -z "$PYTHON" ] && [ -n "$GRAPHIFY_BIN" ]; then
_SHEBANG=$(head -1 "$GRAPHIFY_BIN" | tr -d '#!')
case "$_SHEBANG" in
*[!a-zA-Z0-9/_.@-]*) ;;
*) "$_SHEBANG" -c "import graphify" 2>/dev/null && PYTHON="$_SHEBANG" ;;
esac
fi
# 3. Fall back to python3
if [ -z "$PYTHON" ]; then PYTHON="python3"; fi
if ! "$PYTHON" -c "import graphify" 2>/dev/null; then
if command -v uv >/dev/null 2>&1; then
uv tool install --upgrade graphifyy -q 2>&1 | tail -3
_UV_PY=$(uv tool run --from graphifyy python -c "import sys; print(sys.executable)" 2>/dev/null)
if [ -n "$_UV_PY" ]; then PYTHON="$_UV_PY"; fi
else
"$PYTHON" -m pip install graphifyy -q 2>/dev/null \
|| "$PYTHON" -m pip install graphifyy -q --break-system-packages 2>&1 | tail -3
fi
fi
# Write interpreter path for all subsequent steps (persists across invocations)
mkdir -p graphify-out
"$PYTHON" -c "import sys; open('graphify-out/.graphify_python', 'w', encoding='utf-8').write(sys.executable)"
# Save scan root so `graphify update` (no args) knows where to look next time
echo "$(cd INPUT_PATH && pwd)" > graphify-out/.graphify_root
```
If the import succeeds, print nothing and move straight to Step 2.
**In every subsequent bash block, replace `python3` with `$(cat graphify-out/.graphify_python)` to use the correct interpreter.**
### Step 2 - Detect files
```bash
$(cat graphify-out/.graphify_python) -c "
import json
from graphify.detect import detect
from pathlib import Path
result = detect(Path('INPUT_PATH'))
print(json.dumps(result, ensure_ascii=False))
" > graphify-out/.graphify_detect.json
```
Replace INPUT_PATH with the actual path the user provided. Do NOT cat or print the JSON - read it silently and present a clean summary instead:
```
Corpus: X files · ~Y words
code: N files (.py .ts .go ...)
docs: N files (.md .txt ...)
papers: N files (.pdf ...)
images: N files
video: N files (.mp4 .mp3 ...)
```
Omit any category with 0 files from the summary.
Then act on it:
- If `total_files` is 0: stop with "No supported files found in [path]."
- If `skipped_sensitive` is non-empty: report the count and list the skipped file names, so a wrongly-flagged source or doc is visible and can be renamed or moved (#2106).
- If `total_words` > 2,000,000 OR `total_files` > 500: show the warning. Then compute the top 5 first-level subdirectories by file count:
- Read `scan_root` from the detect JSON (always an absolute path to the resolved INPUT_PATH).
- Concatenate all file lists across all types (`code`, `document`, `paper`, `image`, `video`).
- Filter out any path that starts with `scan_root + "/graphify-out/"` to exclude converted sidecars.
- For each file, strip the `scan_root` prefix and take the first path component. Files directly in `scan_root` with no subdirectory count as `(root)`.
- If all files are in `(root)` with no subdirectories, do not ask to narrow — no subfolders exist. Instead suggest `--no-cluster` to skip the expensive clustering step and proceed.
- Otherwise rank by count, show the top 5 with file counts, then ask which subfolder to run on. Wait for the user's answer before proceeding.
- Otherwise: proceed directly to Step 2.5 if video files were detected, or Step 3 if not.
### Step 2.5 - Video and audio (only if video files detected)
Skip this step entirely if `detect` returned zero `video` files. When the corpus has video or audio, see `references/transcribe.md` to transcribe them to text first, then treat the transcripts as doc files in Step 3.
### Step 3 - Extract entities and relationships
**Before starting:** note whether `--mode deep` was given. You must pass `DEEP_MODE=true` to every subagent in Step B2 if it was. Track this from the original invocation - do not lose it.
This step has two parts: **structural extraction** (deterministic, free) and **semantic extraction** (LLM, costs tokens).
> **graphify needs no API key. Never ask the user for one, and never block on one.** Code is extracted structurally (AST) with no LLM and no key at all — a code-only corpus (the common `/graphify .` on a repo) skips semantic extraction entirely, so it needs nothing here: go straight to Part A and skip Part B. Semantic extraction (only for docs, papers, and images) uses Gemini **only if** `GEMINI_API_KEY`/`GOOGLE_API_KEY` is already set; otherwise the host agent itself is the LLM. graphify does **not** read `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or any other provider key. If you catch yourself about to prompt for, wait on, or stop because of a missing API key, that is a misread of this skill — proceed without one.
**Before semantic extraction:** check whether `GEMINI_API_KEY` or `GOOGLE_API_KEY` is set. If neither is set, print this one-liner to the user:
> Tip: set `GEMINI_API_KEY` or `GOOGLE_API_KEY` to use Gemini for semantic extraction (`pip install 'graphifyy[gemini]'`).
Print it once, then continue — do not wait for the user to supply a key. If `GEMINI_API_KEY` or `GOOGLE_API_KEY` IS set, use `graphify.llm.extract_corpus_parallel(files, backend="gemini")` for semantic extraction instead of dispatching subagents. The default Gemini model is `gemini-3-flash-preview`; set `GRAPHIFY_GEMINI_MODEL` or pass `--model` in headless CLI flows to override it.
> **No other API keys are read.** When `GEMINI_API_KEY`/`GOOGLE_API_KEY` are unset, semantic extraction falls to the host agent itself — the running session is the LLM. On a host that dispatches subagents (e.g. Claude Code), dispatch them as written in Part B. On a host that runs the CLI directly in a terminal and cannot dispatch subagents, do not stall: a code-only corpus has no semantic work, so write the empty semantic file (Part B "Fast path") and continue to Part C; for a corpus with docs/papers/images, either set a Gemini key or extract those inline yourself, but in no case prompt for `ANTHROPIC_API_KEY` — that prompt is a misread of this skill.
**Run Part A (AST) and Part B (semantic) in parallel. Dispatch all semantic subagents AND start AST extraction in the same message. Both can run simultaneously since they operate on different file types. Merge results in Part C as before.**
Note: Parallelizing AST + semantic saves 5-15s on large corpora. AST is deterministic and fast; start it while subagents are processing docs/papers.
#### Part A - Structural extraction for code files
For any code files detected, run AST extraction in parallel with Part B subagents:
```bash
$(cat graphify-out/.graphify_python) -c "
import sys, json
from graphify.extract import collect_files, extract
from pathlib import Path
import json
code_files = []
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
for f in detect.get('files', {}).get('code', []):
code_files.extend(collect_files(Path(f)) if Path(f).is_dir() else [Path(f)])
if code_files:
result = extract(code_files, cache_root=Path('INPUT_PATH'))
Path('graphify-out/.graphify_ast.json').write_text(json.dumps(result, indent=2, ensure_ascii=False), encoding=\"utf-8\")
print(f'AST: {len(result[\"nodes\"])} nodes, {len(result[\"edges\"])} edges')
else:
Path('graphify-out/.graphify_ast.json').write_text(json.dumps({'nodes':[],'edges':[],'input_tokens':0,'output_tokens':0}, ensure_ascii=False), encoding=\"utf-8\")
print('No code files - skipping AST extraction')
"
```
#### Part B - Semantic extraction (parallel subagents)
**Fast path:** If detection found zero docs, papers, and images (code-only corpus), skip Part B entirely and go straight to Part C. AST handles code - there is nothing for semantic subagents to do. **First write an empty semantic file** so Part C's merge has its input (it reads `.graphify_semantic.json` unconditionally; without this a code-only run hits `FileNotFoundError`):
```bash
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
Path('graphify-out/.graphify_semantic.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8')
"
```
**MANDATORY: You MUST use the Agent tool here. Reading files yourself one-by-one is forbidden - it is 5-10x slower. If you do not use the Agent tool you are doing this wrong.**
Before dispatching subagents, print a timing estimate:
- Load `total_words` and file counts from `graphify-out/.graphify_detect.json`
- Estimate agents needed: `ceil(uncached_non_code_files / 22)` (chunk size is 20-25)
- Estimate time: ~45s per agent batch (they run in parallel, so total ≈ 45s × ceil(agents/parallel_limit))
- Print: "Semantic extraction: ~N files → X agents, estimated ~Ys"
**Step B0 - Check extraction cache first**
Before dispatching any subagents, check which files already have cached extraction results:
SPEC_PATH below is the **absolute** path of the `references/extraction-spec.md` that ships beside this SKILL.md — the same file Step B2 loads and hands to every subagent. It is the extraction prompt, so cache entries are attributed to it: when a graphify upgrade changes the prompt, entries produced by the old one are re-extracted instead of replayed, and unchanged prompts keep their entries (#1939). Substitute the real path in both Step B0 and Step B3 — pass the same one to each, and do not drop the argument.
```bash
$(cat graphify-out/.graphify_python) -c "
import json
from graphify.cache import check_semantic_cache
from pathlib import Path
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
# Only content files go to semantic extraction. Code is already covered structurally
# by the AST pass (Part A); flattening every category here makes subagents re-read
# every source file (#1392). Video is transcribed to a document in Step 2.5 first.
all_files = [f for cat in ('document', 'paper', 'image') for f in detect['files'].get(cat, [])]
cached_nodes, cached_edges, cached_hyperedges, uncached = check_semantic_cache(all_files, root='INPUT_PATH', prompt_file='SPEC_PATH')
# Always (re)write the cache file: write hits, else DELETE any leftover from a prior
# run so Part C never merges a stale .graphify_cached.json (#1392).
if cached_nodes or cached_edges or cached_hyperedges:
Path('graphify-out/.graphify_cached.json').write_text(json.dumps({'nodes': cached_nodes, 'edges': cached_edges, 'hyperedges': cached_hyperedges}, ensure_ascii=False), encoding=\"utf-8\")
else:
Path('graphify-out/.graphify_cached.json').unlink(missing_ok=True)
Path('graphify-out/.graphify_uncached.txt').write_text('\n'.join(uncached), encoding=\"utf-8\")
print(f'Cache: {len(all_files)-len(uncached)} files hit, {len(uncached)} files need extraction')
"
```
Only dispatch subagents for files listed in `graphify-out/.graphify_uncached.txt`. If all files are cached, skip to Part C directly.
**Step B1 - Split into chunks**
Load files from `graphify-out/.graphify_uncached.txt`. Split into chunks of 20-25 files each. Each image gets its own chunk (vision needs separate context). When splitting, group files from the same directory together so related artifacts land in the same chunk and cross-file relationships are more likely to be extracted.
**Step B2 - Dispatch ALL subagents in a single message (OpenCode)**
> **OpenCode platform:** Uses `@mention` dispatch instead of the Agent tool. All mentions in a single message run in parallel.
Dispatch one `@mention` per chunk — ALL in the same response:
```
@agent Chunk CHUNK_NUM of TOTAL_CHUNKS: [extraction prompt with FILE_LIST, CHUNK_NUM, TOTAL_CHUNKS, DEEP_MODE substituted]
@agent Chunk 2 of TOTAL_CHUNKS: [next chunk]
```
Wait for all agents to return. Parse each response as JSON. Accumulate nodes/edges/hyperedges across all results and write to `graphify-out/.graphify_semantic_new.json`. If the `@agent` path cannot write chunk files, fall back to the serial path that writes each `graphify-out/.graphify_chunk_NN.json` before merge.
Subagent prompt template:
See `references/extraction-spec.md` for the exact subagent prompt (JSON schema, node-ID rules, confidence rubric, hyperedge, and vision rules). Load it only here, only when at least one chunk holds a doc, paper, or image; a pure-code corpus has skipped Part B and never reads it. Pass each agent that prompt verbatim with FILE_LIST, CHUNK_NUM, TOTAL_CHUNKS, and DEEP_MODE substituted.
**Step B3 - Collect, cache, and merge**
Wait for all subagents. For each result:
- Check that `graphify-out/.graphify_chunk_NN.json` exists on disk — this is the success signal
- If the file exists and contains valid JSON with `nodes` and `edges`, include it and save to cache
- If the file is missing, the subagent was likely dispatched as read-only (Explore type) — print a warning: "chunk N missing from disk — subagent may have been read-only. Re-run with general-purpose agent." Do not silently skip.
- If a subagent failed or returned invalid JSON, print a warning and skip that chunk - do not abort
If more than half the chunks failed or are missing, stop and tell the user to re-run and ensure `subagent_type="general-purpose"` is used.
Merge all chunk files into `.graphify_semantic_new.json`. **After each Agent call completes, read the real token counts from the Agent tool result's `usage` field and write them back into the chunk JSON before merging** — the chunk JSON itself always has placeholder zeros. Then run:
```bash
$(cat graphify-out/.graphify_python) -c "
import json, glob
from pathlib import Path
chunks = sorted(glob.glob('graphify-out/.graphify_chunk_*.json'))
all_nodes, all_edges, all_hyperedges = [], [], []
total_in, total_out = 0, 0
for c in chunks:
d = json.loads(Path(c).read_text(encoding=\"utf-8\"))
all_nodes += d.get('nodes', [])
all_edges += d.get('edges', [])
all_hyperedges += d.get('hyperedges', [])
total_in += d.get('input_tokens', 0)
total_out += d.get('output_tokens', 0)
Path('graphify-out/.graphify_semantic_new.json').write_text(json.dumps({
'nodes': all_nodes, 'edges': all_edges, 'hyperedges': all_hyperedges,
'input_tokens': total_in, 'output_tokens': total_out,
}, indent=2, ensure_ascii=False), encoding=\"utf-8\")
print(f'Merged {len(chunks)} chunks: {total_in:,} in / {total_out:,} out tokens')
"
```
Save new results to cache. Pass the same SPEC_PATH as Step B0 — it stamps each entry with the prompt that produced it, and a write under a different prompt than the read lands where the next run won't look (#1939):
```bash
$(cat graphify-out/.graphify_python) -c "
import json
from graphify.cache import save_semantic_cache
from pathlib import Path
new = json.loads(Path('graphify-out/.graphify_semantic_new.json').read_text(encoding=\"utf-8\")) if Path('graphify-out/.graphify_semantic_new.json').exists() else {'nodes':[],'edges':[],'hyperedges':[]}
uncached = [line for line in Path('graphify-out/.graphify_uncached.txt').read_text(encoding=\"utf-8\").splitlines() if line]
saved = save_semantic_cache(new.get('nodes', []), new.get('edges', []), new.get('hyperedges', []), root='INPUT_PATH', allowed_source_files=uncached, prompt_file='SPEC_PATH')
print(f'Cached {saved} files')
"
```
Merge cached + new results into `graphify-out/.graphify_semantic.json`:
```bash
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
cached = json.loads(Path('graphify-out/.graphify_cached.json').read_text(encoding=\"utf-8\")) if Path('graphify-out/.graphify_cached.json').exists() else {'nodes':[],'edges':[],'hyperedges':[]}
new = json.loads(Path('graphify-out/.graphify_semantic_new.json').read_text(encoding=\"utf-8\")) if Path('graphify-out/.graphify_semantic_new.json').exists() else {'nodes':[],'edges':[],'hyperedges':[]}
all_nodes = cached['nodes'] + new.get('nodes', [])
all_edges = cached['edges'] + new.get('edges', [])
all_hyperedges = cached.get('hyperedges', []) + new.get('hyperedges', [])
seen = set()
deduped = []
for n in all_nodes:
if n['id'] not in seen:
seen.add(n['id'])
deduped.append(n)
merged = {
'nodes': deduped,
'edges': all_edges,
'hyperedges': all_hyperedges,
'input_tokens': new.get('input_tokens', 0),
'output_tokens': new.get('output_tokens', 0),
}
Path('graphify-out/.graphify_semantic.json').write_text(json.dumps(merged, indent=2, ensure_ascii=False), encoding=\"utf-8\")
print(f'Extraction complete - {len(deduped)} nodes, {len(all_edges)} edges ({len(cached[\"nodes\"])} from cache, {len(new.get(\"nodes\",[]))} new)')
"
```
Clean up temp files: `rm -f graphify-out/.graphify_cached.json graphify-out/.graphify_uncached.txt graphify-out/.graphify_semantic_new.json`
#### Part C - Merge AST + semantic into final extraction
```bash
$(cat graphify-out/.graphify_python) -c "
import sys, json
from pathlib import Path
ast = json.loads(Path('graphify-out/.graphify_ast.json').read_text(encoding=\"utf-8\"))
sem = json.loads(Path('graphify-out/.graphify_semantic.json').read_text(encoding=\"utf-8\"))
# Merge: AST nodes first, semantic nodes deduplicated by id
seen = {n['id'] for n in ast['nodes']}
merged_nodes = list(ast['nodes'])
for n in sem['nodes']:
if n['id'] not in seen:
merged_nodes.append(n)
seen.add(n['id'])
merged_edges = ast['edges'] + sem['edges']
merged_hyperedges = sem.get('hyperedges', [])
merged = {
'nodes': merged_nodes,
'edges': merged_edges,
'hyperedges': merged_hyperedges,
'input_tokens': sem.get('input_tokens', 0),
'output_tokens': sem.get('output_tokens', 0),
}
Path('graphify-out/.graphify_extract.json').write_text(json.dumps(merged, indent=2, ensure_ascii=False), encoding=\"utf-8\")
total = len(merged_nodes)
edges = len(merged_edges)
print(f'Merged: {total} nodes, {edges} edges ({len(ast[\"nodes\"])} AST + {len(sem[\"nodes\"])} semantic)')
"
```
### Step 4 - Build graph, cluster, analyze, generate outputs
**Before starting:** the code blocks below pass `directed=IS_DIRECTED` to `build_from_json()`. Replace `IS_DIRECTED` with `True` if `--directed` was given (builds a `DiGraph` preserving edge direction source→target), otherwise `False` (the default undirected `Graph`). Substitute it the same way you substitute `INPUT_PATH` — do not leave the literal `IS_DIRECTED` in the code.
```bash
mkdir -p graphify-out
$(cat graphify-out/.graphify_python) -c "
import sys, json
from graphify.build import build_from_json
from graphify.cluster import cluster, score_all
from graphify.analyze import god_nodes, surprising_connections, suggest_questions
from graphify.report import generate
from graphify.export import to_json
from pathlib import Path
extraction = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
detection = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
# root= mirrors the --update runbook (#1361): relativize source_file to the same
# base so the full build and incremental --update never drift apart on re-extract.
G = build_from_json(extraction, root='INPUT_PATH', directed=IS_DIRECTED)
# Guard BEFORE any write: an empty extraction must not clobber a good graph.json /
# GRAPH_REPORT.md / analysis sidecar. Check immediately after build (#1392).
if G.number_of_nodes() == 0:
print('ERROR: Graph is empty - extraction produced no nodes.')
print('Possible causes: all files were skipped, binary-only corpus, or extraction failed.')
raise SystemExit(1)
communities = cluster(G)
cohesion = score_all(G, communities)
tokens = {'input': extraction.get('input_tokens', 0), 'output': extraction.get('output_tokens', 0)}
gods = god_nodes(G)
surprises = surprising_connections(G, communities)
labels = {cid: 'Community ' + str(cid) for cid in communities}
# Placeholder questions - regenerated with real labels in Step 5
questions = suggest_questions(G, communities, labels)
# Export FIRST and honor the #479 shrink-guard: to_json returns False (writing
# nothing) when the new graph is smaller than the existing graph.json. Only write
# GRAPH_REPORT.md + the analysis sidecar when the graph was actually written, so
# they never describe a graph that graph.json doesn't contain (#1392).
wrote = to_json(G, communities, 'graphify-out/graph.json')
if not wrote:
print('ERROR: refused to shrink graphify-out/graph.json (existing graph has more nodes; #479).')
print('If this shrink is intentional (you deleted files), re-run a full build with --force.')
raise SystemExit(1)
report = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, 'INPUT_PATH', suggested_questions=questions)
Path('graphify-out/GRAPH_REPORT.md').write_text(report, encoding=\"utf-8\")
analysis = {
'communities': {str(k): v for k, v in communities.items()},
'cohesion': {str(k): v for k, v in cohesion.items()},
'gods': gods,
'surprises': surprises,
'questions': questions,
}
Path('graphify-out/.graphify_analysis.json').write_text(json.dumps(analysis, indent=2, ensure_ascii=False), encoding=\"utf-8\")
print(f'Graph: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges, {len(communities)} communities')
"
```
If this step prints `ERROR: Graph is empty`, stop and tell the user what happened - do not proceed to labeling or visualization.
Replace INPUT_PATH with the actual path.
### Step 4.5 - Graph health check (read-only integrity gate)
A non-destructive diagnostic on the extraction, before labeling. It surfaces edge collapse, dangling/missing endpoints, and self-loops — the silent-corruption modes of incremental updates and AST/LLM id mismatches. Read-only; never aborts.
```bash
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
from graphify.diagnostics import diagnose_extraction, format_diagnostic_report
extraction = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
summary = diagnose_extraction(extraction, directed=IS_DIRECTED, root='INPUT_PATH')
print(format_diagnostic_report(summary))
flags = [f'{summary[k]} {label}' for k, label in (
('dangling_endpoint_edges', 'dangling-endpoint edges'),
('missing_endpoint_edges', 'missing-endpoint edges'),
('self_loop_edges', 'self-loop edges'),
('directed_same_endpoint_collapsed_edges', 'collapsed (directed) edges'),
('undirected_same_endpoint_collapsed_edges', 'collapsed (undirected) edges'),
) if summary.get(k, 0)]
print('GRAPH HEALTH WARNING: ' + '; '.join(flags) + ' - graph may be incomplete/corrupt.' if flags else 'Graph health: OK (no dangling/missing/collapsed edges).')
"
```
Substitute `IS_DIRECTED` and `INPUT_PATH` as in Step 4. If a `GRAPH HEALTH WARNING` prints, surface it in the final summary (do not abort — the graph is still usable, but the integrity issue must be visible, per the Honesty Rules).
### Step 5 - Label communities
Read `graphify-out/.graphify_analysis.json`. For each community key, look at its node labels and write a 2-5 word plain-language name (e.g. "Attention Mechanism", "Training Pipeline", "Data Loading").
Then regenerate the report and save the labels for the visualizer:
```bash
$(cat graphify-out/.graphify_python) -c "
import sys, json
from graphify.build import build_from_json
from graphify.cluster import score_all
from graphify.analyze import god_nodes, surprising_connections, suggest_questions
from graphify.report import generate
from pathlib import Path
extraction = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
detection = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
analysis = json.loads(Path('graphify-out/.graphify_analysis.json').read_text(encoding=\"utf-8\"))
# root= as in Step 4 / the --update runbook (#1361) — same base for node-key parity.
G = build_from_json(extraction, root='INPUT_PATH', directed=IS_DIRECTED)
communities = {int(k): v for k, v in analysis['communities'].items()}
cohesion = {int(k): v for k, v in analysis['cohesion'].items()}
tokens = {'input': extraction.get('input_tokens', 0), 'output': extraction.get('output_tokens', 0)}
# LABELS - replace these with the names you chose above
labels = LABELS_DICT
# Regenerate questions with real community labels (labels affect question phrasing)
questions = suggest_questions(G, communities, labels)
report = generate(G, communities, cohesion, labels, analysis['gods'], analysis['surprises'], detection, tokens, 'INPUT_PATH', suggested_questions=questions)
Path('graphify-out/GRAPH_REPORT.md').write_text(report, encoding=\"utf-8\")
Path('graphify-out/.graphify_labels.json').write_text(json.dumps({str(k): v for k, v in labels.items()}, ensure_ascii=False), encoding=\"utf-8\")
print('Report updated with community labels')
"
```
Replace `LABELS_DICT` with the actual dict you constructed (e.g. `{0: "Attention Mechanism", 1: "Training Pipeline"}`).
Replace INPUT_PATH with the actual path.
### Step 6 - Generate Obsidian vault (opt-in) + HTML
**Generate HTML always** (unless `--no-viz`). **Obsidian vault only if `--obsidian` was explicitly given** — skip it otherwise, it generates one file per node.
If `--obsidian` was given:
- If `--obsidian-dir <path>` was also given, pass it via `--dir`. Otherwise defaults to `graphify-out/obsidian`.
```bash
graphify export obsidian
# or with custom dir: graphify export obsidian --dir ~/vaults/my-project
```
Generate the HTML graph (always, unless `--no-viz`):
```bash
graphify export html # auto-aggregates to community view if graph > 5000 nodes
# or: graphify export html --no-viz
```
### Steps 6b-8 - Wiki, Neo4j, FalkorDB, SVG, GraphML, MCP, benchmark (only on their flags)
These run only when their flag is present (`--wiki`, `--neo4j`/`--neo4j-push`, `--falkordb`/`--falkordb-push`, `--svg`, `--graphml`, `--mcp`) or, for the token-reduction benchmark, when `total_words` exceeds 5,000. A default run with no export flags skips all of them. See `references/exports.md` for each one. Run any `--wiki` export before Step 9 cleanup so `.graphify_labels.json` is still available.
---
### Step 9 - Save manifest, update cost tracker, clean up, and report
```bash
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
from datetime import datetime, timezone
from graphify.detect import save_manifest
# Save manifest for --update
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
# In --update mode, 'all_files' carries the full corpus; 'files' is the changed
# subset. Full-rebuild mode populates only 'files', so the fallback handles that.
# root= relativizes the manifest keys to the scan root (same base as the build),
# so the on-disk manifest is portable across clones/machines and a later --update
# matches cached files instead of missing every one (#1417).
#
# Only stamp semantic files (docs/papers/images) that ACTUALLY produced output:
# a detected file whose chunk failed or was omitted must stay unstamped so the
# next --update re-queues it, otherwise it is marked done and its content is lost
# forever (#2015). This mirrors the library extract path exactly
# (cli._stamped_manifest_files + clear_semantic + scan_corpus); do not stamp the
# raw corpus. Code files are always stamped (AST is deterministic); only semantic
# types are gated on output.
from graphify.cli import _stamped_manifest_files
_corpus = detect.get('all_files') or detect['files']
_manifest_files = _stamped_manifest_files(_corpus, extract, Path('INPUT_PATH'))
# Files dispatched this run (the changed subset) but NOT stamped above still carry
# a stale semantic_hash from a prior run; clear it so detect_incremental re-queues
# them instead of reading them as unchanged (#1948).
_sem_types = ('document', 'paper', 'image')
_dispatched = {f for t, fl in detect['files'].items() if t in _sem_types for f in fl}
_stamped = {f for fl in _manifest_files.values() for f in fl}
_cleared = _dispatched - _stamped
# scan_corpus = the RAW full corpus (not the stamp-filtered subset) so in-root
# files newly excluded since last run are dropped rather than masquerading as
# deletions; untouched files' prior rows are still preserved (#1908).
_scan = {f for fl in _corpus.values() for f in fl}
save_manifest(_manifest_files, root='INPUT_PATH', scan_corpus=_scan, clear_semantic=_cleared or None)
# Update cumulative cost tracker
input_tok = extract.get('input_tokens', 0)
output_tok = extract.get('output_tokens', 0)
cost_path = Path('graphify-out/cost.json')
if cost_path.exists():
cost = json.loads(cost_path.read_text(encoding=\"utf-8\"))
else:
cost = {'runs': [], 'total_input_tokens': 0, 'total_output_tokens': 0}
cost['runs'].append({
'date': datetime.now(timezone.utc).isoformat(),
'input_tokens': input_tok,
'output_tokens': output_tok,
'files': detect.get('total_files', 0),
})
cost['total_input_tokens'] += input_tok
cost['total_output_tokens'] += output_tok
cost_path.write_text(json.dumps(cost, indent=2, ensure_ascii=False), encoding=\"utf-8\")
print(f'This run: {input_tok:,} input tokens, {output_tok:,} output tokens')
print(f'All time: {cost[\"total_input_tokens\"]:,} input, {cost[\"total_output_tokens\"]:,} output ({len(cost[\"runs\"])} runs)')
"
rm -f graphify-out/.graphify_detect.json graphify-out/.graphify_extract.json graphify-out/.graphify_ast.json graphify-out/.graphify_semantic.json graphify-out/.graphify_analysis.json
find graphify-out -maxdepth 1 -name '.graphify_chunk_*.json' -delete 2>/dev/null
rm -f graphify-out/.needs_update 2>/dev/null || true
```
Replace INPUT_PATH with the actual path (same value used in Steps 4-5) so the manifest is relativized to the scan root.
Tell the user (omit the obsidian line unless --obsidian was given):
```
Graph complete. Outputs in PATH_TO_DIR/graphify-out/
graph.html - interactive graph, open in browser
GRAPH_REPORT.md - audit report
graph.json - raw graph data
obsidian/ - Obsidian vault (only if --obsidian was given)
```
If graphify saved you time, consider supporting it: https://github.com/sponsors/safishamsi
Replace PATH_TO_DIR with the actual absolute path of the directory that was processed.
Then paste these sections from GRAPH_REPORT.md directly into the chat:
- God Nodes
- Surprising Connections
- Suggested Questions
Do NOT paste the full report - just those three sections. Keep it concise.
Then immediately offer to explore. Pick the single most interesting suggested question from the report - the one that crosses the most community boundaries or has the most surprising bridge node - and ask:
> "The most interesting question this graph can answer: **[question]**. Want me to trace it?"
If the user says yes, run `/graphify query "[question]"` on the graph and walk them through the answer using the graph structure - which nodes connect, which community boundaries get crossed, what the path reveals. Keep going as long as they want to explore. Each answer should end with a natural follow-up ("this connects to X - want to go deeper?") so the session feels like navigation, not a one-shot report.
The graph is the map. Your job after the pipeline is to be the guide.
---
## Interpreter guard for subcommands
Before running any subcommand below (`--update`, `--cluster-only`, `query`, `path`, `explain`, `add`), check that `.graphify_python` exists. If it's missing (e.g. user deleted `graphify-out/`), re-resolve the interpreter first:
```bash
if [ ! -f graphify-out/.graphify_python ]; then
GRAPHIFY_BIN=$(which graphify 2>/dev/null)
if [ -n "$GRAPHIFY_BIN" ]; then
PYTHON=$(head -1 "$GRAPHIFY_BIN" | tr -d '#!')
case "$PYTHON" in *[!a-zA-Z0-9/_.@-]*) PYTHON="python3" ;; esac
else
PYTHON="python3"
fi
mkdir -p graphify-out
"$PYTHON" -c "import sys; open('graphify-out/.graphify_python', 'w', encoding='utf-8').write(sys.executable)"
fi
```
## For --update and --cluster-only
Both are non-default subcommands. `--update` re-extracts only new or changed files; `--cluster-only` reruns clustering on the existing graph. See `references/update.md` for both flows.
---
## For /graphify query
When `graphify-out/graph.json` already exists and the user asks a question about the corpus, answer from the graph rather than rebuilding it:
```bash
graphify query "<question>"
```
Before traversal, expand the question against the graph's own vocabulary so a wording mismatch does not collapse the answer to noise. If the `graphify query` CLI is unavailable, fall back to an inline NetworkX traversal of `graphify-out/graph.json`. Answer using only what the graph output contains, and quote `source_location` when citing a specific fact. For that vocab-expansion step, the BFS/DFS traversal modes, the `--budget` cap, the NetworkX fallback, `save-result` feedback, and the `/graphify path` and `/graphify explain` flows, see `references/query.md`.
---
## For /graphify add and --watch
Neither is part of the default build. When the user runs `/graphify add <url>` to fetch a URL into the corpus, or passes `--watch` to auto-rebuild on file changes, see `references/add-watch.md`.
---
## For the commit hook and native CLAUDE.md integration
When the user asks to install the post-commit auto-rebuild hook or wire graphify into a project's CLAUDE.md, see `references/hooks.md`.
---
## Honesty Rules
- Never invent an edge. If unsure, use AMBIGUOUS.
- Never skip the corpus check warning.
- Always show token cost in the report.
- Never hide cohesion scores behind symbols - show the raw number.
- Never run HTML viz on a graph with more than 5,000 nodes without warning the user.

View File

@@ -1,56 +0,0 @@
# graphify reference: add a URL and watch a folder
Load this when the user ran `/graphify add <url>` or passed `--watch`. Neither is part of the default build.
## For /graphify add
Fetch a URL and add it to the corpus, then update the graph.
```bash
$(cat graphify-out/.graphify_python) -c "
import sys
from graphify.ingest import ingest
from pathlib import Path
try:
out = ingest('URL', Path('./raw'), author='AUTHOR', contributor='CONTRIBUTOR')
print(f'Saved to {out}')
except ValueError as e:
print(f'error: {e}', file=sys.stderr)
sys.exit(1)
except RuntimeError as e:
print(f'error: {e}', file=sys.stderr)
sys.exit(1)
"
```
Replace `URL` with the actual URL, `AUTHOR` with the user's name if provided, `CONTRIBUTOR` likewise. If the command exits with an error, tell the user what went wrong - do not silently continue. After a successful save, automatically run the `--update` pipeline on `./raw` to merge the new file into the existing graph.
Supported URL types (auto-detected):
- YouTube / any video URL → audio downloaded via yt-dlp, transcribed to `.txt` on next run (requires `pip install 'graphifyy[video]'`)
- Twitter/X → fetched via oEmbed, saved as `.md` with tweet text and author
- arXiv → abstract + metadata saved as `.md`
- PDF → downloaded as `.pdf`
- Images (.png/.jpg/.webp) → downloaded, Claude vision extracts on next run
- Any webpage → converted to markdown via html2text
---
## For --watch
Start a background watcher that monitors a folder and auto-updates the graph when files change.
```bash
$(cat graphify-out/.graphify_python) -m graphify.watch INPUT_PATH --debounce 3
```
Replace INPUT_PATH with the folder to watch. Behavior depends on what changed:
- **Code files only (.py, .ts, .go, etc.):** re-runs AST extraction + rebuild + cluster immediately, no LLM needed. `graph.json` and `GRAPH_REPORT.md` are updated automatically.
- **Docs, papers, or images:** writes a `graphify-out/needs_update` flag and prints a notification to run `/graphify --update` (LLM semantic re-extraction required).
Debounce (default 3s): waits until file activity stops before triggering, so a wave of parallel agent writes doesn't trigger a rebuild per file.
Press Ctrl+C to stop.
For agentic workflows: run `--watch` in a background terminal. Code changes from agent waves are picked up automatically between waves. If agents are also writing docs or notes, you'll need a manual `/graphify --update` after those waves.

View File

@@ -1,87 +0,0 @@
# graphify reference: extra exports and benchmark
Load this when the user passed one of the export flags (`--wiki`, `--neo4j`, `--neo4j-push`, `--falkordb`, `--falkordb-push`, `--svg`, `--graphml`, `--mcp`), or when the corpus is large enough for the token-reduction benchmark. Each step runs only for its own flag.
### Step 6b - Wiki (only if --wiki flag)
**Only run this step if `--wiki` was explicitly given in the original command.**
Run this before Step 9 (cleanup) so `.graphify_labels.json` is still available.
```bash
graphify export wiki
```
### Step 7 - Neo4j export (only if --neo4j or --neo4j-push flag)
**If `--neo4j`** - generate a Cypher file for manual import:
```bash
graphify export neo4j
```
**If `--neo4j-push <uri>`** - push directly to a running Neo4j instance. Ask the user for credentials if not provided:
```bash
graphify export neo4j --push bolt://localhost:7687 --user neo4j --password PASSWORD
```
Default URI is `bolt://localhost:7687`, default user is `neo4j`. Uses MERGE - safe to re-run without creating duplicates.
### Step 7a - FalkorDB export (only if --falkordb or --falkordb-push flag)
**If `--falkordb`** - generate a Cypher file. The statements are OpenCypher, but FalkorDB's `GRAPH.QUERY` runs one statement at a time (no bulk script import like Neo4j's `cypher-shell`), so prefer `--falkordb-push` to load a graph. Use this only when you want the portable `cypher.txt` artifact:
```bash
graphify export falkordb
```
**If `--falkordb-push <uri>`** - push directly to a running FalkorDB instance. Credentials are optional; ask the user only if the instance requires auth:
```bash
graphify export falkordb --push falkordb://localhost:6379
```
Default URI is `falkordb://localhost:6379` (the scheme is informational - `redis://` or a bare `host:port` work too), auth is optional, and the target graph defaults to `graphify`. Uses MERGE - safe to re-run without creating duplicates.
### Step 7b - SVG export (only if --svg flag)
```bash
graphify export svg
```
### Step 7c - GraphML export (only if --graphml flag)
```bash
graphify export graphml
```
### Step 7d - MCP server (only if --mcp flag)
```bash
$(cat graphify-out/.graphify_python) -m graphify.serve graphify-out/graph.json
```
This starts a stdio MCP server that exposes tools: `query_graph`, `get_node`, `get_neighbors`, `get_community`, `god_nodes`, `graph_stats`, `shortest_path`. Add to Claude Desktop or any MCP-compatible agent orchestrator so other agents can query the graph live.
To configure in Claude Desktop, add to `claude_desktop_config.json`. Claude Desktop can't run `$(...)`, and under `uv tool install` the system `python3` can't import graphify — so set `command` to the **absolute interpreter path** printed by `cat graphify-out/.graphify_python`:
```json
{
"mcpServers": {
"graphify": {
"command": "<absolute path from: cat graphify-out/.graphify_python>",
"args": ["-m", "graphify.serve", "/absolute/path/to/graphify-out/graph.json"]
}
}
}
```
### Step 8 - Token reduction benchmark (only if total_words > 5000)
If `total_words` from `graphify-out/.graphify_detect.json` is greater than 5,000, run:
```bash
graphify benchmark
```
Print the output directly in chat. If `total_words <= 5000`, skip silently - the graph value is structural clarity, not token compression, for small corpora.

View File

@@ -1,70 +0,0 @@
# graphify reference: extraction subagent prompt
Load this in Step 3 Part B when the corpus has at least one doc, paper, or image chunk. A pure-code corpus skips Part B and never reads this file. Each semantic subagent receives the prompt below verbatim (substitute FILE_LIST, CHUNK_NUM, TOTAL_CHUNKS, DEEP_MODE, and CHUNK_PATH).
```
You are a graphify extraction subagent. Read the files listed and extract a knowledge graph fragment.
Output ONLY valid JSON matching the schema below - no explanation, no markdown fences, no preamble.
Files (chunk CHUNK_NUM of TOTAL_CHUNKS):
FILE_LIST
Rules:
- EXTRACTED: relationship explicit in source (import, call, citation, "see §3.2")
- INFERRED: reasonable inference (shared data structure, implied dependency)
- AMBIGUOUS: uncertain - flag for review, do not omit
Code files: focus on semantic edges AST cannot find (call relationships, shared data, arch patterns).
Do not re-extract imports - AST already has those.
Doc/paper files: extract named concepts, entities, citations. For rationale (WHY decisions were made, trade-offs, design intent): store as a `rationale` attribute on the relevant concept node — do NOT create a separate rationale node or fragment node. Only create a node for something that is itself a named entity or concept. Use `file_type:"rationale"` for concept-like nodes (ideas, principles, mechanisms, design patterns). `file_type` MUST be one of exactly these six values: `code`, `document`, `paper`, `image`, `rationale`, `concept`. Any other value is invalid and will be rejected.
Code files: when adding `calls` edges, source MUST be the caller (the function/class doing the calling), target MUST be the callee. Never reverse this direction. `calls` edges MUST stay within one language: a Python function cannot `calls` a JS/TS/Go/Rust/Java symbol and vice versa — cross-language call edges are phantom artifacts, never emit them.
Image files: use vision to understand what the image IS - do not just OCR.
UI screenshot: layout patterns, design decisions, key elements, purpose.
Chart: metric, trend/insight, data source.
Tweet/post: claim as node, author, concepts mentioned.
Diagram: components and connections.
Research figure: what it demonstrates, method, result.
Handwritten/whiteboard: ideas and arrows, mark uncertain readings AMBIGUOUS.
DEEP_MODE (if --mode deep was given): be aggressive with INFERRED edges - indirect deps,
shared assumptions, latent couplings. Mark uncertain ones AMBIGUOUS instead of omitting.
Semantic similarity: if two concepts in this chunk solve the same problem or represent the same idea without any structural link (no import, no call, no citation), add a `semantically_similar_to` edge marked INFERRED with a confidence_score reflecting how similar they are (0.6-0.95). Examples:
- Two functions that both validate user input but never call each other
- A class in code and a concept in a paper that describe the same algorithm
- Two error types that handle the same failure mode differently
Only add these when the similarity is genuinely non-obvious and cross-cutting. Do not add them for trivially similar things.
Hyperedges: if 3 or more nodes clearly participate together in a shared concept, flow, or pattern that is not captured by pairwise edges alone, add a hyperedge to a top-level `hyperedges` array. Examples:
- All classes that implement a common protocol or interface
- All functions in an authentication flow (even if they don't all call each other)
- All concepts from a paper section that form one coherent idea
Use sparingly — only when the group relationship adds information beyond the pairwise edges. Maximum 3 hyperedges per chunk.
If a file has YAML frontmatter (--- ... ---), copy source_url, captured_at, author,
contributor onto every node from that file.
confidence_score is REQUIRED on every edge - never omit it, never use 0.5 as a default:
- EXTRACTED edges: confidence_score = 1.0 always
- INFERRED edges: pick exactly ONE value from this set — never 0.5:
0.95 direct structural evidence (shared data structure, named cross-file reference).
0.85 strong inference (clear functional alignment, no direct symbol link).
0.75 reasonable inference (shared problem domain + similar shape, requires interpretation).
0.65 weak inference (thematically related, no shape evidence).
0.55 speculative but plausible (surface-level co-occurrence only).
Models follow discrete rubrics better than continuous ranges; the bimodal
distribution observed in production (>50% at 0.5, >40% at 0.85+) shows the
range guidance is being collapsed to a binary. If no value above fits, mark
the edge AMBIGUOUS rather than picking 0.4 or below.
- AMBIGUOUS edges: 0.1-0.3
Node ID format: lowercase, only `[a-z0-9_]`, no dots or slashes. Format: `{stem}_{entity}` where stem is the **full repo-relative path with the extension dropped**, every path segment kept and joined with `_` (each segment lowercased with non-alphanumeric chars replaced by `_`), and entity is the symbol name similarly normalized. Use every directory level, not just the immediate parent — this keeps same-named files in different directories distinct. Examples: `src/auth/session.py` + `ValidateToken` → `src_auth_session_validatetoken`; `lib/utils/helpers.py` + `parse_url` → `lib_utils_helpers_parse_url`; `tests/test_foo.py` + `_helper` → `tests_test_foo_helper`; `docs/v1/api/README.md` + `getUser` → `docs_v1_api_readme_getuser`. Top-level files (no parent dir, e.g. `setup.py`) use just the filename stem: `setup_my_func`. This must match the ID the AST extractor generates — using just the filename (e.g., `session_validatetoken`) or only the immediate parent (e.g., `auth_session_validatetoken`) will create orphan ghost-duplicate nodes. If you are re-extracting a project built under the old immediate-parent format, the user should run `graphify extract --force` to rebuild cleanly. CRITICAL: never append chunk numbers, sequence numbers, or any suffix to an ID (no `_c1`, `_c2`, `_chunk2`, etc.). IDs must be deterministic from the label alone — the same entity must always produce the same ID regardless of which chunk processes it.
Generate the extraction JSON matching this schema exactly:
{"nodes":[{"id":"auth_session_validatetoken","label":"Human Readable Name","file_type":"code|document|paper|image|rationale|concept","source_file":"<FILE_LIST path verbatim>","source_location":null,"source_url":null,"captured_at":null,"author":null,"contributor":null}],"edges":[{"source":"node_id","target":"node_id","relation":"calls|implements|references|cites|conceptually_related_to|shares_data_with|semantically_similar_to|rationale_for","confidence":"EXTRACTED|INFERRED|AMBIGUOUS","confidence_score":1.0,"source_file":"<FILE_LIST path verbatim>","source_location":null,"weight":1.0}],"hyperedges":[{"id":"snake_case_id","label":"Human Readable Label","nodes":["node_id1","node_id2","node_id3"],"relation":"participate_in|implement|form","confidence":"EXTRACTED|INFERRED","confidence_score":0.75,"source_file":"<FILE_LIST path verbatim>"}],"input_tokens":0,"output_tokens":0}
source_file RULE (every node, edge, and hyperedge): set source_file to the path of the originating file EXACTLY as it appears in FILE_LIST — verbatim and absolute. Do NOT shorten to a basename, do NOT re-relativize, do NOT strip any directory prefix, and do NOT change separators (the engine canonicalizes separators and relativizes against the build root downstream). Copy the FILE_LIST entry character-for-character. This keeps the full build and incremental --update on the same base, so build_merge's replace-on-re-extract matches the existing node instead of accumulating a duplicate.
Then write the JSON to disk using the Write tool at this exact absolute path (no relative paths — Write resolves relative paths against an undefined cwd and the file will be silently lost):
CHUNK_PATH
```

View File

@@ -1,46 +0,0 @@
# graphify reference: GitHub clone and cross-repo merge
Load this when the user passed one or more `https://github.com/...` URLs, or named several local subfolders to merge into one graph.
### Step 0 - Clone GitHub repo(s) (only if a GitHub URL was given)
**Single repo:**
```bash
LOCAL_PATH=$(graphify clone <github-url> [--branch <branch>])
# Use LOCAL_PATH as the target for all subsequent steps
```
**Multiple repos (cross-repo graph):**
```bash
# Clone each repo, run the full pipeline on each, then merge
graphify clone <url1> # → ~/.graphify/repos/<owner1>/<repo1>
graphify clone <url2> # → ~/.graphify/repos/<owner2>/<repo2>
# Run /graphify on each local path to produce their graph.json files
# Then merge:
graphify merge-graphs \
~/.graphify/repos/<owner1>/<repo1>/graphify-out/graph.json \
~/.graphify/repos/<owner2>/<repo2>/graphify-out/graph.json \
--out graphify-out/cross-repo-graph.json
```
Graphify clones into `~/.graphify/repos/<owner>/<repo>` and reuses existing clones on repeat runs. Each node in the merged graph carries a `repo` attribute so you can filter by origin.
**Multiple local subfolders (monorepo or multi-service layout):**
The skill pipeline writes all intermediate and final outputs to `graphify-out/` in the current working directory. Running the skill on each subfolder separately will clobber the same output dir. Instead, use the CLI directly for each subfolder — it places `graphify-out/` *inside* the scanned path:
```bash
graphify extract ./core/ # → ./core/graphify-out/graph.json
graphify extract ./service/ # → ./service/graphify-out/graph.json
graphify extract ./platform/ # → ./platform/graphify-out/graph.json
# Add --backend gemini|kimi|openai|deepseek|claude-cli depending on which API key you have set
# Then merge at the project root:
graphify merge-graphs \
./core/graphify-out/graph.json \
./service/graphify-out/graph.json \
./platform/graphify-out/graph.json \
--out graphify-out/graph.json
```
Once `graphify-out/graph.json` exists, the fast path above takes over: any codebase question runs `graphify query` directly on the merged graph — no re-extraction, no size gate.

View File

@@ -1,33 +0,0 @@
# graphify reference: commit hook and native CLAUDE.md integration
Load this when the user asked to install the post-commit hook or wire graphify into a project's CLAUDE.md.
## For git commit hook
Install a post-commit hook that auto-rebuilds the graph after every commit. No background process needed - triggers once per commit, works with any editor.
```bash
graphify hook install # install
graphify hook uninstall # remove
graphify hook status # check
```
After every `git commit`, the hook detects which code files changed (via `git diff HEAD~1`), re-runs AST extraction on those files, and rebuilds `graph.json` and `GRAPH_REPORT.md`. Doc/image changes are ignored by the hook - run `/graphify --update` manually for those.
If a post-commit hook already exists, graphify appends to it rather than replacing it.
---
## For native CLAUDE.md integration
Run once per project to make graphify always-on in Claude Code sessions:
```bash
graphify claude install
```
This writes a `## graphify` section to the local `CLAUDE.md` that instructs Claude to check the graph before answering codebase questions and rebuild it after code changes. No manual `/graphify` needed in future sessions.
```bash
graphify claude uninstall # remove the section
```

View File

@@ -1,311 +0,0 @@
# graphify reference: query, path, explain
Load this when the user asks a question against an existing graph, or runs `/graphify path` or `/graphify explain`. The core's query stub points here for the full traversal flow. These flows use the `graphify query` CLI when it is available and fall back to an inline NetworkX traversal otherwise.
Two traversal modes - choose based on the question:
| Mode | Flag | Best for |
|------|------|----------|
| BFS (default) | _(none)_ | "What is X connected to?" - broad context, nearest neighbors first |
| DFS | `--dfs` | "How does X reach Y?" - trace a specific chain or dependency path |
First check the graph exists:
```bash
$(cat graphify-out/.graphify_python) -c "
from pathlib import Path
if not Path('graphify-out/graph.json').exists():
print('ERROR: No graph found. Run /graphify <path> first to build the graph.')
raise SystemExit(1)
"
```
If it fails, stop and tell the user to run `/graphify <path>` first.
### Step 0 — Constrained query expansion (REQUIRED before traversal)
graphify's `query` CLI matches nodes via case-folded substring + IDF — there is **no stemming, no synonyms, no cross-language match** inside the binary, and the inline fallback below matches the same way. If the user's question uses different language or different domain vocabulary than the graph's labels (user says "обработчик" / graph says "handler"; user says "authentication" / graph says "Guardian"), the literal matcher returns 0 hits and the answer collapses to noise.
Fix this **without inventing tokens** by expanding the query against the actual graph vocabulary first:
1. Extract the token vocabulary from node labels:
```bash
$(cat graphify-out/.graphify_python) -c "
import json, re
from pathlib import Path
data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8'))
vocab = set()
for n in data['nodes']:
for c in re.findall(r'[^\W\d_]+', n.get('label','') or '', re.UNICODE):
parts = re.findall(r'[A-Z]+(?=[A-Z][a-z])|[A-Z]?[a-z]+|[A-Z]+', c) or [c]
for p in parts:
t = p.lower()
if 3 <= len(t) <= 30:
vocab.add(t)
Path('graphify-out/.vocab.txt').write_text('\n'.join(sorted(vocab)), encoding='utf-8')
print(f'vocab: {len(vocab)} tokens')
"
```
2. Read `graphify-out/.vocab.txt`. Then for the user's question, select **up to 12 tokens from this exact list** that semantically match the query intent. Hard constraints:
- You MUST pick only tokens present in the vocabulary file. Do NOT invent tokens.
- If a query concept has no plausible token in the vocab, skip it — do not substitute a near-synonym from training memory.
- If **no** vocab tokens match the query at all, output an empty list and tell the user the corpus has no relevant vocabulary for this question. Do not fabricate a search.
- Translate cross-language: Russian "аутентификация" → look for `auth`, `credential`, `token`, `security` IFF present in vocab.
- Morphology: "handlers" maps to `handler` IFF present; "todos" maps to `todo` IFF present.
3. Print the selection explicitly to the user before running the query, so the expansion is auditable:
```
Query expanded to (from graph vocab, N tokens): [token1, token2, ...]
```
If the list is empty, say so plainly and stop — do not proceed to traversal.
### Step 1 — Traversal
Build the **expanded query string** by joining the selected tokens with spaces. Use this string as `QUESTION` below — NOT the original user question. (The original question is preserved only for `save-result` at the end.)
Prefer the CLI when it is installed:
```bash
graphify query "QUESTION"
# or: graphify query "QUESTION" --dfs --budget 3000
```
If the CLI is unavailable, load `graphify-out/graph.json` and run the traversal inline:
1. Find the 1-3 nodes whose label best matches the expanded tokens.
2. Run the appropriate traversal from each starting node.
3. Read the subgraph - node labels, edge relations, confidence tags, source locations.
4. Answer using **only** what the graph contains. Quote `source_location` when citing a specific fact.
5. If the graph lacks enough information, say so - do not hallucinate edges.
```bash
$(cat graphify-out/.graphify_python) -c "
import sys, json
from networkx.readwrite import json_graph
import networkx as nx
from pathlib import Path
data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8'))
G = json_graph.node_link_graph(data, edges='links')
question = 'QUESTION'
mode = 'MODE' # 'bfs' or 'dfs'
terms = [t.lower() for t in question.split() if len(t) >= 3] # match the vocab threshold; keeps api/jwt/ios (#1392)
# Find best-matching start nodes
scored = []
for nid, ndata in G.nodes(data=True):
label = ndata.get('label', '').lower()
score = sum(1 for t in terms if t in label)
if score > 0:
scored.append((score, nid))
scored.sort(reverse=True)
start_nodes = [nid for _, nid in scored[:3]]
if not start_nodes:
print('No matching nodes found for query terms:', terms)
sys.exit(0)
subgraph_nodes = set()
subgraph_edges = []
if mode == 'dfs':
# DFS: follow one path as deep as possible before backtracking.
# Depth-limited to 6 to avoid traversing the whole graph.
visited = set()
stack = [(n, 0) for n in reversed(start_nodes)]
while stack:
node, depth = stack.pop()
if node in visited or depth > 6:
continue
visited.add(node)
subgraph_nodes.add(node)
for neighbor in G.neighbors(node):
if neighbor not in visited:
stack.append((neighbor, depth + 1))
subgraph_edges.append((node, neighbor))
else:
# BFS: explore all neighbors layer by layer up to depth 3.
frontier = set(start_nodes)
subgraph_nodes = set(start_nodes)
for _ in range(3):
next_frontier = set()
for n in frontier:
for neighbor in G.neighbors(n):
if neighbor not in subgraph_nodes:
next_frontier.add(neighbor)
subgraph_edges.append((n, neighbor))
subgraph_nodes.update(next_frontier)
frontier = next_frontier
# Token-budget aware output: rank by relevance, cut at budget (~4 chars/token)
token_budget = BUDGET # default 2000
char_budget = token_budget * 4
# Score each node by term overlap for ranked output
def relevance(nid):
label = G.nodes[nid].get('label', '').lower()
return sum(1 for t in terms if t in label)
ranked_nodes = sorted(subgraph_nodes, key=relevance, reverse=True)
lines = [f'Traversal: {mode.upper()} | Start: {[G.nodes[n].get(\"label\",n) for n in start_nodes]} | {len(subgraph_nodes)} nodes']
for nid in ranked_nodes:
d = G.nodes[nid]
lines.append(f' NODE {d.get(\"label\", nid)} [src={d.get(\"source_file\",\"\")} loc={d.get(\"source_location\",\"\")}]')
for u, v in subgraph_edges:
if u in subgraph_nodes and v in subgraph_nodes:
_raw = G[u][v]; d = next(iter(_raw.values()), {}) if isinstance(G, nx.MultiGraph) else _raw
lines.append(f' EDGE {G.nodes[u].get(\"label\",u)} --{d.get(\"relation\",\"\")} [{d.get(\"confidence\",\"\")}]--> {G.nodes[v].get(\"label\",v)}')
output = '\n'.join(lines)
if len(output) > char_budget:
output = output[:char_budget] + f'\n... (truncated at ~{token_budget} token budget - use --budget N for more)'
print(output)
"
```
Replace `QUESTION` with the **expanded** query string, `MODE` with `bfs` or `dfs`, and `BUDGET` with the token budget (default `2000`, or whatever `--budget N` specifies). Then answer based on the subgraph output above, using only what the graph contains.
After writing the answer, save it back into the graph so it improves future queries. Include the expanded tokens inside the `--answer` text (e.g. `"Expanded from original query via vocab: [tokens]. Then traversed..."`) so the next `--update` extracts the expansion history as a graph node:
```bash
$(cat graphify-out/.graphify_python) -m graphify save-result --question "ORIGINAL_QUESTION" --answer "ANSWER" --type query --nodes NODE1 NODE2
```
Replace `ORIGINAL_QUESTION` with the user's verbatim question, `ANSWER` with your full answer text (containing the expanded-token trace), `NODE1 NODE2` with the list of node labels you cited. This closes the feedback loop: the next `--update` will extract this Q&A as a node in the graph.
**Work memory (self-improving loop).** Add an `--outcome` so future sessions learn from this one — append `--outcome useful|dead_end|corrected` to the `save-result` command (and `--correction "the right answer"` when correcting):
- `useful` — the cited nodes answered the question well (they become *preferred sources*).
- `dead_end` — the question/path led nowhere; don't re-derive it next time.
- `corrected` — the saved answer was wrong; `--correction` records what was right.
At the **start** of graph work, refresh and read the lessons: run `graphify reflect --if-stale` (cheap, deterministic, no LLM; `--if-stale` makes it a no-op when `LESSONS.md` is already newer than every input, e.g. when the git hook just refreshed it), then read `graphify-out/reflections/LESSONS.md`. It lists **preferred sources** (start there), **known dead ends** (skip them), and prior **corrections**. Running `reflect` yourself keeps the lessons current even without the git hook installed; if the post-commit hook *is* installed, `--if-stale` means your session-start run costs almost nothing.
---
## For /graphify path
Find the shortest path between two named concepts in the graph. Prefer the CLI when installed:
```bash
graphify path "NODE_A" "NODE_B"
```
If the CLI is unavailable, run it inline:
```bash
$(cat graphify-out/.graphify_python) -c "
import json, sys
import networkx as nx
from networkx.readwrite import json_graph
from pathlib import Path
data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8'))
G = json_graph.node_link_graph(data, edges='links')
a_term = 'NODE_A'
b_term = 'NODE_B'
def find_node(term):
term = term.lower()
scored = sorted(
[(sum(1 for w in term.split() if w in G.nodes[n].get('label','').lower()), n)
for n in G.nodes()],
reverse=True
)
return scored[0][1] if scored and scored[0][0] > 0 else None
src = find_node(a_term)
tgt = find_node(b_term)
if not src or not tgt:
print(f'Could not find nodes matching: {a_term!r} or {b_term!r}')
sys.exit(0)
try:
path = nx.shortest_path(G, src, tgt)
print(f'Shortest path ({len(path)-1} hops):')
for i, nid in enumerate(path):
label = G.nodes[nid].get('label', nid)
if i < len(path) - 1:
_raw = G[nid][path[i+1]]; edge = next(iter(_raw.values()), {}) if isinstance(G, nx.MultiGraph) else _raw
rel = edge.get('relation', '')
conf = edge.get('confidence', '')
print(f' {label} --{rel}--> [{conf}]')
else:
print(f' {label}')
except nx.NetworkXNoPath:
print(f'No path found between {a_term!r} and {b_term!r}')
except nx.NodeNotFound as e:
print(f'Node not found: {e}')
"
```
Replace `NODE_A` and `NODE_B` with the actual concept names from the user. Then explain the path in plain language - what each hop means, why it's significant.
After writing the explanation, save it back:
```bash
$(cat graphify-out/.graphify_python) -m graphify save-result --question "Path from NODE_A to NODE_B" --answer "ANSWER" --type path_query --nodes NODE_A NODE_B
```
---
## For /graphify explain
Give a plain-language explanation of a single node - everything connected to it. Prefer the CLI when installed:
```bash
graphify explain "NODE_NAME"
```
If the CLI is unavailable, run it inline:
```bash
$(cat graphify-out/.graphify_python) -c "
import json, sys
import networkx as nx
from networkx.readwrite import json_graph
from pathlib import Path
data = json.loads(Path('graphify-out/graph.json').read_text(encoding='utf-8'))
G = json_graph.node_link_graph(data, edges='links')
term = 'NODE_NAME'
term_lower = term.lower()
# Find best matching node
scored = sorted(
[(sum(1 for w in term_lower.split() if w in G.nodes[n].get('label','').lower()), n)
for n in G.nodes()],
reverse=True
)
if not scored or scored[0][0] == 0:
print(f'No node matching {term!r}')
sys.exit(0)
nid = scored[0][1]
data_n = G.nodes[nid]
print(f'NODE: {data_n.get(\"label\", nid)}')
print(f' source: {data_n.get(\"source_file\",\"unknown\")}')
print(f' type: {data_n.get(\"file_type\",\"unknown\")}')
print(f' degree: {G.degree(nid)}')
print()
print('CONNECTIONS:')
for neighbor in G.neighbors(nid):
_raw = G[nid][neighbor]; edge = next(iter(_raw.values()), {}) if isinstance(G, nx.MultiGraph) else _raw
nlabel = G.nodes[neighbor].get('label', neighbor)
rel = edge.get('relation', '')
conf = edge.get('confidence', '')
src_file = G.nodes[neighbor].get('source_file', '')
print(f' --{rel}--> {nlabel} [{conf}] ({src_file})')
"
```
Replace `NODE_NAME` with the concept the user asked about. Then write a 3-5 sentence explanation of what this node is, what it connects to, and why those connections are significant. Use the source locations as citations.
After writing the explanation, save it back:
```bash
$(cat graphify-out/.graphify_python) -m graphify save-result --question "Explain NODE_NAME" --answer "ANSWER" --type explain --nodes NODE_NAME
```

View File

@@ -1,52 +0,0 @@
# graphify reference: transcribe video and audio
Load this only when `detect` reported one or more `video` files. A corpus with no video never reads this.
### Step 2.5 - Transcribe video / audio files (only if video files detected)
Skip this step entirely if `detect` returned zero `video` files.
Video and audio files cannot be read directly. Transcribe them to text first, then treat the transcripts as doc files in Step 3.
**Strategy:** Read the god nodes from `graphify-out/.graphify_detect.json` (or the analysis file if it exists from a previous run). You are already a language model — write a one-sentence domain hint yourself from those labels. Then pass it to Whisper as the initial prompt. No separate API call needed.
**However**, if the corpus has *only* video files and no other docs/code, use the generic fallback prompt: `"Use proper punctuation and paragraph breaks."`
**Step 1 - Write the Whisper prompt yourself.**
Read the top god node labels from detect output or analysis, then compose a short domain hint sentence, for example:
- Labels: `transformer, attention, encoder, decoder``"Machine learning research on transformer architectures and attention mechanisms. Use proper punctuation and paragraph breaks."`
- Labels: `kubernetes, deployment, pod, helm``"DevOps discussion about Kubernetes deployments and Helm charts. Use proper punctuation and paragraph breaks."`
**Export** it as `GRAPHIFY_WHISPER_PROMPT` (the exact name the transcriber reads — and it must be `export`ed so the child Python process sees it) for the next command.
**Step 2 - Transcribe:**
```bash
export GRAPHIFY_WHISPER_MODEL=base # or whatever --whisper-model the user passed (must be exported)
export GRAPHIFY_WHISPER_PROMPT="<the one-sentence domain hint you composed in Step 1>"
$(cat graphify-out/.graphify_python) -c "
import json, os, sys
from pathlib import Path
from graphify.transcribe import transcribe_all
detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
video_files = detect.get('files', {}).get('video', [])
prompt = os.environ.get('GRAPHIFY_WHISPER_PROMPT', 'Use proper punctuation and paragraph breaks.')
transcript_paths = transcribe_all(video_files, initial_prompt=prompt)
# Write the JSON from Python (NOT a shell '>' redirect): transcribe_all/Whisper
# print progress to stdout, which would otherwise corrupt the JSON file (#1392).
Path('graphify-out/.graphify_transcripts.json').write_text(json.dumps(transcript_paths, ensure_ascii=False), encoding=\"utf-8\")
print(f'Transcribed {len(transcript_paths)} file(s)', file=sys.stderr)
"
```
After transcription:
- Read the transcript paths from `graphify-out/.graphify_transcripts.json`
- Add them to the docs list before dispatching semantic subagents in Step 3B
- Print how many transcripts were created: `Transcribed N video file(s) -> treating as docs`
- If transcription fails for a file, print a warning and continue with the rest
**Whisper model:** Default is `base`. If the user passed `--whisper-model <name>`, `export GRAPHIFY_WHISPER_MODEL=<name>` (it must be exported, not just assigned) before running the command above.

View File

@@ -1,210 +0,0 @@
# graphify reference: incremental update and cluster-only
Load this only when the user passed `--update` or `--cluster-only`. A first-time full build never reads this file.
## For --update (incremental re-extraction)
Use when you've added or modified files since the last run. Only re-extracts changed files - saves tokens and time.
```bash
$(cat graphify-out/.graphify_python) -c "
import sys, json
from graphify.detect import detect_incremental, save_manifest
from pathlib import Path
result = detect_incremental(Path('INPUT_PATH'))
new_total = result.get('new_total', 0)
print(json.dumps(result, indent=2, ensure_ascii=False))
Path('graphify-out/.graphify_incremental.json').write_text(json.dumps(result, ensure_ascii=False), encoding=\"utf-8\")
deleted = list(result.get('deleted_files', []))
if new_total == 0 and not deleted:
print('No files changed since last run. Nothing to update.')
raise SystemExit(0)
if deleted:
print(f'{len(deleted)} deleted file(s) to prune.')
if new_total > 0:
print(f'{new_total} new/changed file(s) to re-extract.')
"
```
Then populate `.graphify_detect.json` so Steps 3A6 (which read it unconditionally) see the right state for an incremental run. `files` carries the changed subset (drives Step 3A AST + Step 3B0 cache check on only what changed); `all_files` carries the full corpus for any step that needs corpus-wide context:
```bash
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
r = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\"))
Path('graphify-out/.graphify_detect.json').write_text(json.dumps({
'files': r.get('new_files', {}),
'all_files': r.get('files', {}),
'total_files': r.get('new_total', 0),
'total_words': r.get('total_words', 0),
'skipped_sensitive': r.get('skipped_sensitive', []),
'needs_graph': True,
}, ensure_ascii=False), encoding=\"utf-8\")
"
```
If new files exist, first check whether all changed files are code files:
```bash
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
result = json.loads(open('graphify-out/.graphify_incremental.json', encoding='utf-8').read()) if Path('graphify-out/.graphify_incremental.json').exists() else {}
code_exts = {'.py','.ts','.js','.go','.rs','.java','.cpp','.c','.rb','.swift','.kt','.cs','.scala','.php','.cc','.cxx','.hpp','.h','.kts','.lua','.toc','.f','.F','.f90','.F90','.f95','.F95','.f03','.F03','.f08','.F08'}
new_files = result.get('new_files', {})
all_changed = [f for files in new_files.values() for f in files]
code_only = all(Path(f).suffix.lower() in code_exts for f in all_changed)
print('code_only:', code_only)
"
```
If `code_only` is True: print `[graphify update] Code-only changes detected - skipping semantic extraction (no LLM needed)`, run only Step 3A (AST) on the changed files, skip Step 3B entirely (no subagents), then go straight to merge and Steps 48.
If `code_only` is False (any changed file is a doc/paper/image/video): **first, if any changed file is in `new_files['video']`, run `references/transcribe.md` (Step 2.5) on those files, then rewrite `.graphify_detect.json` to move the resulting transcript paths into `files['document']` and drop `files['video']`** — otherwise raw `.mp4/.mp3` paths are fed to semantic subagents as unreadable media (#1392). Then run the full Steps 3A3C pipeline as normal.
If no new files exist (only deletions), create an empty extraction so the merge step can prune:
```bash
if [ ! -f graphify-out/.graphify_extract.json ]; then
echo '[graphify update] Only deletions -- creating empty extraction for merge.'
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
Path('graphify-out/.graphify_extract.json').write_text(json.dumps({'nodes':[],'edges':[],'hyperedges':[],'input_tokens':0,'output_tokens':0}), encoding='utf-8')
"
fi
```
Then:
```bash
$(cat graphify-out/.graphify_python) -c "
import json
from pathlib import Path
from graphify.build import build_merge
from graphify.detect import save_manifest
# Load new extraction and incremental state
new_extraction = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
incremental = json.loads(Path('graphify-out/.graphify_incremental.json').read_text(encoding=\"utf-8\"))
deleted = list(incremental.get('deleted_files', []))
# prune_sources is ONLY for genuinely DELETED files. Changed/re-extracted files are
# handled by build_merge's replace-on-re-extract (#1344): every source_file in
# new_chunks is dropped from the base before merge, so old/stale nodes don't survive.
# Do NOT add `changed` here: with root= passed, prune_set relativizes to the same base
# as the freshly merged nodes and would DELETE the re-extracted content (#1178 is moot
# now that replace — not the dedup pass — reconciles changed files).
prune = list(deleted) or None
# Use build_merge() — reads graph.json directly without NetworkX round-trip
# so edge direction (calls, implements, imports) is always preserved (#801).
# Pass root= so prune_sources (absolute paths from detect_incremental) are
# relativized to match the graph's relative source_file values; without it
# nothing is pruned and stale nodes accumulate on every update (#1361).
# directed=IS_DIRECTED: replace IS_DIRECTED with True if --directed was given, else
# False. Without it a --directed --update silently rebuilds undirected and collapses
# reciprocal A<->B edges (#1392).
G = build_merge(
[new_extraction],
graph_path='graphify-out/graph.json',
prune_sources=prune,
root='INPUT_PATH',
directed=IS_DIRECTED,
)
print(f'[graphify update] Merged: {G.number_of_nodes()} nodes, {G.number_of_edges()} edges')
# Write merged result back to .graphify_extract.json so Step 4 sees the full graph
merged_out = {
'nodes': [{'id': n, **d} for n, d in G.nodes(data=True)],
'edges': [
# Explicit source/target last so they win over any stale attrs in d.
{**{k: val for k, val in d.items() if k not in ('_src', '_tgt', 'source', 'target')},
'source': d.get('_src', u), 'target': d.get('_tgt', v)}
for u, v, d in G.edges(data=True)
],
# G.graph["hyperedges"] holds hyperedges from both existing graph.json
# and new_extraction (build_merge combines them). Falling back to
# new_extraction only would silently drop prior-run hyperedges (#801).
'hyperedges': list(G.graph.get('hyperedges', [])),
'input_tokens': new_extraction.get('input_tokens', 0),
'output_tokens': new_extraction.get('output_tokens', 0),
}
Path('graphify-out/.graphify_extract.json').write_text(json.dumps(merged_out, ensure_ascii=False), encoding=\"utf-8\")
print(f'[graphify update] Merged extraction written ({len(merged_out[\"nodes\"])} nodes, {len(merged_out[\"edges\"])} edges)')
# Save manifest so next --update diffs against today's state, not the
# prior run's baseline (prevents ghost-node reports on subsequent updates).
# root= matches the build_merge call above so the manifest keys stay relative to
# the scan root — portable across clones/machines, so --update keeps matching
# cached files instead of missing every one after a move (#1417).
#
# Only stamp semantic files (docs/papers/images) that ACTUALLY produced output
# THIS run (new_extraction is this run's fresh extraction, read above before the
# merge overwrote the file): a changed doc whose chunk failed must stay unstamped
# so the next --update re-queues it, otherwise it is marked done and its content
# is lost forever (#2015). Mirrors the library extract path
# (cli._stamped_manifest_files + clear_semantic + scan_corpus).
from graphify.cli import _stamped_manifest_files
_manifest_files = _stamped_manifest_files(incremental['files'], new_extraction, Path('INPUT_PATH'))
# Changed semantic files dispatched this run but NOT stamped had their chunk fail
# or be omitted; clear any stale semantic_hash so they are re-queued (#1948).
_sem_types = ('document', 'paper', 'image')
_dispatched = {f for t, fl in incremental.get('new_files', {}).items() if t in _sem_types for f in fl}
_stamped = {f for fl in _manifest_files.values() for f in fl}
_cleared = _dispatched - _stamped
# scan_corpus = the RAW full corpus so in-root files newly excluded since last run
# are dropped rather than masquerading as deletions; untouched rows preserved (#1908).
_scan = {f for fl in incremental['files'].values() for f in fl}
save_manifest(_manifest_files, root='INPUT_PATH', scan_corpus=_scan, clear_semantic=_cleared or None)
print('[graphify update] Manifest saved.')
"
```
Then run Steps 48 on the merged graph as normal.
After Step 4, show the graph diff:
```bash
$(cat graphify-out/.graphify_python) -c "
import json
from graphify.analyze import graph_diff
from graphify.build import build_from_json
from networkx.readwrite import json_graph
import networkx as nx
from pathlib import Path
# Load old graph (before update) from backup written before merge
old_data = json.loads(Path('graphify-out/.graphify_old.json').read_text(encoding=\"utf-8\")) if Path('graphify-out/.graphify_old.json').exists() else None
new_extract = json.loads(Path('graphify-out/.graphify_extract.json').read_text(encoding=\"utf-8\"))
G_new = build_from_json(new_extract, directed=IS_DIRECTED)
if old_data:
G_old = json_graph.node_link_graph(old_data, edges='links')
diff = graph_diff(G_old, G_new)
print(diff['summary'])
if diff['new_nodes']:
print('New nodes:', ', '.join(n['label'] for n in diff['new_nodes'][:5]))
if diff['new_edges']:
print('New edges:', len(diff['new_edges']))
"
```
Before the merge step, save the old graph: `cp graphify-out/graph.json graphify-out/.graphify_old.json`
Clean up after: `rm -f graphify-out/.graphify_old.json`
---
## For --cluster-only
Skip Steps 13. Re-run clustering on the existing graph:
```bash
graphify cluster-only .
```
`graphify cluster-only .` is **self-contained**: it re-clusters, names communities, and regenerates `GRAPH_REPORT.md`, `graph.json`, and `graph.html` from the existing graph. **Do not re-run Steps 59** — they read intermediate files (`.graphify_extract.json`, `.graphify_detect.json`, `.graphify_analysis.json`) that a prior build's cleanup (Step 9) already deleted, so they raise `FileNotFoundError` (#1392). When it finishes, present the refreshed `GRAPH_REPORT.md` summary as usual.

5
.ralph-supervisor.log Normal file
View File

@@ -0,0 +1,5 @@
[2026-07-23 10:24:53] supervisor started, tailer pid=1460238
[2026-07-23 10:24:53] cycle 1: running ralph-tui resume (log starts at line 978)
[2026-07-23 10:25:59] ralph-tui exited without a recognized stop reason; retrying resume in 5 min
[2026-07-23 10:33:51] supervisor started, tailer pid=1465293
[2026-07-23 10:33:51] cycle 1: running ralph-tui run (log starts at line 1150)

View File

@@ -976,3 +976,329 @@ reconciled DGR-069 #53 blocked
reconciled DGR-070 #54 blocked
reconciled DGR-071 #55 blocked
synced=55 next=DGR-030 dry_run=False
reconciled DGR-017 #1 completed
reconciled DGR-018 #2 completed
reconciled DGR-019 #3 completed
reconciled DGR-020 #4 completed
reconciled DGR-021 #5 completed
reconciled DGR-022 #6 completed
reconciled DGR-023 #7 completed
reconciled DGR-024 #8 completed
reconciled DGR-025 #9 completed
reconciled DGR-026 #10 completed
reconciled DGR-027 #11 completed
reconciled DGR-028 #12 completed
reconciled DGR-029 #13 completed
reconciled DGR-030 #14 in-progress
reconciled DGR-031 #15 ready
reconciled DGR-032 #16 blocked
reconciled DGR-033 #17 blocked
reconciled DGR-034 #18 blocked
reconciled DGR-035 #19 blocked
reconciled DGR-036 #20 blocked
reconciled DGR-037 #21 blocked
reconciled DGR-038 #22 blocked
reconciled DGR-039 #23 blocked
reconciled DGR-040 #24 blocked
reconciled DGR-041 #25 blocked
reconciled DGR-042 #26 blocked
reconciled DGR-043 #27 blocked
reconciled DGR-044 #28 ready
reconciled DGR-045 #29 blocked
reconciled DGR-046 #30 blocked
reconciled DGR-047 #31 blocked
reconciled DGR-048 #32 blocked
reconciled DGR-049 #33 blocked
reconciled DGR-050 #34 blocked
reconciled DGR-051 #35 blocked
reconciled DGR-052 #36 blocked
reconciled DGR-053 #37 blocked
reconciled DGR-054 #38 blocked
reconciled DGR-055 #39 blocked
reconciled DGR-056 #40 blocked
reconciled DGR-057 #41 blocked
reconciled DGR-058 #42 blocked
reconciled DGR-059 #43 blocked
reconciled DGR-060 #44 blocked
reconciled DGR-061 #45 blocked
reconciled DGR-062 #46 blocked
reconciled DGR-063 #47 blocked
reconciled DGR-064 #48 blocked
reconciled DGR-065 #49 blocked
reconciled DGR-066 #50 blocked
reconciled DGR-067 #51 blocked
reconciled DGR-068 #52 blocked
reconciled DGR-069 #53 blocked
reconciled DGR-070 #54 blocked
reconciled DGR-071 #55 blocked
synced=55 next=DGR-030 dry_run=False
📦 Upgrading ralph-tui configuration...
Installing bundled skills for detected agents...
Installing skills for Claude Code...
✓ Skills installed for Claude Code (claude-code)
Installing skills for OpenCode...
✓ Skills installed for OpenCode (opencode)
· Skipping Factory Droid (not installed)
· Skipping Gemini CLI (not installed)
Installing skills for Codex CLI...
✓ Skills installed for Codex CLI (codex)
· Skipping Kiro CLI (not installed)
Installing skills for Cursor Agent...
✓ Skills installed for Cursor Agent (cursor)
· Skipping GitHub Copilot (not installed)
Installing skills for Kimi CLI...
✗ Failed for Kimi CLI
· Skipping Pi Coding Agent (not installed)
✓ Installed 3 template(s) to /home/popov/.config/ralph-tui/templates
✓ Updated config version
✅ Upgraded to config version 2.1
⚠️ Warnings:
• Failed to install skills for Kimi CLI:
DEPRECATED: 'add-skill' has been renamed to 'skills'
Please use: npx skills add <package>
Example: npx skills add vercel-labs/agent-skills
Forwarding to 'npx skills add'...
│
●  claude-code_2-1-216_agent  Agent detected — installing non-interactively
[?25l│
◇ Source: https://github.com/subsy/ralph-tui.git
[?25h[?25l│
◒ Cloning repository…◐ Cloning repository…◓ Cloning repository…◑ Cloning repository…◒ Cloning repository…◐ Cloning repository…◓ Cloning repository…◑ Cloning repository…◒ Cloning repository….◐ Cloning repository….◓ Cloning repository….◑ Cloning repository….◒ Cloning repository….◐ Cloning repository….◓ Cloning repository….◑ Cloning repository….◒ Cloning repository…..◐ Cloning repository…..◓ Cloning repository…..◑ Cloning repository…..◒ Cloning repository…..◐ Cloning repository…..◇ Repository cloned
[?25h[?25l│
◇ Found 4 skills
[?25h│
● Installing all 4 skills
│
■ Invalid agents: kimi-cli
│
● Valid agents: aider-desk, amp, antigravity, antigravity-cli, astrbot, autohand-code, augment, bob, claude-code, openclaw, cline, codearts-agent, codebuddy, codemaker, codestudio, codex, command-code, continue, cortex, crush, cursor, deepagents, devin, dexto, droid, eve, firebender, forgecode, gemini-cli, github-copilot, goose, grok, hermes-agent, inference-sh, jazz, junie, iflow-cli, kilo, kimchi, kimi-code-cli, kiro-cli, kode, lingma, loaf, mcpjam, mistral-vibe, moxby, mux, opencode, openhands, ona, pi, qoder, qoder-cn, qwen-code, replit, reasonix, rovodev, roo, tabnine-cli, terramind, tinycloud, trae, trae-cn, warp, windsurf, zed, zcode, zencoder, zenflow, neovate, pochi, promptscript, adal, universal
Initializing Ralph TUI...
Env filter: no vars matched exclusion patterns (*_API_KEY, *_SECRET_KEY, *_SECRET)
⚠️ Recovered stale session
Cleared 5 stuck in-progress task(s)
Session status set to "interrupted" (resumable)
Resuming previous session...
Failed to resume session
reconciled DGR-017 #1 completed
reconciled DGR-018 #2 completed
reconciled DGR-019 #3 completed
reconciled DGR-020 #4 completed
reconciled DGR-021 #5 completed
reconciled DGR-022 #6 completed
reconciled DGR-023 #7 completed
reconciled DGR-024 #8 completed
reconciled DGR-025 #9 completed
reconciled DGR-026 #10 completed
reconciled DGR-027 #11 completed
reconciled DGR-028 #12 completed
reconciled DGR-029 #13 completed
reconciled DGR-030 #14 ready
reconciled DGR-031 #15 ready
reconciled DGR-032 #16 blocked
reconciled DGR-033 #17 blocked
reconciled DGR-034 #18 blocked
reconciled DGR-035 #19 blocked
reconciled DGR-036 #20 blocked
reconciled DGR-037 #21 blocked
reconciled DGR-038 #22 blocked
reconciled DGR-039 #23 blocked
reconciled DGR-040 #24 blocked
reconciled DGR-041 #25 blocked
reconciled DGR-042 #26 blocked
reconciled DGR-043 #27 blocked
reconciled DGR-044 #28 ready
reconciled DGR-045 #29 blocked
reconciled DGR-046 #30 blocked
reconciled DGR-047 #31 blocked
reconciled DGR-048 #32 blocked
reconciled DGR-049 #33 blocked
reconciled DGR-050 #34 blocked
reconciled DGR-051 #35 blocked
reconciled DGR-052 #36 blocked
reconciled DGR-053 #37 blocked
reconciled DGR-054 #38 blocked
reconciled DGR-055 #39 blocked
reconciled DGR-056 #40 blocked
reconciled DGR-057 #41 blocked
reconciled DGR-058 #42 blocked
reconciled DGR-059 #43 blocked
reconciled DGR-060 #44 blocked
reconciled DGR-061 #45 blocked
reconciled DGR-062 #46 blocked
reconciled DGR-063 #47 blocked
reconciled DGR-064 #48 blocked
reconciled DGR-065 #49 blocked
reconciled DGR-066 #50 blocked
reconciled DGR-067 #51 blocked
reconciled DGR-068 #52 blocked
reconciled DGR-069 #53 blocked
reconciled DGR-070 #54 blocked
reconciled DGR-071 #55 blocked
synced=55 next=none dry_run=False
reconciled DGR-017 #1 completed
reconciled DGR-018 #2 completed
reconciled DGR-019 #3 completed
reconciled DGR-020 #4 completed
reconciled DGR-021 #5 completed
reconciled DGR-022 #6 completed
reconciled DGR-023 #7 completed
reconciled DGR-024 #8 completed
reconciled DGR-025 #9 completed
reconciled DGR-026 #10 completed
reconciled DGR-027 #11 completed
reconciled DGR-028 #12 completed
reconciled DGR-029 #13 completed
reconciled DGR-030 #14 in-progress
reconciled DGR-031 #15 ready
reconciled DGR-032 #16 blocked
reconciled DGR-033 #17 blocked
reconciled DGR-034 #18 blocked
reconciled DGR-035 #19 blocked
reconciled DGR-036 #20 blocked
reconciled DGR-037 #21 blocked
reconciled DGR-038 #22 blocked
reconciled DGR-039 #23 blocked
reconciled DGR-040 #24 blocked
reconciled DGR-041 #25 blocked
reconciled DGR-042 #26 blocked
reconciled DGR-043 #27 blocked
reconciled DGR-044 #28 ready
reconciled DGR-045 #29 blocked
reconciled DGR-046 #30 blocked
reconciled DGR-047 #31 blocked
reconciled DGR-048 #32 blocked
reconciled DGR-049 #33 blocked
reconciled DGR-050 #34 blocked
reconciled DGR-051 #35 blocked
reconciled DGR-052 #36 blocked
reconciled DGR-053 #37 blocked
reconciled DGR-054 #38 blocked
reconciled DGR-055 #39 blocked
reconciled DGR-056 #40 blocked
reconciled DGR-057 #41 blocked
reconciled DGR-058 #42 blocked
reconciled DGR-059 #43 blocked
reconciled DGR-060 #44 blocked
reconciled DGR-061 #45 blocked
reconciled DGR-062 #46 blocked
reconciled DGR-063 #47 blocked
reconciled DGR-064 #48 blocked
reconciled DGR-065 #49 blocked
reconciled DGR-066 #50 blocked
reconciled DGR-067 #51 blocked
reconciled DGR-068 #52 blocked
reconciled DGR-069 #53 blocked
reconciled DGR-070 #54 blocked
reconciled DGR-071 #55 blocked
synced=55 next=DGR-030 dry_run=False
Initializing Ralph TUI...
Env filter: no vars matched exclusion patterns (*_API_KEY, *_SECRET_KEY, *_SECRET)
Session: b92af6fa-2fb6-4e92-9bf2-d571be5c8561
Agent: claude
Tracker: json
PRD: .scratch/distributed-gguf-runtime/prd.json
Max iterations: unlimited
[10:34:45] [INFO] [session] Session b92af6fa-2fb6-4e92-9bf2-d571be5c8561 created. Agent: claude, Tracker: json
[10:34:45] [INFO] [engine] Ralph started. Total tasks: 42
[10:34:45] [INFO] [progress] Iteration 1/∞: Working on DGR-030 - Add accelerator build presets and native CI matrix
[10:34:51] [INFO] [agent] I'll start by reading the context files for this task.
[10:34:52] [INFO] [agent] [Bash] $ echo "--- RALPH-CONTEXT ---" && cat .scratch/distributed-gguf-runtime/RALPH-CONTEXT.md 2>/dev/null |...
[10:34:53] [INFO] [agent] [Bash] $ echo "--- ISSUE ---" && cat .scratch/distributed-gguf-runtime/issues/030-add-accelerator-build-prese...
[10:34:54] [INFO] [agent] [Bash] $ echo "--- PROGRESS ---" && cat .ralph-tui/progress.md 2>/dev/null | tail -300
[10:34:58] [INFO] [agent] [Bash] $ echo "--- DGR-029 evidence ---" && cat .scratch/distributed-gguf-runtime/evidence/DGR-029/README.md ...
[10:34:59] [INFO] [agent] [Bash] $ echo "--- find native/cmake dirs ---" && find . -iname "*CMakeLists*" -not -path "*/node_modules/*" ...
[10:35:14] [INFO] [agent] [Read] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/packages/node/native/llama/UPSTREAM_LOCK.json
[10:35:14] [INFO] [agent] [Read] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/scripts/llama_cpp_dependency.py
[10:35:21] [INFO] [agent] [Read] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/packages/node/native/CMakeLists.txt
[10:35:23] [INFO] [agent] [Bash] $ find /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime -maxdept...
[10:35:30] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime find . -ma...
[10:35:30] [INFO] [agent] [Read] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/tests/test_llama_cpp_dependency.py
[10:35:44] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime grep -rn "...
[10:35:44] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime grep -rln ...
[10:35:48] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime grep -n "r...
[10:35:49] [INFO] [agent] [Read] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/packages/tracker/meshnet_tracker/capability.py
[10:36:21] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime grep -n -B...
[10:36:21] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime grep -n -B...
[10:37:10] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime ls build/l...
[10:37:25] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime ls .venv/b...
[10:37:29] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime timeout 8 ...
[10:37:36] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime .venv/bin/...
[10:37:41] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime timeout 90...
[10:37:47] [INFO] [agent] Good, cmake is available now in `.venv/bin`. Now let's check for GPU SDK availability more thoroughly and check the c++ compiler.
[10:37:48] [INFO] [agent] [Bash] $ c++ --version 2>&1 | head -2 echo "---" which nvidia-smi rocminfo 2>&1 echo "--- CUDA_HOME / ROCM re...
[10:37:55] [INFO] [agent] [Bash] $ which glslc glslangValidator 2>&1 echo "---" find / -maxdepth 6 -iname "vulkan.h" 2>/dev/null | grep...
[10:39:23] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime python3 -c...
[10:39:27] [INFO] [agent] No separate shared-gates block; they're embedded per-story. Let's check the DGR-025 evidence README for the "isolated out-of-tree" and recipe-identity conventions, briefly, then start implementing.
[10:39:28] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime cat packag...
[10:39:31] [INFO] [agent] Now let's implement. First, update `UPSTREAM_LOCK.json` to add the accelerator presets.
[10:39:35] [INFO] [agent] [Edit] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/packages/node/native/llama/UPSTREAM_LOCK.json edit: " "smoke_binary": "bin/llama-gguf-hash",
[10:39:35] [INFO] [agent] "sm..." → " "smoke_binary": "bin/llama-gguf-hash",
[10:39:35] [INFO] [agent] "sm..."
[10:39:39] [INFO] [agent] Now let's update `scripts/llama_cpp_dependency.py` with the accelerator lane primitives.
[10:39:47] [INFO] [agent] [Edit] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/scripts/llama_cpp_dependency.py edit: " if lock["expected_source"] != {"git_tree": loc..." → " if lock["expected_source"] != {"git_tree": loc..."
[10:39:51] [INFO] [agent] Now let's add the accelerator lane functions after `ctest_lane`:
[10:40:07] [INFO] [agent] [Edit] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/scripts/llama_cpp_dependency.py edit: "def ctest_lane(build_dir: pathlib.Path) -> None:
[10:40:07] [INFO] [agent] ..." → "def ctest_lane(build_dir: pathlib.Path) -> None:
[10:40:07] [INFO] [agent] ..."
[10:40:10] [INFO] [agent] Now add the CLI subcommands:
[10:40:13] [INFO] [agent] [Edit] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/scripts/llama_cpp_dependency.py edit: " ctest_parser = subcommands.add_parser("ctest")..." → " ctest_parser = subcommands.add_parser("ctest")..."
[10:40:22] [INFO] [agent] [Edit] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/scripts/llama_cpp_dependency.py edit: " elif args.command == "ctest":
[10:40:22] [INFO] [agent] ..." → " elif args.command == "ctest":
[10:40:22] [INFO] [agent] ..."
[10:40:29] [INFO] [agent] I need to check that this doesn't break the existing test whose fixture lock has `"build": {}`. Let me guard against that.
[10:40:30] [INFO] [agent] [Read] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/scripts/llama_cpp_dependency.py
[10:40:36] [INFO] [agent] [Edit] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/scripts/llama_cpp_dependency.py edit: " presets = lock.get("accelerator_presets", {})
[10:40:36] [INFO] [agent] ..." → " presets = lock.get("accelerator_presets", {})
[10:40:36] [INFO] [agent] ..."
[10:40:40] [INFO] [agent] Now let's write the CI matrix script.
[10:40:53] [INFO] [agent] [Write] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/scripts/native_accelerator_matrix.py "#!/usr/bin/env python3
[10:40:53] [INFO] [agent] """DGR-030: native CI/build matrix over the CPU default plus accelerator lanes.
[10:40:53] [INFO] [agent] Runs the exact deterministic CPU lane DGR-029 locked (unchanged), then probes
[10:40:53] [INFO] [agent] each accelerator p... (4553 chars)"
[10:40:59] [INFO] [agent] Now let's verify the Python compiles and existing tests still pass.
[10:41:00] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime python3 -m...
[10:41:03] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime export PAT...
[10:41:10] [INFO] [agent] Existing tests pass. Now let's quickly test the CLI additions and the SDK-probe logic live.
[10:41:11] [INFO] [agent] [Bash] $ do echo "--- $lane ---" python3 scripts/llama_cpp_dependency.py accelerator-status --name "$lane...
[10:41:14] [INFO] [agent] Now let's run the full matrix live end-to-end to verify the CPU lane still builds correctly and accelerator lanes report skipped, then verify the reverse-cleanup contract.
[10:41:16] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime export PAT...
[10:43:19] [INFO] [agent] It's compiling in the background (matches DGR-029's ~2m16s wall-clock for the CPU lane). I'll wait for it.
[10:43:20] [INFO] [agent] [Bash] $ sleep 90 && cat /tmp/claude-1000/-run-media-popov-d-DEV-repos-d-popov-com-AI--claude-worktrees-distr...
[10:43:20] [INFO] [agent] [Error: <tool_use_error>Blocked: sleep 90 followed by: cat /tmp/claude-1000/-run-media-popov-d-DEV-repos-d-popov-com-AI--claude-worktrees-distributed-gguf-runtime/972c183b-ce77-43c0-9fb1-d513602df13b/tasks/b1g4st9uy.output tail -100. To wait for a condition, use Monitor with an until-loop (e.g. `until <check>; do sleep 2; done`). To wait for a command you started, use run_in_background: true. Do not chain shorter sleeps to work around this block.</tool_use_error>]
[10:43:25] [INFO] [agent] The build moved to background automatically; I'll get notified when it finishes. Meanwhile, let's write tests for the new accelerator functionality.
[10:43:26] [INFO] [agent] [Read] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/tests/test_llama_cpp_dependency.py
[10:44:52] [INFO] [agent] [Bash] $ tail -50 /tmp/claude-1000/-run-media-popov-d-DEV-repos-d-popov-com-AI--claude-worktrees-distributed-...
[10:44:57] [INFO] [agent] The live matrix run succeeded: CPU lane built and CTest-passed exactly as before, and all four accelerator lanes correctly reported `skipped` with an explicit reason (no false success). Let's confirm the source checkout was properly reversed to pristine and the build dirs exist as expected.
[10:44:58] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime git -C bui...
[10:45:06] [INFO] [agent] Good — the checkout is pristine (matches the locked pin/tree), and only the CPU `build` dir was created (accelerator lanes never created their build dirs since they were skipped, as designed). Now let's write the unit tests.
[10:45:26] [INFO] [agent] [Edit] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/tests/test_llama_cpp_dependency.py edit: " dependency._load_lock = lambda: _lock_with_reg..." → " dependency._load_lock = lambda: _lock_with_reg..."
[10:45:30] [INFO] [agent] Now let's create the matrix orchestration test file.
[10:45:51] [INFO] [agent] [Write] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/tests/test_native_accelerator_matrix.py """"Offline behavior tests for DGR-030's native CI/build matrix orchestration.
[10:45:51] [INFO] [agent] These tests never fetch or compile llama.cpp: `llama_cpp_dependency`'s fetch/
[10:45:51] [INFO] [agent] apply/reverse/build/smoke/ctest_lane/accele... (7187 chars)"
[10:46:02] [INFO] [agent] [Edit] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/tests/test_native_accelerator_matrix.py edit: " assert report["failed_lanes"] == []
[10:46:02] [INFO] [agent] assert..." → " assert report["failed_lanes"] == []
[10:46:02] [INFO] [agent] assert..."
[10:46:17] [INFO] [agent] [Bash] $ grep -n "workspace / \"build\"" tests/test_native_accelerator_matrix.py
[10:46:21] [INFO] [agent] [Edit] /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime/tests/test_native_accelerator_matrix.py edit: " assert smoke_calls == [("smoke", (workspace / ..." → " assert smoke_calls == [("smoke", (workspace.re..."
[10:46:28] [INFO] [agent] Now let's run the full test suite.
[10:46:29] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime export PAT...
[10:46:42] [INFO] [agent] All 19 tests pass. Now let's run the broader shared quality gates.
[10:46:43] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime export PAT...
[10:46:55] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime export PAT...
[10:47:00] [INFO] [agent] [Bash] $ cd /run/media/popov/d/DEV/repos/d-popov.com/AI/.claude/worktrees/distributed-gguf-runtime git stash ...

2
.ralph-tui/config.toml Normal file
View File

@@ -0,0 +1,2 @@
autoCommit = true
configVersion = "2.1"

View File

@@ -1,26 +0,0 @@
# Architecture Deepening
## Goal
Increase depth, locality, and testability in the existing Meshnet runtime without changing its domain behavior or reopening accepted architecture decisions.
## Scope
This feature backlog is derived from the Graphify code graph and the architecture review. It targets three high-coupling modules:
1. Distributed Route Session execution in the node HTTP path.
2. Node startup orchestration.
3. Tracker request intake and HTTP dispatch.
## Constraints
- Preserve ADR-0009: the Tracker is the control plane and public proxy; workers own tokenizer and model execution.
- Preserve the active Distributed GGUF Runtime plan: DGR-040 owns native-worker supervision; DGR-041 owns native capability registration. Do not duplicate or redesign those stories.
- Preserve existing Transformers behavior and backend-agnostic Tracker routing, billing, admission, telemetry, and relay semantics.
- Each task starts with focused characterization tests, then moves behavior behind one deep module interface.
## Order
1. Route Session execution, because it has the clearest seam and lets distributed execution be tested without HTTP.
2. Node startup orchestration, using the existing capability-validator adapters.
3. Tracker intake, only after the first two establish the preferred deep-module style.

View File

@@ -1,36 +0,0 @@
# AD-001: Deepen Route Session execution behind one node seam
- **Status:** needs-triage
- **Priority:** p0
- **Dependencies:** none
- **Blocks:** AD-002
- **Evidence:** Graphify identifies `torch_server.py` as the Activation Transport & Binary Frames hub; `_TorchHandler._do_chat_completions` has cyclomatic complexity 53 and owns request parsing, complete-model generation, distributed prefill/decode, Hot KV State recovery, transport clients, SSE, telemetry, and cleanup.
## Objective
Move distributed Route Session execution behind one deep module interface so the HTTP module only translates a client request into a Route Session result/stream.
## Constraints
- Preserve ADR-0009: the head worker owns tokenization and shard execution.
- Preserve the existing OpenAI-compatible HTTP/SSE behavior.
- Keep Hot KV State local to each shard and retain cache-miss re-prefill behavior.
- Do not introduce native GGUF worker work; DGR-040 and DGR-041 own that scope.
## Acceptance criteria
- [ ] Characterization tests cover prefill, decode, cache-miss re-prefill, cancellation, and cleanup through the new module interface without an HTTP server.
- [ ] The HTTP module retains only request translation, response translation, and request accounting.
- [ ] Route Session lifecycle owns downstream direct/relay client cleanup in one place.
- [ ] Existing two-node, KV-cache, relay, and OpenAI compatibility tests retain behavior.
- [ ] `pytest` targeted tests and `python -m compileall packages tests` pass.
## Likely files
- Modify: `packages/node/meshnet_node/torch_server.py`
- Create: module adjacent to `torch_server.py` for Route Session execution
- Modify/add: `tests/test_two_node_pipeline.py`, `tests/test_kv_cache_distributed.py`, focused new tests
## Non-goals
No change to public route selection, model architecture behavior, native worker protocol, or WAN KV migration.

View File

@@ -1,33 +0,0 @@
# AD-002: Deepen Node startup orchestration
- **Status:** needs-triage
- **Priority:** p1
- **Dependencies:** AD-001
- **Evidence:** `run_startup()` in `packages/node/meshnet_node/startup.py` has cyclomatic complexity 101, a broad caller-facing parameter surface, and coordinates hardware, wallet, assignment, artifacts, server construction, capability proof, and Tracker registration.
## Objective
Create a deep Node startup module with explicit immutable startup intent and one execution seam, so callers and tests do not need to understand the full startup sequence.
## Constraints
- Retain the existing explicit capability-validator adapter used by tests.
- Preserve current CLI behavior, registration data, startup ordering, and Transformers behavior.
- Keep native-worker supervision out of scope: DGR-040 owns it. The result may expose a phase where DGR-040 can later attach, but must not implement that worker supervision.
## Acceptance criteria
- [ ] Characterization tests pin successful startup, capability refusal before registration, assignment behavior, and failure classification.
- [ ] The public startup interface accepts a cohesive intent/plan rather than leaking orchestration details across callers.
- [ ] Hardware/assignment, artifact/server, and proof/registration behavior are internally ordered and individually testable through internal seams.
- [ ] Existing `tests/test_node_startup.py`, `tests/test_node_admission.py`, and mining CLI tests retain behavior.
- [ ] `pytest` targeted tests and `python -m compileall packages tests` pass.
## Likely files
- Modify: `packages/node/meshnet_node/startup.py`, `packages/node/meshnet_node/testing.py`, `packages/node/meshnet_node/cli.py`
- Modify/add: `tests/test_node_startup.py`, `tests/test_node_admission.py`, `tests/test_mining_cli.py`
## Non-goals
No new backend type, no Tracker placement algorithm change, and no native-worker process supervision.

View File

@@ -1,35 +0,0 @@
# AD-003: Deepen Tracker request intake without changing control-plane semantics
- **Status:** needs-triage
- **Priority:** p1
- **Dependencies:** AD-001, AD-002
- **Evidence:** Graphify marks `_TrackerHandler` as the highest-degree node (93 edges). `do_POST` dispatches auth, accounts, billing, registry, raft, gossip, placement, calibration, model, and inference paths; `do_GET` mixes operational projections and public request paths. Major handlers include proxy chat (CC 127), registration (CC 82), models (CC 43), and network assignment (CC 42).
## Objective
Deepen Tracker request intake around existing domain seams so HTTP dispatch stays thin and request-specific policy no longer leaks across unrelated control-plane workflows.
## Constraints
- Preserve ADR-0009: Tracker remains a control plane and public inference proxy, never a model host.
- Preserve coverage-first assignment, billing, admission, relay, telemetry, Raft, and existing endpoint contracts.
- Do not create a speculative adapter: each new seam must have at least two real callers/adapters or remain internal.
## Acceptance criteria
- [ ] Characterization tests pin all affected public endpoint response and error behavior before moving code.
- [ ] HTTP dispatch delegates to cohesive intake modules for inference, node/registry lifecycle, and operator projections.
- [ ] Route selection, billing attribution, admission, and coverage logic remain backend-agnostic and do not move into the HTTP module.
- [ ] `_TrackerHandler` no longer owns unrelated endpoint policy directly.
- [ ] Existing routing, capability-admission, billing, account, and consensus tests retain behavior.
- [ ] `pytest` targeted tests and `python -m compileall packages tests` pass.
## Likely files
- Modify: `packages/tracker/meshnet_tracker/server.py`
- Potentially modify: `packages/tracker/meshnet_tracker/billing.py`, `accounts.py`, `capability.py`, `recipe.py`
- Modify/add: focused tests alongside `tests/test_tracker_routing.py`, `tests/test_tracker_capability_admission.py`, `tests/test_billing_ledger.py`, and `tests/test_tracker_consensus.py`
## Non-goals
No redesign of the Tracker architecture, no public endpoint removal, and no change to backend-neutral provider semantics.

View File

@@ -1,10 +0,0 @@
{
"name": "Architecture Deepening",
"description": "Deepen high-coupling Meshnet modules behind narrow interfaces while preserving current domain behavior and locked ADR decisions.",
"sourceOfTruth": "This prd.json and its issue files are planning artifacts; no task is approved for implementation until triaged.",
"stories": [
{"id":"AD-001","title":"Deepen Route Session execution behind one node seam","status":"needs-triage","priority":"p0","dependsOn":[],"blocks":["AD-002"],"files":["packages/node/meshnet_node/torch_server.py","tests/test_two_node_pipeline.py","tests/test_kv_cache_distributed.py"]},
{"id":"AD-002","title":"Deepen Node startup orchestration","status":"needs-triage","priority":"p1","dependsOn":["AD-001"],"blocks":[],"files":["packages/node/meshnet_node/startup.py","packages/node/meshnet_node/testing.py","tests/test_node_startup.py","tests/test_node_admission.py"]},
{"id":"AD-003","title":"Deepen Tracker request intake without changing control-plane semantics","status":"needs-triage","priority":"p1","dependsOn":["AD-001","AD-002"],"blocks":[],"files":["packages/tracker/meshnet_tracker/server.py","tests/test_tracker_routing.py"]}
]
}

View File

@@ -1,6 +1,6 @@
# Distributed GGUF Runtime planning workspace
> **Specification status:** planning artifacts only. No distributed GGUF runtime is implemented. DGR-017 cleanup is complete; no runtime implementation story has completion credit. `prd.json` is authoritative.
> **Implementation status:** DGR-017 through DGR-033 have verified lane evidence, including a fixture-only standalone C++ gRPC worker. These lane checkpoints still require serialized integration and remote publication; they do not claim real model inference. `prd.json` is authoritative.
## Locked scope

View File

@@ -0,0 +1,275 @@
# DGR-030 evidence — accelerator build presets and native CI/build matrix
**Status:** implementation complete, live-verified in this session (2026-07-23).
**Authority:** local `prd.json` is authoritative; Gitea is a projection.
**Upstream pin:** `e920c523e3b8a0163fe498af5bf90df35ff51d25` (`llama.cpp`, unchanged from DGR-027..029).
## What existed before this session
DGR-029 locked exactly one build lane — the deterministic CPU-only lane — in
`UPSTREAM_LOCK.json`'s `build` section, plus `scripts/llama_cpp_dependency.py`'s
`build()`/`smoke()`/`ctest_lane()`/`reproduce()`. There was no accelerator
preset, no SDK-availability probing, and no matrix runner: only the one CPU
lane existed, and there was no mechanism that could ever advertise a GPU
backend as compiled or capable.
## What changed in this session
- `packages/node/native/llama/UPSTREAM_LOCK.json`: added a new top-level
`accelerator_presets` object with one entry each for `cuda` (`GGML_CUDA`),
`rocm` (`GGML_HIP`), `vulkan` (`GGML_VULKAN`), and `metal` (`GGML_METAL`).
Each entry names only the one backend flag it flips and an `sdk_probe`
(a binary to resolve on `PATH`, an optional env-var override, and — for
Metal — a `platform_only: "darwin"` gate). **The existing `build` section
— the deterministic CPU default DGR-029 locked — is untouched.**
- `scripts/llama_cpp_dependency.py`:
- `_load_lock()` now calls a new `_verify_accelerator_presets()`, which
fail-closed-rejects any preset whose named backend flag is not `OFF` in
the CPU default's `configure_flags` — structurally guaranteeing a preset
can only ever *add* one backend on top of the untouched CPU baseline,
never redefine it.
- `accelerator_configure_flags(lock, name)` returns a **new** flag list —
the CPU default's own `configure_flags` list is never mutated — with
exactly the named preset's backend flag flipped `ON` and every other flag
(including `GGML_CPU=ON`, the fallback ops backend GPU builds still need)
left exactly as the CPU default declares it.
- `_sdk_probe(probe)` / `accelerator_status(name, lock)` resolve a lane's
SDK without ever raising: an absent SDK is returned as
`{"available": false, "reason": "<binary> is unavailable on PATH"}` (or
a platform-mismatch reason for Metal), so "unavailable" is data a caller
reports, never an exception a caller has to remember to catch.
- `accelerator_build(source, name, build_dir)` compiles one lane into its
own out-of-tree `build_dir` (an isolated directory, never DGR-029's CPU
`build_dir`), using the same patched-source verification and
`native_targets` as the CPU lane, then writes a
`meshnet-build-metadata.json` recording the exact `commit`/`commit_tree`,
per-patch SHA-256 digests, the lane's overridden `configure_flags`, the
resolved `cmake`/`cxx`/SDK-binary versions/paths, and explicit
`model_downloads: false`, `hardware_execution: false`,
`hardware_certified: false`, `semantic_certification: false` fields plus
a `note` stating the lane is registered-dark until a real-hardware
certification record exists. It **never** calls `smoke()`/`ctest_lane()`
— running a binary linked against a real accelerator backend would touch
real hardware, which this story deliberately keeps out of scope.
- Added `accelerator-status --name <lane>` and
`accelerator-build --name <lane> --source-dir --build-dir` CLI
subcommands, mirroring the existing `ctest`/`build` subcommand pattern.
- `scripts/native_accelerator_matrix.py` (new): the native CI/build matrix.
`run_matrix(workspace)` fetches and applies the locked pin/patch stack once,
runs the unchanged CPU lane (build → smoke → ctest, exactly DGR-029's
contract), then for each `accelerator_presets` entry either reports
`{"status": "skipped", "reason": ...}` (SDK absent) or compiles it via
`accelerator_build` and reports `{"status": "built", ...}` — never silently
treating a skip as a pass. Any `DependencyError` from a lane (CPU or
accelerator) is caught per-lane and reported as `{"status": "failed", ...}`
without aborting the remaining lanes or skipping cleanup. `reverse()` always
runs in a `finally`, restoring the exact pristine pin/tree regardless of
lane outcomes. The CLI prints a JSON report and exits non-zero only if any
lane actually `failed` (a `skipped` lane never fails the run).
- `tests/test_llama_cpp_dependency.py`: added 7 new tests —
`test_accelerator_presets_isolate_one_backend_without_touching_the_cpu_default`
(every preset flips exactly its own flag and the CPU default list is never
mutated), `test_accelerator_configure_flags_rejects_an_unknown_lane`,
`test_accelerator_status_reports_unavailable_sdks_without_raising` (asserts
the exact reason string for cuda/rocm/vulkan/metal absence),
`test_accelerator_status_honors_an_explicit_sdk_override`,
`test_accelerator_status_rejects_an_unknown_lane`,
`test_accelerator_build_refuses_to_compile_an_unavailable_lane` (asserts no
build directory is created), and a `requires_cmake`-gated
`test_accelerator_build_compiles_the_available_lane_with_isolated_evidence`,
which builds a tiny synthetic CMake project (not the full llama.cpp tree) to
prove `accelerator_build`'s "SDK present" path really configures with the
overridden flag, compiles, and writes the registered-dark metadata — in
about a second, without a real GPU SDK.
- `tests/test_native_accelerator_matrix.py` (new): 3 offline tests exercising
`run_matrix`'s orchestration with `llama_cpp_dependency`'s
fetch/apply/reverse/build/smoke/ctest_lane/accelerator_status/
accelerator_build stubbed out — proving unavailable SDKs are reported
`skipped` (never a false pass), an available accelerator lane is compiled
without ever calling `smoke`/`ctest_lane`, and a lane failure is reported
per-lane without aborting sibling lanes or skipping the `reverse()` cleanup.
## Toolchain note
As in DGR-029, neither the ambient system Python nor `.venv-rocm` has `cmake`;
this session's `.venv` also had no `cmake` (a prior session's install did not
persist). This session ran `.venv/bin/python3 -m ensurepip --upgrade` (no
`pip` was present in `.venv` either) and then
`.venv/bin/python3 -m pip install cmake`, landing the same PyPI wheel
(`cmake==4.4.0`) DGR-029 used, at `.venv/bin/cmake` / `.venv/bin/ctest`. All
commands below were run with that `.venv/bin` prepended to `PATH`. No CUDA,
ROCm, or Vulkan SDK (`nvcc`, `hipcc`, `glslc`) is installed in this
environment, and the host platform is Linux, not `darwin` — so all four
accelerator lanes are genuinely `skipped` in this environment's own live run
below, which is real evidence for AC2 ("unavailable SDKs ... explicit
unavailable/skipped lanes"), not a simulated one.
## Verification — live native CI/build matrix run
```text
$ rm -rf build/llama.cpp/build build/llama.cpp/build-cuda build/llama.cpp/build-rocm build/llama.cpp/build-vulkan build/llama.cpp/build-metal
$ python3 scripts/native_accelerator_matrix.py
reused verified offline cache: .../build/llama.cpp/source
usage: .../build/llama.cpp/build/bin/llama-gguf-hash [options] GGUF_IN
...
Test project .../build/llama.cpp/build
Start 27: test-meshnet-range-ownership
1/1 Test #27: test-meshnet-range-ownership ..... Passed 0.01 sec
100% tests passed out of 1
{
"failed_lanes": [],
"hardware_certified": false,
"lanes": [
{
"build_dir": ".../build/llama.cpp/build",
"lane": "cpu",
"metadata": {
"cmake": "cmake version 4.4.0",
"commit": "e920c523e3b8a0163fe498af5bf90df35ff51d25",
"commit_tree": "6c91a11407a3a3fb160f5dac705f9c59718f54f1",
"configure_flags": [
"-DCMAKE_BUILD_TYPE=Release", "-DLLAMA_BUILD_TESTS=ON",
"-DLLAMA_BUILD_EXAMPLES=ON", "-DLLAMA_BUILD_SERVER=OFF",
"-DLLAMA_BUILD_TOOLS=OFF", "-DLLAMA_BUILD_APP=OFF", "-DLLAMA_CURL=OFF",
"-DGGML_CPU=ON", "-DGGML_BLAS=OFF", "-DGGML_CUDA=OFF",
"-DGGML_HIP=OFF", "-DGGML_VULKAN=OFF", "-DGGML_METAL=OFF"
],
"cxx": "c++ (GCC) 15.2.1 20260123 (Red Hat 15.2.1-7)",
"model_downloads": false,
"patches": { "...": "... (5 entries, unchanged sha256 digests from DGR-029)" },
"semantic_certification": false
},
"status": "built"
},
{"lane": "cuda", "reason": "nvcc is unavailable on PATH", "status": "skipped"},
{"lane": "rocm", "reason": "hipcc is unavailable on PATH", "status": "skipped"},
{"lane": "vulkan", "reason": "glslc is unavailable on PATH", "status": "skipped"},
{"lane": "metal", "reason": "platform 'linux' is not 'darwin'", "status": "skipped"}
],
"note": "A `built` lane means it compiled with the exact recorded compiler/SDK/upstream-pin/patch-stack/build-option evidence — it never means an accelerator device was exercised. Every backend/model/recipe lane stays registered-dark until a separate real-hardware certification record exists."
}
$ echo $?
0
```
Wall-clock: `real 2m19.797s` — matches DGR-029's ~2m16s CPU-lane compile; no
accelerator lane actually compiled in this environment (all four SDKs are
genuinely absent), so this run's added cost over DGR-029's own CPU-only
`reproduce()` is just the four fast SDK probes.
Post-run checks (source checkout left pristine by the matrix's `reverse()`):
```text
$ git -C build/llama.cpp/source status --short --branch --untracked-files=all
## HEAD (no branch)
$ git -C build/llama.cpp/source rev-parse HEAD HEAD^{tree}
e920c523e3b8a0163fe498af5bf90df35ff51d25
6c91a11407a3a3fb160f5dac705f9c59718f54f1
$ ls build/llama.cpp/ | grep build
build
```
Only the CPU lane's `build/` directory was created — no `build-cuda`,
`build-rocm`, `build-vulkan`, or `build-metal` directory exists, because every
accelerator lane was genuinely skipped rather than attempted.
## Verification — targeted test suites and shared gates
| Command | Result |
| --- | --- |
| `python3 -m pytest -q tests/test_llama_cpp_dependency.py tests/test_native_accelerator_matrix.py` | `19 passed` (9 pre-existing + 7 new accelerator-lane tests in `test_llama_cpp_dependency.py`, 3 new in `test_native_accelerator_matrix.py`; the `requires_cmake`-gated compile test ran for real, not skipped) |
| `python3 -m compileall -q packages tests` | exit 0 |
| `git diff --check -- packages/node/native/llama/UPSTREAM_LOCK.json scripts/llama_cpp_dependency.py tests/test_llama_cpp_dependency.py scripts/native_accelerator_matrix.py tests/test_native_accelerator_matrix.py` | exit 0 |
| `python3 scripts/ralph_prd_schema.py validate .scratch/distributed-gguf-runtime/prd.json` | `OK: 55 stories validated.` |
`git diff --check` against the full working tree separately reports one
pre-existing trailing-whitespace line in `.ralph-tui-run.log`, which was
already modified before this session started (see the session's initial
`git status`) and is unrelated to this story's scope; it is excluded above by
naming this story's own changed files explicitly.
`python3 -m pytest -q tests/test_ralph_prd_schema.py` reports `55 failed, 53
passed` in this session (all `test_render_issue_markdown_matches_committed_file`
drift between `prd.json` and committed issue Markdown for other stories,
e.g. `DGR-053`..`DGR-071`). `git stash`-ing this session's changes and rerunning
reproduces `56 failed, 52 passed` identically — the same 56 failures minus the
one this session's own `DGR-030` regeneration fixed, confirming the remaining
55 predate this story and are out of scope to fix here. This session did
regenerate `.scratch/distributed-gguf-runtime/issues/030-add-accelerator-
build-presets-and-native-ci-matrix.md` via
`python3 scripts/ralph_prd_schema.py render ... DGR-030` so DGR-030's own
generated issue Markdown matches `prd.json` byte-for-byte (confirmed by the
`test_render_issue_markdown_matches_committed_file[DGR-030]` case no longer
appearing in the failure list).
## Ensuring build success does not advertise capability
- Every accelerator lane's `meshnet-build-metadata.json` explicitly records
`hardware_execution: false`, `hardware_certified: false`, and
`semantic_certification: false`, plus a `note` stating the lane is
registered-dark until a separate real-hardware certification record exists
— the same "artifact states this, not just prose" pattern DGR-029 used for
the CPU lane's `model_downloads`/`semantic_certification` fields.
- `accelerator_build` never runs `smoke()` or `ctest_lane()`: it only
configures and compiles the exact `native_targets` DGR-029 already locked
(`llama-gguf-hash`, `test-meshnet-range-ownership`) — no binary linked
against a real accelerator backend is ever executed by this story's code.
- `_verify_accelerator_presets()` structurally refuses any preset whose
backend flag is not `OFF` in the locked CPU default, so a preset can never
be defined in a way that redefines (rather than adds one backend on top of)
DGR-029's deterministic CPU lane.
- The matrix's top-level report always carries `"hardware_certified": false`
regardless of how many lanes built, and its `note` field states this
explicitly for any consumer reading only the report, not the per-lane
metadata.
## Limitations
- This story proves accelerator lanes *compile* with correct, isolated
flags and preserves exact evidence when a lane's SDK is present. It proves
nothing about numerical correctness, performance, or any backend/model/
recipe capability on real accelerator hardware — that is explicitly
deferred to DGR-041 (capability registration), DGR-053 (real 2-4 stage
certification), and DGR-067 (capability matrix certification), all of which
remain unimplemented.
- No CUDA, ROCm, or Vulkan SDK, and no macOS/Metal toolchain, is available in
this session's environment, so the "compile an available accelerator lane"
path is proven end-to-end only via the `requires_cmake`-gated synthetic-
project unit test and the offline matrix-orchestration tests, not via a
live compile of the real llama.cpp tree under `GGML_CUDA=ON` (etc.). A
future session with a real SDK installed will exercise
`accelerator_build`'s real-lane path against the genuine llama.cpp source
for the first time; nothing in this story's design assumes that hasn't
happened yet.
- The accelerator lanes reuse the CPU lane's exact `native_targets`
(`llama-gguf-hash`, `test-meshnet-range-ownership`), so a passing
accelerator compile also proves the DGR-027/DGR-028 patch stack's
range-ownership code compiles under that backend flag combination — but,
per the point above, only structurally; it says nothing about GPU
execution correctness.
- `cmake`/`ctest` remain absent system-wide in this environment; this session
reinstalled them into `.venv` exactly as DGR-029 did, and that install does
not appear to persist across sessions (this session found `.venv` without
`cmake` despite DGR-029's evidence recording its earlier install). A future
session without a `cmake`-equipped `.venv` will see the same actionable
"cmake is unavailable" failure DGR-029 demonstrated, not a silent pass, and
the new `requires_cmake`-gated tests will be skipped rather than failing.
- `git diff --check` and `tests/test_ralph_prd_schema.py` both carry
pre-existing, out-of-scope failures unrelated to this story (see the gates
table above); this story's own changed files pass both checks cleanly.
## Dependency handoff
DGR-053 (real 2-4 stage certification), DGR-067 (capability matrix
certification), and DGR-068 (packaged releases) may rely on: four isolated,
out-of-tree accelerator build presets (`cuda`/`rocm`/`vulkan`/`metal`) in
`UPSTREAM_LOCK.json`'s `accelerator_presets`, each toggling exactly one
backend flag on top of DGR-029's unchanged CPU default; a native CI/build
matrix (`scripts/native_accelerator_matrix.py`) that compiles every
SDK-available lane with full compiler/SDK/upstream-pin/patch-stack/build-
option evidence and reports SDK-unavailable lanes as explicit `skipped`
lanes, never a false pass; and a compile-only contract (no lane here ever
runs a binary against real accelerator hardware). Real-hardware execution,
numerical correctness, performance measurement, and backend/model/recipe
certification for any accelerator remain entirely unimplemented and must not
be assumed from any lane's green compile.

View File

@@ -0,0 +1,237 @@
# 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.

View File

@@ -0,0 +1,259 @@
# DGR-032 evidence — deterministic fake `ShardEngine`
**Completed:** 2026-07-23
**Branch:** `ralph/distributed-gguf-runtime`
**Authority:** `.scratch/distributed-gguf-runtime/prd.json`
**Dependencies:** DGR-031 (`evidence/DGR-031/README.md` — the project-owned
`ShardEngine` abstract contract, `tests/shard_engine_contract.py`'s
`assert_shard_engine_contract`, and its own dependency-handoff note that
DGR-032 should "subclass `ShardEngine`, add delay/memory-pressure/malformed-
output/crash injection, and pass the *same* `assert_shard_engine_contract`
... — no new contract vocabulary should be needed").
## Objective
Provide an engine fixture that deterministically transforms typed boundary
bundles and session state: head/middle/tail, prefill/decode, cancellation,
release, isolated per-session epoch state, deterministic cache-miss/stale-
epoch failures, and configurable delay/memory-pressure/malformed-output/
crash-injection fault surfaces — all without llama.cpp, a GPU, or any I/O.
## What was found live before changing code
- `packages/node/meshnet_node/shard_engine.py` (DGR-031): the abstract
`ShardEngine` with eight operations (`load`, `capabilities`, `prefill`,
`decode`, `cancel`, `release`, `health`, `metrics`) and its project-owned
dataclasses (`LoadRequest`, `EngineCapabilities`, `PrefillRequest`/
`DecodeRequest`, `StepResult`, `BoundaryBundle`/`EngineTensor`,
`TokenOutput`, `HealthResult`, `MetricsResult`).
- `tests/shard_engine_contract.py` (DGR-031): the reusable
`assert_shard_engine_contract(make_engine)` helper — nine lifecycle checks
any implementation must pass, explicitly designed to be imported by
DGR-032 and DGR-037 against their own engines.
- `tests/test_shard_engine.py` (DGR-031): its `_ReferenceEngine` is
explicitly documented as *not* the DGR-032 fixture ("no delay/memory-
pressure/malformed/crash injection... that is a separate, larger story") —
confirming this story starts from nothing, not inherited credit.
- `grep -rn -i "fakeshardengine\|fake_shard_engine"` across `.py`/`.md`
returned no prior matches — no fake engine existed before this story.
- No file in `packages/node/meshnet_node/` wires a `ShardEngine` into
`shard_runtime_server.py` yet (confirmed by grep for `ShardEngine`/
`shard_engine` in that file — no matches); that wiring is DGR-037's scope,
so this fixture is a standalone, importable engine only.
Live verification of the pre-existing dependency contract before adding new
code:
```bash
PYTHONPATH=packages/node:packages/tracker .venv/bin/python3 -m pytest -q tests/test_shard_engine.py
```
```text
12 passed in 0.13s
```
## What was added (this story's change)
### `packages/node/meshnet_node/fake_shard_engine.py` (new)
`FakeShardEngine(ShardEngine)` — a pure-Python, deterministic fixture:
- **Determinism.** Every `prefill`/`decode` output is `SHA-256(seed_bytes +
idempotency_step)`, where `seed_bytes` is derived from `token_ids` (head)
or the input `BoundaryBundle`'s tensor bytes plus any `token_id_sideband`
(middle/tail-in). Replaying identical inputs on a brand-new session
produces byte-identical output — proven by
`assert_shard_engine_contract`'s own determinism check and reused directly.
- **Head/middle/tail.** Tail shards (`shard_end >= total_layers - 1`) return
a `TokenOutput` sampled into `[0, TOKEN_ID_VOCAB_SIZE)`; head/middle shards
return a `BoundaryBundle` tagged `boundary_point="post_head_residual"` or
`"post_middle_residual"` respectively, so the three cases are
distinguishable in fixture output, not just in the load request. A middle
shard's `token_id_sideband` passes through unchanged from its input bundle
to its output bundle (the V4 first-three-hash-routed-layers requirement
RALPH-CONTEXT documents), never invented or dropped.
- **Isolated session/epoch state.** `_sessions: dict[str, _SessionState]`
keyed by `session_id`; each session tracks its own `epoch`/`cancelled`
flag. A stale epoch, cancel, or release on one session never touches
another's state (`test_session_state_is_isolated_between_two_concurrent_sessions`
proves a stale-epoch rejection and a cancel on session `"a"` leave session
`"b"` fully serviceable). Decoding an unopened session is a deterministic
`NOT_FOUND`/`CacheResult.MISS`, not an exception.
- **Configurable delay.** `FakeShardEngineConfig.step_delay_seconds` +
injectable `sleep` hook (defaults to `time.sleep`, overridable in tests so
they don't block wall-clock time) — invoked once per `prefill`/`decode`
call before computing the deterministic output.
- **Configurable memory pressure.** `FakeShardEngineConfig.memory_budget_bytes`
— the engine accumulates `_bytes_used` across every step's seed bytes;
once a step would push cumulative usage past the budget, that step
deterministically returns `StatusCode.RESOURCE_EXHAUSTED` (`retryable=True`)
with no output, instead of computing one.
- **Configurable malformed output.** `FakeShardEngineConfig.malformed_output`
— when set, the engine still reports `StatusCode.OK` (the point is a
buggy-but-"successful"-looking response, not a status-coded failure) but
the payload is structurally valid, semantically wrong: a tail `TokenOutput`
is pushed past `MALFORMED_TOKEN_ID_FLOOR` (outside the fixture's own
advertised vocab), and a head/middle `BoundaryBundle` gets an
`architecture` field prefixed `"malformed:"` and its tensor `data`
truncated to one byte — both structurally valid per `EngineTensor`'s and
`BoundaryBundle`'s own `__post_init__` validation (which does not
cross-check `data` length against `shape`/`dtype`), so a consumer must
actually check shape/semantics, not just status codes, to catch it.
- **Configurable crash injection.** `FakeShardEngineConfig.crash_after_calls`
+ `crash_exception_factory` — after the configured number of
`prefill`/`decode` calls, the engine raises an arbitrary exception (default
`RuntimeError`, injectable) directly out of the call instead of returning a
`StepResult`. This is deliberately *not* wrapped in `EngineError`/
`StructuredStatus`: it simulates a whole-process failure (what a worker
supervisor — DGR-040 — must catch and restart around), which is a
different failure mode from a graceful status-coded rejection.
- **Fixture-vs-real marker.** `FakeShardEngine.EVIDENCE_CLASS = "fixture"` —
a structural constant (not just docstring prose) so DGR-036's fixture-vs-
real-model parity check can assert programmatically that it is comparing a
fixture engine against a real one, never two fixtures.
- Every fault-injection knob defaults to off (`0`/`None`/`False`), so a bare
`FakeShardEngine()` passes `assert_shard_engine_contract` unmodified —
fault injection is opt-in, never a baseline behavior change.
### `tests/test_fake_shard_engine.py` (new)
- `test_fake_shard_engine_obeys_the_shared_shard_engine_contract` — runs the
full DGR-031 contract against a bare `FakeShardEngine`.
- `test_fake_shard_engine_declares_fixture_evidence_class` — pins the
`EVIDENCE_CLASS` marker DGR-036 will rely on.
- Head/middle/tail output-shape tests (`boundary_point`, token-id-sideband
pass-through, tail vocab range).
- `test_session_state_is_isolated_between_two_concurrent_sessions` — a
stale-epoch rejection and a cancel on one session leave a second,
concurrently open session fully serviceable.
- One test per fault-injection knob (delay hook invocation, memory-budget
trip, malformed tail/boundary-bundle output, crash-after-N-calls,
configurable crash exception type) plus `FakeShardEngineConfig`'s own
`__post_init__` validation (negative delay, negative budget, non-positive
`crash_after_calls`).
- `test_load_result_and_capabilities_report_recipe_architecture` — the
fixture threads `LoadRequest.recipe["architecture"]` through to both
`LoadResult.architecture` and `EngineCapabilities.architecture` rather than
hardcoding `"dense"`/`"fake"` everywhere, so a future V4 recipe is visible
in fixture output too.
### `.scratch/distributed-gguf-runtime/prd.json` / issue markdown
Marked `DGR-032.passes = true` with `completionNotes`; regenerated
`issues/032-implement-deterministic-fake-shardengine.md` via
`scripts/ralph_prd_schema.py render` so it matches `prd.json` byte-for-byte.
## Acceptance criteria → evidence
1. **Head, middle, tail, prefill, decode, cancellation, release with
deterministic outputs** — `FakeShardEngine`'s `_transform`, boundary-point
tagging, and `assert_shard_engine_contract`'s own determinism/cancel/
release checks. Verified by
`test_fake_shard_engine_obeys_the_shared_shard_engine_contract`,
`test_head_shard_returns_boundary_bundle_with_post_head_residual_point`,
`test_middle_shard_returns_boundary_bundle_and_passes_through_token_sideband`,
`test_tail_shard_returns_token_output_within_advertised_vocab`.
2. **Isolated session/epoch state and deterministic cache-miss/stale-epoch
failures** — `_sessions` dict keyed per session;
`test_session_state_is_isolated_between_two_concurrent_sessions` plus the
shared contract's own cache-miss/stale-epoch checks.
3. **Configurable delay, memory pressure, malformed output, crash
injection** — `FakeShardEngineConfig`; verified by
`test_step_delay_seconds_invokes_the_configured_sleep_hook`,
`test_memory_budget_bytes_trips_deterministic_resource_exhausted`,
`test_malformed_output_is_structurally_valid_but_semantically_wrong_for_tail`,
`test_malformed_output_is_structurally_valid_but_semantically_wrong_for_boundary_bundle`,
`test_crash_after_calls_raises_instead_of_returning_a_structured_status`,
`test_crash_exception_factory_is_configurable`,
`test_config_rejects_invalid_knob_values`.
4. **Contract tests distinguish fixture evidence from real-model
certification** — module docstring and this README are explicit that
this is FIXTURE evidence only (numeric parity is DGR-036 onward); the
`EVIDENCE_CLASS = "fixture"` constant makes that distinction structurally
checkable, not just prose, pinned by
`test_fake_shard_engine_declares_fixture_evidence_class`.
5. **Gates + this handoff** — below.
## Commands and results
```bash
PYTHONPATH=packages/node:packages/tracker .venv/bin/python3 -m pytest -q tests/test_fake_shard_engine.py tests/test_shard_engine.py
```
```text
26 passed in 0.17s
```
```bash
PYTHONPATH=packages/node:packages/tracker .venv/bin/python3 -m pytest -q \
tests/test_fake_shard_engine.py 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
109 passed, 3 skipped in 3.78s
```
```bash
.venv/bin/python3 -m compileall -q packages tests
```
```text
(no output — clean; exit 0)
```
```bash
git diff --check
```
```text
(no output — clean)
```
## Limitations
- `tests/` as a whole does not collect cleanly in this environment: the same
pre-existing collection errors DGR-031's evidence recorded (missing
optional dependencies such as `cryptography`) are still present and are
unrelated to this story. This story's own gates were run as the targeted,
scoped test set above per the shared quality gates' wording ("Targeted
deterministic tests pass").
- This is FIXTURE evidence only. `FakeShardEngine` proves lifecycle,
session/epoch isolation, and fault-injection semantics; it proves nothing
about numerical parity with a real model. That is DGR-036's explicit job
once DGR-037's real engine exists, and DGR-053/054 for V4 alpha
certification.
- `FakeShardEngine` is not wired into `shard_runtime_server.py` or any gRPC
surface — it is a standalone, importable engine only. Wiring a
`ShardEngine` (fake or real) into the gRPC servicer is DGR-037's scope for
the real engine; DGR-033 covers a C++ worker surface, which is a separate
native executable, not a consumer of this Python module.
- No gRPC/CMake/native-build changes were needed or made — this story is
pure Python fixture code (`evidenceClass: fixture`, `hardware: none`), so
the native CMake/CTest and patch-stack gates in the shared quality-gate
list do not apply here, consistent with DGR-031's own README recording the
same non-applicability.
## Dependency handoff
- **DGR-033** (standalone fake C++ gRPC Shard worker): its own issue
describes a native C++ executable serving the lifecycle/stream RPC
contract "using the fake engine" — that is a native analogue, not a
consumer of this Python module; DGR-033 should still read this README for
the exact deterministic-output/session-isolation/fault-injection semantics
its C++ fake engine needs to reproduce so both fakes behave identically
from a client's point of view.
- **DGR-034/DGR-035** (range-aware GGUF ownership, boundary I/O):
`FakeShardEngine` already demonstrates range-driven head/middle/tail
behavior purely from `LoadRequest.shard_start`/`shard_end`/`total_layers`;
no new range vocabulary was needed.
- **DGR-036** (fixture vs real-model parity): compare a `FakeShardEngine`
instance's `EVIDENCE_CLASS` (`"fixture"`) against DGR-037's real engine's
equivalent marker (expected `"real"`) to assert the parity check is
actually comparing two different implementations; reuse
`assert_shard_engine_contract` against both to prove lifecycle parity
before attempting numeric parity.
- **DGR-037** (bind llama.cpp to the worker): `FakeShardEngine` is the
reference implementation to diff a real engine's lifecycle behavior
against — same request/result types, same session/epoch model, no new
contract vocabulary.
- **DGR-040** (worker supervision): the crash-injection knob
(`crash_after_calls`/`crash_exception_factory`) exists specifically so
supervision/restart logic has a deterministic way to trigger and test an
unhandled engine failure distinct from a graceful `StructuredStatus`
rejection.

View File

@@ -0,0 +1,281 @@
# DGR-033 evidence — standalone fake C++ gRPC Shard worker
**Completed:** 2026-07-25 (initial); **repaired:** 2026-07-26 after Codex
GPT-5.5 cross-review BLOCK (see "Cross-review repair" below).
**Branch:** `ralph/distributed-gguf-opus`
**Authority:** `.scratch/distributed-gguf-runtime/prd.json`
**Dependencies:** DGR-022 (lifecycle/status contract), DGR-024 (real generated
gRPC harness + `shard_runtime_server.py` reference semantics), DGR-032
(deterministic fake `ShardEngine` semantics).
## Objective
Prove the standalone worker process, stream, lifecycle, and supervision shape
before any llama.cpp integration: a real C++ executable that serves the whole
ShardRuntime lifecycle/stream contract over gRPC using a model-free fake engine,
driven end-to-end by Python integration tests over a real socket.
## What was found live before changing code
- `packages/node/native/proto/shard_runtime.proto` (DGR-021..023): the single
semantic contract. Its `ShardRuntime` service has exactly five RPCs —
`GetCapability`, `Health`, `Session` (bidi stream), `Release`, `Cancel`.
- `packages/node/meshnet_node/shard_runtime_server.py` (DGR-024): the reference
Python servicer. It performs a *bounded real forward* (a CRC over the received
bundle bytes) then echoes the chunk, and fails closed on stale epoch, expired
deadline, corrupt/mis-tiled fragments, exhausted flow-control credit, duplicate
idempotency step, and in-band/out-of-band cancellation, with per-`route_session_id`
state kept on the servicer so an out-of-band `Cancel` can reach a live session.
**Key finding:** despite the schema labelling the checksum `CRC32C`, this
runtime computes it with `zlib.crc32` (standard CRC-32, *not* Castagnoli). The
C++ worker mirrors `zlib.crc32` exactly so its checksum acceptance is
byte-identical to the existing Python surface (the committed C++ *conformance*
test, by contrast, uses true Castagnoli against separately-generated goldens —
the two are unrelated code paths).
- `packages/node/native/CMakeLists.txt` (DGR-029/030): configures against the
ignored `build/native-toolchain` prefix (pinned Protobuf 33.1 + gRPC 1.82.1),
always generates both message and service stubs, and registers a C++
conformance CTest. There was **no** worker executable and **no** Python
worker integration test before this story (confirmed by
`ls packages/node/native/worker` → absent, and grep for `shard_worker`).
- `packages/node/meshnet_node/fake_shard_engine.py` (DGR-032): the Python fake
engine, deliberately *not* wired into the gRPC surface. DGR-033's worker is
its native analogue — a separate executable, not a consumer of that module —
so both fakes present identical behaviour to a client (deterministic,
model-free bounded forward; per-session isolation; fail-closed lifecycle).
## What was added (this story's change)
### `packages/node/native/worker/fake_engine.h` (new)
`meshnet::worker::FakeShardEngine` — a header-only, model-free fixture engine.
Its only capability is to validate a `TensorBundle` (fragments tile exactly, the
uncompressed CRC-32 matches the declared checksum, the declared payload stays
within the negotiated `max_chunk_bytes`) and fold the fragment bytes through a
bounded forward. It links, loads, and dispatches to **nothing** — no llama.cpp,
no graph execution. Carries `kEvidenceClass = "fixture"` mirroring the Python
`FakeShardEngine.EVIDENCE_CLASS` for the later DGR-036 parity check.
### `packages/node/native/worker/shard_service.{h,cpp}` (new)
`ShardRuntimeServiceImpl : meshnet::shard::v1::ShardRuntime::Service` — a faithful
C++ port of the DGR-024 Python servicer: the same per-`route_session_id`
identity/credit/dedup state guarded by a mutex, the same fail-closed negative
paths, and the same lifecycle (open → prefill/decode → flow-control top-up →
release/cancel). Each per-request response is computed under the lock and written
*after* releasing it, so a blocking `Write` can never deadlock the out-of-band
`Cancel` RPC that needs the same lock. Bounded messages are enforced two ways: a
per-tensor `RESOURCE_EXHAUSTED` app check against `max_chunk_bytes`, plus a hard
transport receive ceiling.
### `packages/node/native/worker/shard_worker_main.cpp` (new)
The standalone `shard_worker` executable. Binds `MESHNET_SHARD_LISTEN_ADDR`
(or an `argv` address), prints one readiness line (`ShardRuntime worker listening
on <addr>`), and serves until `SIGTERM`/`SIGINT`. **Graceful shutdown** uses a
self-pipe: the async-signal-safe handler writes one byte, a drain thread reads it
and calls `server->Shutdown()`, so in-flight sessions finish and the process
exits `0` printing `ShardRuntime worker shut down cleanly`. A `--selftest` mode
binds an ephemeral port and self-drives capability/health/fragmented-prefill/
decode/release over a real loopback gRPC channel, giving a pure-C++ CTest that
needs no Python.
### `packages/node/native/CMakeLists.txt` (modified)
Adds the `shard_worker` executable (linking only `shard_runtime_grpc` +
`gRPC::grpc++` — no llama.cpp) and registers `shard_worker_selftest` as a CTest.
### `tests/test_native_shard_worker.py` (new)
18 integration tests that spawn the **real compiled binary** as a subprocess and
drive it with the committed generated stubs over a real localhost socket. When
the binary is not built they skip (the DGR-029/030 `requires_cmake` gating
pattern), locating it via `MESHNET_SHARD_WORKER_BIN` or `build/native/shard_worker`.
## Acceptance criteria → evidence
1. **Standalone C++ executable serves the complete lifecycle/stream contract
using the fake engine** — `shard_worker` builds and serves all five RPCs; the
`shard_worker_selftest` CTest drives open → fragmented prefill → decode →
release over real gRPC; the 18 Python tests cover the same against the
subprocess.
2. **Python integration tests cover startup, health, capability, fragmented
prefill, decode, release, cancellation, graceful shutdown** —
`test_worker_startup_and_health`, `test_worker_capability`,
`test_fragmented_prefill_echoes_reassembled_payload` (3-fragment tiling),
`test_decode_step_is_served`, `test_release_is_terminal`,
`test_in_band_cancel_of_single_work_item_does_not_end_stream`,
`test_in_band_cancel_of_whole_session_is_terminal`,
`test_out_of_band_cancel_rpc_races_ahead_of_open`,
`test_graceful_shutdown_on_sigterm` (SIGTERM → exit 0 + clean-shutdown line).
3. **Bounded messages, deadlines, flow control, independent session
cancellation enforced** — `test_bounded_message_is_rejected`
(`RESOURCE_EXHAUSTED` on an over-ceiling tensor),
`test_expired_deadline_is_rejected`, `test_flow_control_violation_and_topup`,
`test_independent_session_cancellation` (cancelling session A leaves session B
fully serviceable), plus `test_stale_route_epoch_is_rejected`,
`test_duplicate_idempotency_step_is_acked`,
`test_malformed_fragment_tiling_is_rejected`.
4. **Exposes neither llama.cpp RPC nor arbitrary graph execution**
`ldd build/native/shard_worker` shows no llama/ggml shared libs;
`nm -C build/native/shard_worker | grep -icE 'llama_|ggml_'``0`; the proto
exposes exactly one service with five lifecycle RPCs and no graph-exec entry.
5. **Gates + this handoff** — below.
## Commands and results
Toolchain (ignored `build/native-toolchain`, pinned Protobuf 33.1 + gRPC 1.82.1):
```bash
bash scripts/bootstrap_native_toolchain.sh "$PWD/build/native-toolchain"
# ... gRPC 1.82.1 commit acccf84c0df20487d64101f528e5d426541ca4e5
# grpc_cpp_plugin sha256 43705cf26ae9ce98bbcee76b3408f5e171eec746b50bf0dd42dd68d132c6a533
```
Focused out-of-tree CMake build + CTest:
```bash
cmake -S packages/node/native -B build/native -DCMAKE_PREFIX_PATH="$PWD/build/native-toolchain"
cmake --build build/native -j"$(nproc)"
ctest --test-dir build/native --output-on-failure
```
```text
1/2 Test #1: shard_worker_selftest ............ Passed 0.01 sec
2/2 Test #2: shard_protocol_conformance ....... Passed 0.00 sec
100% tests passed out of 2
```
Python integration tests against the real binary:
```bash
PYTHONPATH=packages/node:packages/tracker python -m pytest -q tests/test_native_shard_worker.py
```
```text
18 passed in 3.96s
```
AC4 (no llama.cpp / no graph exec):
```bash
ldd build/native/shard_worker | grep -iE 'llama|ggml' # -> (no matches)
nm build/native/shard_worker | grep -icE 'llama_|ggml_' # -> 0
```
Shared gates + regression:
```bash
python -m compileall -q packages tests # exit 0
git diff --check -- packages/node/native tests/test_native_shard_worker.py # exit 0
PYTHONPATH=packages/node:packages/tracker python -m pytest -q \
tests/test_shard_runtime_harness.py tests/test_native_shard_protocol.py
# -> 61 passed, 2 skipped (DGR-024 harness + native protocol untouched)
```
Toolchain used: `cmake`/`ctest` from the `distributed-gguf-runtime` worktree's
`.venv` (PyPI `cmake==4.4.0` wheel — no system cmake exists here, same as
DGR-029/030); the Python client uses that venv's `grpcio==1.82.1`,
`grpcio-tools==1.82.1`, `protobuf`, `pytest`. `g++ (GCC) 15.2.1`.
## Limitations
- This is FIXTURE evidence only. The worker's "forward" is a CRC-over-wire-bytes
echo, not real tensor compute; it proves process/stream/lifecycle/supervision
shape, nothing about numerical correctness. Real engine binding is DGR-037 and
numeric parity is DGR-036/052.
- The worker checksum path mirrors the DGR-024 runtime's `zlib.crc32` (standard
CRC-32 under a `CRC32C` label). Compressed-tensor tiling/checksum is not
independently verified (no zstd decompressor in the fixture) — identical to the
DGR-024 limitation.
- Default `pytest` runs skip `tests/test_native_shard_worker.py` unless the
worker binary is built (or `MESHNET_SHARD_WORKER_BIN` is set); this session
built it and ran all 18 for real (results above). Building requires the pinned
gRPC C++ toolchain, which is not present by default and must be bootstrapped.
- No CUDA/ROCm/GPU, no model download, no network at test time — all default
tests are fixture-only and offline.
## Dependency handoff
- **DGR-036** (fixture vs real-model parity): the worker's `FakeShardEngine`
carries `kEvidenceClass = "fixture"`; diff it against DGR-037's real engine's
equivalent marker, and reuse the same lifecycle/stream contract this worker
serves to prove behavioural parity before numeric parity.
- **DGR-037** (bind llama.cpp): replace `FakeShardEngine`'s bounded forward with
the real engine behind the *same* `ShardRuntimeServiceImpl` surface; the
service's session/epoch/credit/dedup/cancel machinery and the graceful-shutdown
supervision shape are reusable as-is.
- **DGR-040** (worker supervision): `shard_worker` already provides the
supervision primitives — a readiness line for start detection, `SIGTERM`
graceful drain with a clean-exit line, and a `--selftest` liveness probe.
A supervisor can start/monitor/restart the process around these.
## Cross-review repair (2026-07-26)
An independent Codex GPT-5.5 review BLOCKED the initial implementation. Four
root protocol defects in the native worker were fixed in this worktree
(`.claude/worktrees/distributed-gguf-opus`); the fake-engine echo semantics and
supervision shape are unchanged.
### Defects fixed
1. **Activation before SessionOpen bypassed all state.** A chunk/decode whose
`route_session_id` had no opened session fell through every `if (state && ...)`
guard and was echoed — bypassing lifecycle, cancellation, epoch and
flow-control. `SessionState` now carries an `opened` flag set only by a valid
`SessionOpen`; chunk and decode fail closed with a terminal
`ERROR_CODE_INTERNAL` and end the stream when it is false. A placeholder state
created by an out-of-band `Cancel` that races `Open` has `opened == false`, so
it can never admit work either.
2. **Flow control blindly trusted the peer proposal.** `SessionOpen` copied the
proposed `credits/max_inflight/max_chunk_bytes` verbatim into session state and
the accepted reply. New `ShardRuntimeServiceImpl::NegotiateFlow` takes the
strictest bound of peer-vs-worker for every field (mirroring
`negotiate_flow_control` in `native_protocol/codec.py`), stores the negotiated
ceilings on the session, and enforces the negotiated per-session
`max_chunk_bytes` on every bundle (`FakeShardEngine::Validate` now takes the
ceiling as an argument instead of a fixed construction-time value).
3. **In-stream `ReleaseSignal` leaked session state.** The stream `release` arm
wrote a terminal status but never dropped the session. It now erases the
session under the lock before responding, so KV/credits/dedup are freed
immediately (the out-of-band `Release` RPC already erased).
4. **`SessionOpen` echoed caller identity instead of validating it.** The handshake
now rejects an incompatible `schema_version` (`SCHEMA_UNSUPPORTED`), a
mismatched model/recipe `Fingerprint` (`FINGERPRINT_MISMATCH`), and a
`ShardRange` outside the worker's served range (`SHARD_RANGE_MISMATCH`), each
terminal; `SessionAccepted` now reports the worker's own served fingerprint
rather than a copy of the caller's.
### Changed files (repair)
- `packages/node/native/worker/shard_service.h``opened` +
`max_prefill_chunk_tokens` on `SessionState`; `NegotiateFlow` decl; engine now
default-constructed.
- `packages/node/native/worker/shard_service.cpp` — worker-identity constants +
fill helpers; `NegotiateFlow`; `SessionOpen` validation/negotiation; fail-closed
chunk/decode; per-session `max_chunk_bytes`; in-stream release erase.
- `packages/node/native/worker/fake_engine.h``Validate(bundle, max_chunk_bytes)`.
- `tests/test_native_shard_worker.py` — extended `_open` (schema/fingerprint/range/
flow overrides); fixed `test_release_rpc_is_idempotent` for the new erase
semantics; added 9 regression tests (chunk/decode before open, flow-control
clamp, negotiated-ceiling cap, in-stream release erase, schema/fingerprint/range
rejection, worker-fingerprint-not-caller).
### Re-run gates (real, rebuilt binary)
Build driven through the pinned `cmake` (Unix Makefiles + `gmake`, gRPC 1.82.1):
```text
cmake --build build/native --parallel 8 -> BUILD_EXIT 0
ctest --test-dir build/native --output-on-failure -> 100% (2/2) passed
shard_worker_selftest ....... Passed
shard_protocol_conformance .. Passed
python -m pytest -q tests/test_native_shard_worker.py -> 27 passed
python -m pytest -q tests/test_shard_runtime_harness.py \
tests/test_native_shard_protocol.py -> 63 passed
python -m compileall -q packages tests -> exit 0
git diff --check -> clean
ldd build/native/shard_worker | grep -iE 'llama|ggml' -> NONE
nm -C build/native/shard_worker | grep -cE 'llama_|ggml_' -> 0
```
The worker integration suite grew from 18 to 27 tests; all pass against the
freshly compiled binary. No `.ralph-lane` runtime artifacts were touched.

View File

@@ -0,0 +1,94 @@
# DGR-034 evidence — dense-Llama range-aware GGUF ownership
**Status:** implemented and live-verified on 2026-08-01. `prd.json` remains
the authority for story state.
## What changed
- The pinned llama.cpp patch stack adds `meshnet_owned_layer_start/end` and
filters dense-Llama GGUF registration to `blk.N.*` for the requested
half-open range. `token_embd.weight` belongs to the head; `output_norm` and
`output.weight` (or the tied embedding) belong to the tail.
- The load state exposes a C range report derived from the registered model
buffers, and a project-owned `meshnet-range-report` tool audits the live
registered tensor map. It rejects empty, inverted, out-of-model, missing,
outside-range, unexpected, and endpoint-inconsistent loads.
- `meshnet_node.range_report` accepts only audited tool output. It makes the
range and endpoint flags authoritative from loaded state rather than caller
assertions, and fails closed on malformed ownership or byte counts.
## Real-model memory evidence
Artifact: `Magistral-Small-2509-Q4_K_M.gguf`, 14,333,911,104 bytes, SHA-256
`a17a113480e7f55780ad1d100493c70ac158d1943e578bbdd75acef0872ab7dc`.
It stayed on the configured mounted drive; no artifact was downloaded or put
under `/home`.
The direct non-mmap lane proves resident storage tracks owned tensors:
| Range | Registered tensors | Resident bytes | Process peak RSS |
| --- | ---: | ---: | ---: |
| `[10, 20)` | 90 | 3,304,898,560 | 3,298,800 KiB |
| `[0, 40)` | 363 | 14,326,026,240 | 14,061,632 KiB |
Raw reports and timings are in `runs/default-mid-a.*` and
`runs/default-full-nommap.*`. The middle range is 23.1% of the full
resident allocation and owns 24.8% of the registered tensors.
## Commands and results
```text
python3 scripts/llama_cpp_dependency.py reverse --source-dir build/llama.cpp/source
python3 scripts/llama_cpp_dependency.py verify --workspace build/llama.cpp
python3 scripts/llama_cpp_dependency.py apply --source-dir build/llama.cpp/source
# apply/check/reverse succeeded against e920c523e3b8a0163fe498af5bf90df35ff51d25;
# the source was then applied for the focused native checks.
(cd packages/node/native/llama/patches && sha256sum -c SHA256SUMS)
# all six patches: OK
/home/popov/.hermes/hermes-agent/venv/bin/ctest \
--test-dir build/llama.cpp/dgr034-check \
-R '^test-meshnet-range-ownership$' --output-on-failure
# 1/1 passed
PYTHONPATH=packages/node MESHNET_RANGE_REPORT_BIN="$PWD/build/llama.cpp/dgr034-check/bin/meshnet-range-report" \
/home/popov/.hermes/hermes-agent/venv/bin/pytest -q \
tests/test_range_report.py tests/test_meshnet_range_report_tool.py \
tests/test_llama_cpp_dependency.py
# 56 passed in 0.87s
PYTHONPATH=packages/node /home/popov/.hermes/hermes-agent/venv/bin/python \
-m compileall -q packages tests
python3 scripts/ralph_prd_schema.py validate .scratch/distributed-gguf-runtime/prd.json
git diff --check && git diff --cached --check
# all exit 0; PRD validation: 55 stories validated
```
The model commands used the same `meshnet-range-report` binary with
`--no-mmap --no-extra-bufts`, first for `[10,20)` and then `[0,40)`; both
returned `ok: true` and their exact output is retained above.
## Changed files
- `packages/node/native/llama/PATCH-STACK.md`
- `packages/node/native/llama/UPSTREAM_LOCK.json`
- `packages/node/native/llama/patches/{series,SHA256SUMS,UPSTREAM-ASSUMPTIONS.json,0006-meshnet-range-report-tool.patch}`
- `packages/node/meshnet_node/range_report.py`
- `tests/test_range_report.py`
- `tests/test_meshnet_range_report_tool.py`
- `.scratch/distributed-gguf-runtime/evidence/DGR-034/*`
## Limitations and dependency handoff
- The mmap loader can retain broad contiguous file spans when GGUF tensor
order places a tail endpoint near the beginning of the artifact; the direct
non-mmap lane is the certified resident-memory result. The raw mmap report
is retained in `runs/default-head.json` and must not be presented as a
physical-RSS saving.
- This story proves loading/ownership only. Partial-range graph execution
remains fail-closed until DGR-035 provides typed dense boundary adapters.
- DGR-037 can bind the worker to `llama_model_meshnet_range_report` or the
strict Python consumer; it must use the reported range, not requested range,
for capability publication. DGR-051 must add its V4-specific ownership
rules separately.

View File

@@ -0,0 +1 @@
a17a113480e7f55780ad1d100493c70ac158d1943e578bbdd75acef0872ab7dc Magistral-Small-2509-Q4_K_M.gguf

View File

@@ -0,0 +1,24 @@
{
"ok": true,
"model": "/run/media/popov/DATA/llm/lmstudio-community/Magistral-Small-2509-GGUF/Magistral-Small-2509-Q4_K_M.gguf",
"architecture": "llama",
"n_layer": 40,
"file_bytes": 14333911104,
"requested_range": [0, 40],
"reported_range": [0, 40],
"mmap": false,
"touched": false,
"use_extra_bufts": false,
"has_token_embeddings": true,
"has_output_head": true,
"tied_output_head": false,
"mapped_bytes": 0,
"resident_bytes": 14326026240,
"registered_tensors": 363,
"registered_bytes": 14326026240,
"unexpected_registered_tensors": [],
"missing_owned_layers": [],
"vm_size_bytes": 14392061952,
"vm_rss_bytes": 14387003392,
"vm_hwm_bytes": 14399111168
}

View File

@@ -0,0 +1 @@
elapsed=0:02.48 maxrss_kib=14061632 exit=0

View File

@@ -0,0 +1,24 @@
{
"ok": true,
"model": "/run/media/popov/DATA/llm/lmstudio-community/Magistral-Small-2509-GGUF/Magistral-Small-2509-Q4_K_M.gguf",
"architecture": "llama",
"n_layer": 40,
"file_bytes": 14333911104,
"requested_range": [0, 10],
"reported_range": [0, 10],
"mmap": true,
"touched": false,
"use_extra_bufts": true,
"has_token_embeddings": true,
"has_output_head": false,
"tied_output_head": false,
"mapped_bytes": 6219366400,
"resident_bytes": 6219366400,
"registered_tensors": 91,
"registered_bytes": 3771596800,
"unexpected_registered_tensors": [],
"missing_owned_layers": [],
"vm_size_bytes": 16942260224,
"vm_rss_bytes": 16937005056,
"vm_hwm_bytes": 16947953664
}

View File

@@ -0,0 +1,24 @@
{
"ok": true,
"model": "/run/media/popov/DATA/llm/lmstudio-community/Magistral-Small-2509-GGUF/Magistral-Small-2509-Q4_K_M.gguf",
"architecture": "llama",
"n_layer": 40,
"file_bytes": 14333911104,
"requested_range": [10, 20],
"reported_range": [10, 20],
"mmap": false,
"touched": false,
"use_extra_bufts": false,
"has_token_embeddings": false,
"has_output_head": false,
"tied_output_head": false,
"mapped_bytes": 0,
"resident_bytes": 3304898560,
"registered_tensors": 90,
"registered_bytes": 3304898560,
"unexpected_registered_tensors": [],
"missing_owned_layers": [],
"vm_size_bytes": 3370934272,
"vm_rss_bytes": 3365814272,
"vm_hwm_bytes": 3377971200
}

View File

@@ -0,0 +1 @@
elapsed=0:00.82 maxrss_kib=3298800 exit=0

View File

@@ -1,7 +1,7 @@
<!-- GENERATED FROM prd.json — DO NOT EDIT AS AN INDEPENDENT SOURCE. prd.json IS AUTHORITATIVE. -->
# DGR-030: Add accelerator build presets and native CI matrix
- **Status / triage:** specification only; `ready-for-agent`; `passes: false`
- **Status / triage:** completed; `passes: true`
- **Execution mode:** `AFK`
- **Milestone:** `M1`
- **Dependencies:** `DGR-029`
@@ -18,11 +18,11 @@ Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`,
## Acceptance criteria
- [ ] Add isolated out-of-tree presets for CUDA, ROCm, Vulkan, and Metal without changing the deterministic CPU default.
- [ ] Add a native CI/build matrix that reports unavailable SDKs as explicit unavailable/skipped lanes rather than false success.
- [ ] Compile each available lane and preserve exact compiler, SDK, upstream pin, patch-stack, and build-option evidence.
- [ ] Keep every backend/model/recipe lane registered-dark until a separate real-hardware certification record exists.
- [ ] Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff.
- [x] Add isolated out-of-tree presets for CUDA, ROCm, Vulkan, and Metal without changing the deterministic CPU default.
- [x] Add a native CI/build matrix that reports unavailable SDKs as explicit unavailable/skipped lanes rather than false success.
- [x] Compile each available lane and preserve exact compiler, SDK, upstream pin, patch-stack, and build-option evidence.
- [x] Keep every backend/model/recipe lane registered-dark until a separate real-hardware certification record exists.
- [x] Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff.
## Shared quality gates
@@ -36,4 +36,4 @@ Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`,
## Evidence handoff
Write and verify `.scratch/distributed-gguf-runtime/evidence/DGR-030/README.md`. Until every criterion and applicable gate has real evidence, this story remains `passes: false`. Legacy evidence is provenance only, not completion credit.
Verified evidence: `.scratch/distributed-gguf-runtime/evidence/DGR-030/README.md`. Legacy evidence remains provenance only and grants no implementation completion credit.

View File

@@ -1,7 +1,7 @@
<!-- GENERATED FROM prd.json — DO NOT EDIT AS AN INDEPENDENT SOURCE. prd.json IS AUTHORITATIVE. -->
# DGR-031: Introduce the project-owned `ShardEngine` interface
- **Status / triage:** specification only; `ready-for-agent`; `passes: false`
- **Status / triage:** completed; `passes: true`
- **Execution mode:** `AFK`
- **Milestone:** `M1`
- **Dependencies:** `DGR-021`, `DGR-025`
@@ -18,11 +18,11 @@ Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`,
## Acceptance criteria
- [ ] Define load, capabilities, prefill/decode, boundary/logits result, cancel, release, health, and metrics operations.
- [ ] Use project-owned request/result/state types; expose no `ggml_tensor`, llama context, scheduler, or ABI-owned structure.
- [ ] Reserve typed MTP and architecture auxiliary-state hooks without enabling them.
- [ ] Add contract tests proving fake and future llama implementations obey identical lifecycle semantics.
- [ ] Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff.
- [x] Define load, capabilities, prefill/decode, boundary/logits result, cancel, release, health, and metrics operations.
- [x] Use project-owned request/result/state types; expose no `ggml_tensor`, llama context, scheduler, or ABI-owned structure.
- [x] Reserve typed MTP and architecture auxiliary-state hooks without enabling them.
- [x] Add contract tests proving fake and future llama implementations obey identical lifecycle semantics.
- [x] Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff.
## Shared quality gates
@@ -36,4 +36,4 @@ Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`,
## Evidence handoff
Write and verify `.scratch/distributed-gguf-runtime/evidence/DGR-031/README.md`. Until every criterion and applicable gate has real evidence, this story remains `passes: false`. Legacy evidence is provenance only, not completion credit.
Verified evidence: `.scratch/distributed-gguf-runtime/evidence/DGR-031/README.md`. Legacy evidence remains provenance only and grants no implementation completion credit.

View File

@@ -1,7 +1,7 @@
<!-- GENERATED FROM prd.json — DO NOT EDIT AS AN INDEPENDENT SOURCE. prd.json IS AUTHORITATIVE. -->
# DGR-032: Implement deterministic fake `ShardEngine`
- **Status / triage:** specification only; `ready-for-agent`; `passes: false`
- **Status / triage:** completed; `passes: true`
- **Execution mode:** `AFK`
- **Milestone:** `M1`
- **Dependencies:** `DGR-031`
@@ -18,11 +18,11 @@ Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`,
## Acceptance criteria
- [ ] Support head, middle, tail, prefill, decode, cancellation, and release with deterministic outputs.
- [ ] Model isolated session/epoch state and deterministic cache-miss/stale-epoch failures.
- [ ] Support configurable delay, memory pressure, malformed output, and crash injection.
- [ ] Contract tests distinguish fixture evidence from real-model certification.
- [ ] Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff.
- [x] Support head, middle, tail, prefill, decode, cancellation, and release with deterministic outputs.
- [x] Model isolated session/epoch state and deterministic cache-miss/stale-epoch failures.
- [x] Support configurable delay, memory pressure, malformed output, and crash injection.
- [x] Contract tests distinguish fixture evidence from real-model certification.
- [x] Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff.
## Shared quality gates
@@ -36,4 +36,4 @@ Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`,
## Evidence handoff
Write and verify `.scratch/distributed-gguf-runtime/evidence/DGR-032/README.md`. Until every criterion and applicable gate has real evidence, this story remains `passes: false`. Legacy evidence is provenance only, not completion credit.
Verified evidence: `.scratch/distributed-gguf-runtime/evidence/DGR-032/README.md`. Legacy evidence remains provenance only and grants no implementation completion credit.

View File

@@ -1,7 +1,7 @@
<!-- GENERATED FROM prd.json — DO NOT EDIT AS AN INDEPENDENT SOURCE. prd.json IS AUTHORITATIVE. -->
# DGR-033: Build a standalone fake C++ gRPC Shard worker
- **Status / triage:** specification only; `ready-for-agent`; `passes: false`
- **Status / triage:** completed; `passes: true`
- **Execution mode:** `AFK`
- **Milestone:** `M1`
- **Dependencies:** `DGR-022`, `DGR-024`, `DGR-032`
@@ -18,11 +18,11 @@ Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`,
## Acceptance criteria
- [ ] A standalone C++ executable serves the complete lifecycle and stream RPC contract using the fake engine.
- [ ] Python integration tests cover startup, health, capability, fragmented prefill, decode, release, cancellation, and graceful shutdown.
- [ ] Bounded messages, deadlines, flow control, and independent session cancellation are enforced.
- [ ] The worker exposes neither llama.cpp RPC nor arbitrary graph execution.
- [ ] Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff.
- [x] A standalone C++ executable serves the complete lifecycle and stream RPC contract using the fake engine.
- [x] Python integration tests cover startup, health, capability, fragmented prefill, decode, release, cancellation, and graceful shutdown.
- [x] Bounded messages, deadlines, flow control, and independent session cancellation are enforced.
- [x] The worker exposes neither llama.cpp RPC nor arbitrary graph execution.
- [x] Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff.
## Shared quality gates
@@ -36,4 +36,4 @@ Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`,
## Evidence handoff
Write and verify `.scratch/distributed-gguf-runtime/evidence/DGR-033/README.md`. Until every criterion and applicable gate has real evidence, this story remains `passes: false`. Legacy evidence is provenance only, not completion credit.
Verified evidence: `.scratch/distributed-gguf-runtime/evidence/DGR-033/README.md`. Legacy evidence remains provenance only and grants no implementation completion credit.

View File

@@ -1,7 +1,7 @@
<!-- GENERATED FROM prd.json — DO NOT EDIT AS AN INDEPENDENT SOURCE. prd.json IS AUTHORITATIVE. -->
# DGR-034: Implement dense-Llama range-aware GGUF ownership
- **Status / triage:** specification only; `ready-for-agent`; `passes: false`
- **Status / triage:** completed; `passes: true`
- **Execution mode:** `AFK`
- **Milestone:** `M2`
- **Dependencies:** `DGR-028`, `DGR-029`, `DGR-031`
@@ -18,11 +18,11 @@ Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`,
## Acceptance criteria
- [ ] Load only `blk.N.*` tensors in the assigned range, embeddings only at the head, and norm/output or tied output only at the tail.
- [ ] Derive authoritative range and endpoint ownership from the loaded engine state.
- [ ] Reject invalid/gapped/out-of-model ranges and unexpected required tensors.
- [ ] Real-model evidence shows mapped/resident memory scales with owned tensors rather than full artifact size.
- [ ] Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff.
- [x] Load only `blk.N.*` tensors in the assigned range, embeddings only at the head, and norm/output or tied output only at the tail.
- [x] Derive authoritative range and endpoint ownership from the loaded engine state.
- [x] Reject invalid/gapped/out-of-model ranges and unexpected required tensors.
- [x] Real-model evidence shows mapped/resident memory scales with owned tensors rather than full artifact size.
- [x] Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff.
## Shared quality gates
@@ -36,4 +36,4 @@ Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`,
## Evidence handoff
Write and verify `.scratch/distributed-gguf-runtime/evidence/DGR-034/README.md`. Until every criterion and applicable gate has real evidence, this story remains `passes: false`. Legacy evidence is provenance only, not completion credit.
Verified evidence: `.scratch/distributed-gguf-runtime/evidence/DGR-034/README.md`. Legacy evidence remains provenance only and grants no implementation completion credit.

View File

@@ -1,7 +1,265 @@
{
"name": "Distributed GGUF Runtime",
"description": "Benchmark-gated distributed GGUF Shards using existing Meshnet control-plane routing and a standalone C++ gRPC worker around pinned upstream llama.cpp, targeting DeepSeek V4 Flash without hardcoded quantization or topology.",
"branchName": "ralph/distributed-gguf-runtime",
"description": "Benchmark-gated distributed GGUF Shards using existing Meshnet control-plane routing and a standalone C++ gRPC worker around pinned upstream llama.cpp, targeting DeepSeek V4 Flash without hardcoded quantization or topology.",
"sourceOfTruth": "This prd.json is authoritative. Generated issue Markdown and planning summaries are projections and must not override it. DGR-017 through DGR-033 have verified lane evidence; DGR-034 through DGR-071 remain unimplemented specifications with passes=false. Fixture evidence does not claim real model inference.",
"qualityGates": {
"universal": [
"Targeted deterministic tests pass; Python changes also pass `python -m compileall packages tests`.",
"`git diff --check` passes.",
"Default tests are model-download-free, API-credit-free, and GPU-free.",
"Evidence README records exact changed files, commands/results, limitations, and dependency handoff; no fabricated evidence or inherited completion credit."
],
"native": [
"Native changes pass focused out-of-tree CMake build and CTest; patch changes verify clean apply/check/reverse against the exact llama.cpp pin."
],
"realModelHardware": [
"Runs are opt-in and record exact artifact/split hashes, runtime/upstream pin, backend/driver, hardware, network, commands, and raw metrics. Model artifacts use configured mounted-drive storage and never `/home`."
],
"scope": [
"Preserve existing Transformers behavior and backend-agnostic Tracker routing/load balancing/billing/relay semantics unless an explicit versioned contract says otherwise. One scoped story commit is expected during execution, but this specification-materialization change is not committed."
]
},
"metadataSchema": {
"requiredStoryFields": [
"id",
"title",
"description",
"acceptanceCriteria",
"priority",
"passes",
"milestone",
"executionMode",
"labels",
"triage",
"evidenceClass",
"evidencePath",
"hardware",
"model",
"upstream",
"dependsOn",
"notes",
"blocks"
],
"optionalStoryFields": [
"completionNotes"
],
"idRange": "DGR-017..DGR-071 inclusive",
"triageValues": [
"ready-for-agent",
"ready-for-human"
],
"executionModeValues": [
"AFK",
"HITL"
],
"evidenceClassValues": [
"model-free",
"fixture",
"real-model",
"real-hardware",
"release"
],
"hardwareValues": [
"none",
"optional",
"required"
],
"upstreamValues": [
"yes",
"no",
"conditional"
],
"typeDerivation": "A story type is derived from its type:<value> label; gate:<value> stories derive release-gate.",
"labelConventions": "Reserved prefixes include type:, priority:, area:, gate:, and ready-for-agent/ready-for-human triage labels; at most one type: and one priority: label are allowed.",
"generatedArtifactDisclaimer": "<!-- GENERATED FROM prd.json \u2014 DO NOT EDIT AS AN INDEPENDENT SOURCE. prd.json IS AUTHORITATIVE. -->",
"dependencyRules": "Dependencies reference existing numerically earlier IDs; graph is acyclic. blocks is mechanically derived from dependsOn.",
"authorityRule": "Generated issue files state that prd.json is authoritative and cannot independently claim completion or override it."
},
"milestones": [
{
"id": "M0",
"name": "Truth and contracts",
"stories": "DGR-017..DGR-020",
"outcome": "Reconciled legacy truth, canonical metadata, immutable gates, and a controlled whole-model baseline."
},
{
"id": "M1",
"name": "Protocol and native substrate",
"stories": "DGR-021..DGR-033",
"outcome": "Versioned gRPC protocol, exact identities/artifacts, pinned upstream, reproducible builds, ShardEngine, and fake worker."
},
{
"id": "M2",
"name": "Dense vertical proof",
"stories": "DGR-034..DGR-043",
"outcome": "Dense ranged execution, parity, local state, worker integration, and GGUF inputs to existing routing."
},
{
"id": "M3",
"name": "DeepSeek V4 Flash alpha",
"stories": "DGR-044..DGR-054",
"outcome": "Pinned V4 adapter around upstream llama.cpp, real route certification, and pre-locked alpha decision with MTP off."
},
{
"id": "M4",
"name": "Performance and beta hardening",
"stories": "DGR-055..DGR-067",
"outcome": "Batching, backpressure, recovery, scale certification, optimization, MTP, and hardware matrix."
},
{
"id": "M5",
"name": "Release and maintenance",
"stories": "DGR-068..DGR-071",
"outcome": "Reproducible packages, upstream collaboration, beta decision, and sustainable recertification."
}
],
"supersededStories": {
"DGR-001": {
"newIds": [
"DGR-019",
"DGR-020",
"DGR-054",
"DGR-070"
],
"disposition": "Benchmark scaffold/evidence may be audited; old pass state is void."
},
"DGR-002": {
"newIds": [
"DGR-021",
"DGR-022",
"DGR-023",
"DGR-024"
],
"disposition": "Split protocol, lifecycle, code generation, and fake transport."
},
"DGR-003": {
"newIds": [
"DGR-025"
],
"disposition": "Replaced by exact artifact/runtime compatibility identity."
},
"DGR-004": {
"newIds": [
"DGR-027",
"DGR-028",
"DGR-029",
"DGR-030",
"DGR-071"
],
"disposition": "Split provenance, patch stack, builds, and maintenance."
},
"DGR-005": {
"newIds": [
"DGR-034",
"DGR-045"
],
"disposition": "Dense and V4 ownership separated."
},
"DGR-006": {
"newIds": [
"DGR-031",
"DGR-035",
"DGR-036",
"DGR-046",
"DGR-047",
"DGR-048",
"DGR-049"
],
"disposition": "Engine, dense boundary, V4 typed boundary, and local-state adapters separated."
},
"DGR-007": {
"newIds": [
"DGR-038",
"DGR-049"
],
"disposition": "Replaced by session/epoch-keyed local KV and V4 auxiliary state."
},
"DGR-008": {
"newIds": [
"DGR-032",
"DGR-033",
"DGR-037"
],
"disposition": "Old implementation/evidence absent; no completion credit transfers."
},
"DGR-009": {
"newIds": [
"DGR-040",
"DGR-041",
"DGR-042",
"DGR-043"
],
"disposition": "Supervision, registration, relay, and routing-input integration separated."
},
"DGR-010": {
"newIds": [
"DGR-036",
"DGR-039",
"DGR-052"
],
"disposition": "Fixture, dense real acceptance, and V4 parity separated."
},
"DGR-011": {
"newIds": [
"DGR-053",
"DGR-061",
"DGR-062",
"DGR-067"
],
"disposition": "Replaced by scenario-based real 2\u20134, existing-routing 10+, real 10+, and backend certification."
},
"DGR-012": {
"newIds": [
"DGR-055",
"DGR-056",
"DGR-057"
],
"disposition": "Batching, admission/backpressure, and benchmarking separated."
},
"DGR-013": {
"newIds": [
"DGR-058",
"DGR-059"
],
"disposition": "Failure semantics and restart/re-prefill recovery separated."
},
"DGR-014": {
"newIds": [
"DGR-019",
"DGR-054",
"DGR-070"
],
"disposition": "Replaced by immutable performance, alpha, and beta gates."
},
"DGR-015": {
"newIds": [
"DGR-044",
"DGR-045",
"DGR-046",
"DGR-047",
"DGR-048",
"DGR-049",
"DGR-050",
"DGR-051",
"DGR-052",
"DGR-053",
"DGR-054",
"DGR-060",
"DGR-065",
"DGR-066",
"DGR-067"
],
"disposition": "Qwen target superseded by DeepSeek V4 Flash; no old completion transfers."
},
"DGR-016": {
"newIds": [
"DGR-069",
"DGR-071"
],
"disposition": "Upstream collaboration and ongoing maintenance separated."
}
},
"userStories": [
{
"id": "DGR-017",
@@ -106,7 +364,7 @@
"Define controlled safetensors, whole-model GGUF, dense distributed GGUF, and V4 Flash distributed lanes with fixed prompts, context/output lengths, sampling, concurrency, hardware, and metrics.",
"Alpha requires correctness plus a human-approved useful-speed threshold; beta adds concurrency, long-context, failure, and sustained-throughput thresholds.",
"Separate quantization/model-fit gains from runtime, transport, batching, and kernel gains.",
"Treat quants and 24/10+ stage counts only as named certification scenarios; no product logic may hardcode them.",
"Treat quants and 2\u20134/10+ stage counts only as named certification scenarios; no product logic may hardcode them.",
"Lock thresholds and stop conditions in versioned machine-readable data before benchmark result ingestion.",
"Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff."
],
@@ -262,12 +520,12 @@
"acceptanceCriteria": [
"Pin protoc, gRPC, and plugin versions or declare a verified compatible range.",
"Generate Python and C++ bindings into out-of-tree build/package locations through documented commands.",
"Add PythonC++ round-trip and descriptor compatibility tests.",
"Add Python\u2194C++ round-trip and descriptor compatibility tests.",
"A clean checkout regenerates bindings deterministically or fails with an actionable toolchain error.",
"Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff."
],
"passes": true,
"notes": "Completed from Gitea #7 after controller provisioned and exercised the exact Python/C++ toolchains. Verified deterministic generation, native CMake/CTest, PythonC++ byte parity, compileall, and diff checks; fixed relative bootstrap prefix resolution.",
"notes": "Completed from Gitea #7 after controller provisioned and exercised the exact Python/C++ toolchains. Verified deterministic generation, native CMake/CTest, Python\u2194C++ byte parity, compileall, and diff checks; fixed relative bootstrap prefix resolution.",
"completionNotes": "Verified exact grpcio-tools 1.82.1, Protobuf 33.1, Abseil 20250814.1, and gRPC C++ 1.82.1 at commit acccf84c0df20487d64101f528e5d426541ca4e5. Mandatory Python/C++ message and service generation, native CTest, deterministic regeneration, and byte-for-byte Python/C++ parity passed; see evidence/DGR-023/README.md.",
"blocks": [
"DGR-024",
@@ -352,7 +610,7 @@
"DGR-041",
"DGR-044"
],
"completionNotes": "Completed 2026-07-17. Verified the live DGR-003-lineage identity core against every criterion: node packages/node/meshnet_node/runtime_recipe.py and the independent tracker packages/tracker/meshnet_tracker/recipe.py (pinned together by tests/data/recipe_fingerprint_vectors.json) fingerprint the source artifact SHA, tokenizer pin, architecture adapter and config digest, boundary/protocol schema versions, backend, weight quant, activation/compute dtypes, and KV dtype/layout under domain-separated digests; shards bind to exact half-open ranges with no topology or quant constants; route/handshake/session checks fail closed with structured mismatch reasons; recipes stay registered-but-dark in the tracker CertificationLedger until a real >=2-distinct-node whole-model distributed forward certifies them. Closed the one open criterion gap (runtime pin/patch stack): new packages/node/meshnet_node/runtime_pin.py derives the runtime_version axis from the DGR-027 lock manifest exact upstream commit plus a digest over the ordered patch-stack bytes failing closed on any UPSTREAM_LOCK.json/UPSTREAM_COMMIT/series/SHA256SUMS/patch-byte disagreement, and both identity implementations now reject a moving runtime_version reference. Tests: tests/test_runtime_pin_identity.py (17 passed) plus 196 passing impacted identity/admission/native-emission tests; python -m compileall and git diff --check clean. Also repaired backlog consistency left by prior sessions: added the missing DGR-022/DGR-027 completionNotes, regenerated the DGR-022/025/027 issue projections, and relocated three pre-DGR legacy GLM alpha issue files to issues/legacy/."
"completionNotes": "Completed 2026-07-17. Verified the live DGR-003-lineage identity core against every criterion: node packages/node/meshnet_node/runtime_recipe.py and the independent tracker packages/tracker/meshnet_tracker/recipe.py (pinned together by tests/data/recipe_fingerprint_vectors.json) fingerprint the source artifact SHA, tokenizer pin, architecture adapter and config digest, boundary/protocol schema versions, backend, weight quant, activation/compute dtypes, and KV dtype/layout under domain-separated digests; shards bind to exact half-open ranges with no topology or quant constants; route/handshake/session checks fail closed with structured mismatch reasons; recipes stay registered-but-dark in the tracker CertificationLedger until a real >=2-distinct-node whole-model distributed forward certifies them. Closed the one open criterion gap (runtime pin/patch stack): new packages/node/meshnet_node/runtime_pin.py derives the runtime_version axis from the DGR-027 lock manifest \u2014 exact upstream commit plus a digest over the ordered patch-stack bytes \u2014 failing closed on any UPSTREAM_LOCK.json/UPSTREAM_COMMIT/series/SHA256SUMS/patch-byte disagreement, and both identity implementations now reject a moving runtime_version reference. Tests: tests/test_runtime_pin_identity.py (17 passed) plus 196 passing impacted identity/admission/native-emission tests; python -m compileall and git diff --check clean. Also repaired backlog consistency left by prior sessions: added the missing DGR-022/DGR-027 completionNotes, regenerated the DGR-022/025/027 issue projections, and relocated three pre-DGR legacy GLM alpha issue files to issues/legacy/."
},
{
"id": "DGR-026",
@@ -419,7 +677,7 @@
"Manifest records upstream URL, exact commit, expected source archive/tree hash, license, and retrieval method.",
"Fetch tooling verifies identity before use and refuses an unpinned branch/tag.",
"Source is fetched into an ignored build workspace; no submodule, vendored source tree, or permanent fork is introduced.",
"Offline reuse is supported only after the cached trees exact identity is verified.",
"Offline reuse is supported only after the cached tree\u2019s exact identity is verified.",
"Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff."
],
"passes": true,
@@ -538,13 +796,14 @@
"Keep every backend/model/recipe lane registered-dark until a separate real-hardware certification record exists.",
"Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff."
],
"passes": false,
"passes": true,
"notes": "Generated source issue: .scratch/distributed-gguf-runtime/issues/030-add-accelerator-build-presets-and-native-ci-matrix.md; prd.json is authoritative.",
"blocks": [
"DGR-053",
"DGR-067",
"DGR-068"
]
],
"completionNotes": "Completed by agent"
},
{
"id": "DGR-031",
@@ -576,14 +835,15 @@
"Add contract tests proving fake and future llama implementations obey identical lifecycle semantics.",
"Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff."
],
"passes": false,
"passes": true,
"notes": "Generated source issue: .scratch/distributed-gguf-runtime/issues/031-introduce-the-project-owned-shardengine-interface.md; prd.json is authoritative.",
"blocks": [
"DGR-032",
"DGR-034",
"DGR-035",
"DGR-037"
]
],
"completionNotes": "Completed by agent"
},
{
"id": "DGR-032",
@@ -615,11 +875,12 @@
"Contract tests distinguish fixture evidence from real-model certification.",
"Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff."
],
"passes": false,
"passes": true,
"notes": "Generated source issue: .scratch/distributed-gguf-runtime/issues/032-implement-deterministic-fake-shardengine.md; prd.json is authoritative.",
"blocks": [
"DGR-033"
]
],
"completionNotes": "Completed by agent"
},
{
"id": "DGR-033",
@@ -653,12 +914,13 @@
"The worker exposes neither llama.cpp RPC nor arbitrary graph execution.",
"Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff."
],
"passes": false,
"passes": true,
"notes": "Generated source issue: .scratch/distributed-gguf-runtime/issues/033-build-a-standalone-fake-c-grpc-shard-worker.md; prd.json is authoritative.",
"blocks": [
"DGR-036",
"DGR-040"
]
],
"completionNotes": "Cross-review (Codex GPT-5.5) BLOCK repaired in worktree distributed-gguf-opus. Root protocol defects fixed in the native worker: (1) chunk/decode now fail closed before SessionOpen via a per-session opened flag (terminal ERROR_CODE_INTERNAL), so no activation bypasses lifecycle/cancellation/epoch/flow-control state even when an out-of-band Cancel created placeholder state; (2) flow control is negotiated with strict worker bounds (ShardRuntimeServiceImpl::NegotiateFlow mirrors native_protocol/codec.py negotiate_flow_control) and the negotiated per-session max_chunk_bytes is enforced on every bundle instead of trusting the peer proposal; (3) an in-stream ReleaseSignal now erases session state immediately; (4) SessionOpen rejects incompatible schema, artifact/recipe fingerprint, and shard-range identity and reports the worker own served fingerprint rather than echoing the caller. Nine regression tests added. Real gates on the rebuilt pinned-gRPC binary: cmake --build exit 0; ctest 2/2 passed (shard_worker_selftest, shard_protocol_conformance); tests/test_native_shard_worker.py 27 passed; DGR-024 harness + native protocol 63 passed; compileall exit 0; git diff --check clean; ldd/nm show 0 llama/ggml linkage. Evidence: .scratch/distributed-gguf-runtime/evidence/DGR-033/README.md."
},
{
"id": "DGR-034",
@@ -692,13 +954,14 @@
"Real-model evidence shows mapped/resident memory scales with owned tensors rather than full artifact size.",
"Applicable shared quality gates in `prd.json` pass, and the evidence handoff records exact commands/results, changed files, limitations, and dependency handoff."
],
"passes": false,
"passes": true,
"notes": "Generated source issue: .scratch/distributed-gguf-runtime/issues/034-implement-dense-llama-range-aware-gguf-ownership.md; prd.json is authoritative.",
"blocks": [
"DGR-035",
"DGR-037",
"DGR-051"
]
],
"completionNotes": "Completed by agent"
},
{
"id": "DGR-035",
@@ -1155,7 +1418,7 @@
"triage": "ready-for-agent",
"description": "Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`, source issue `.scratch/distributed-gguf-runtime/issues/046-define-the-v4-typed-architecture-boundary-schema.md`, and evidence READMEs for dependencies (DGR-021, DGR-045) before changing code. Inspect live source/tests rather than trusting legacy pass states. Objective: Define the exact cross-stage V4 architecture boundary while keeping per-layer attention and auxiliary caches shard-local.",
"acceptanceCriteria": [
"Define a versioned named bundle for the mHC 4×4096 residual boundary, positions, token-ID sideband where required, and schema/cache expectations.",
"Define a versioned named bundle for the mHC 4\u00d74096 residual boundary, positions, token-ID sideband where required, and schema/cache expectations.",
"Explicitly exclude per-layer CSA, HCA, SWA, indexer, compressor, KV, and MTP caches/state from the WAN boundary; those remain local to the owning shard and session/epoch.",
"Reserve typed MTP boundary fields but mark MTP execution unsupported and unroutable for alpha.",
"Fingerprint independently of quant/topology and fail closed on missing, incompatible, incorrectly shaped, or stale boundary/cache expectations.",
@@ -1194,7 +1457,7 @@
"triage": "ready-for-agent",
"description": "Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`, source issue `.scratch/distributed-gguf-runtime/issues/047-adapt-the-upstream-v4-mhc-boundary-for-ranged-ownership.md`, and evidence READMEs for dependencies (DGR-045, DGR-046) before changing code. Inspect live source/tests rather than trusting legacy pass states. Objective: Add range-boundary adapters around upstream llama.cpp V4 mHC execution without reimplementing the V4 graph or kernels.",
"acceptanceCriteria": [
"Represent and validate the upstream V4 4×4096 mHC boundary without flattening semantic axes.",
"Represent and validate the upstream V4 4\u00d74096 mHC boundary without flattening semantic axes.",
"Add only head/intermediate/tail range ownership and boundary conversion hooks around the pinned upstream llama.cpp graph.",
"Compare deterministic fixture vectors and single-process ranged outputs with upstream whole-model execution.",
"Document that llama.cpp owns V4 mHC graph/kernels and that quantized storage does not alter the logical boundary schema.",
@@ -1404,7 +1667,7 @@
},
{
"id": "DGR-053",
"title": "Certify a real 24-stage V4 route",
"title": "Certify a real 2\u20134-stage V4 route",
"priority": 37,
"milestone": "M3",
"executionMode": "HITL",
@@ -1429,7 +1692,7 @@
"triage": "ready-for-human",
"description": "Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`, source issue `.scratch/distributed-gguf-runtime/issues/053-certify-a-real-2-4-stage-v4-route.md`, and evidence READMEs for dependencies (DGR-030, DGR-043, DGR-052) before changing code. Inspect live source/tests rather than trusting legacy pass states. Objective: Prove real Tracker-selected V4 execution across physical machines before alpha.",
"acceptanceCriteria": [
"Run one documented 24-stage certification scenario using exact compatible artifacts/recipes; the count and chosen quant are evidence inputs, not product constants.",
"Run one documented 2\u20134-stage certification scenario using exact compatible artifacts/recipes; the count and chosen quant are evidence inputs, not product constants.",
"Actual CPU/GPU work executes on every stage; fake workers do not satisfy acceptance.",
"Record parity, TTFT, prefill/decode speed, seam cost, memory, cache/state isolation, cancellation, and cleanup.",
"Tracker selection remains dynamic and rejects an injected incompatible backend/recipe.",
@@ -1708,7 +1971,7 @@
"DGR-058"
],
"triage": "ready-for-agent",
"description": "Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`, source issue `.scratch/distributed-gguf-runtime/issues/060-certify-v4-long-context-state-correctness.md`, and evidence READMEs for dependencies (DGR-051, DGR-056, DGR-058) before changing code. Inspect live source/tests rather than trusting legacy pass states. Objective: Prove V4s KV and auxiliary state remain correct and bounded at long contexts.",
"description": "Fresh Ralph session: read `.scratch/distributed-gguf-runtime/RALPH-CONTEXT.md`, source issue `.scratch/distributed-gguf-runtime/issues/060-certify-v4-long-context-state-correctness.md`, and evidence READMEs for dependencies (DGR-051, DGR-056, DGR-058) before changing code. Inspect live source/tests rather than trusting legacy pass states. Objective: Prove V4\u2019s KV and auxiliary state remain correct and bounded at long contexts.",
"acceptanceCriteria": [
"Exercise pre-locked context lengths covering multiple prefill chunks and sustained decode.",
"Validate KV plus CSA/HCA/SWA/indexer/compressor state positions across every stage.",
@@ -2161,6 +2424,6 @@
}
],
"metadata": {
"updatedAt": "2026-07-22T06:44:18.107Z"
"updatedAt": "2026-07-23T08:09:17.286Z"
}
}

View File

@@ -15,16 +15,3 @@ Default mattpocock/skills label strings (`needs-triage`, `needs-info`, `ready-fo
### Domain docs
Multi-context layout: `CONTEXT-MAP.md` at root points to per-context `CONTEXT.md` files; system-wide ADRs in `docs/adr/`, context-scoped ADRs in `src/<context>/docs/adr/`. See `docs/agents/domain.md`.
## graphify
This project has a knowledge graph at graphify-out/ with god nodes, community structure, and cross-file relationships.
When the user types `/graphify`, use the installed graphify skill or instructions before doing anything else.
Rules:
- For codebase questions, first run `graphify query "<question>"` when graphify-out/graph.json exists. Use `graphify path "<A>" "<B>"` for relationships and `graphify explain "<concept>"` for focused concepts. These return a scoped subgraph, usually much smaller than GRAPH_REPORT.md or raw grep output.
- Dirty graphify-out/ files are expected after hooks or incremental updates; dirty graph files are not a reason to skip graphify. Only skip graphify if the task is about stale or incorrect graph output, or the user explicitly says not to use it.
- If graphify-out/wiki/index.md exists, use it for broad navigation instead of raw source browsing.
- Read graphify-out/GRAPH_REPORT.md only for broad architecture review or when query/path/explain do not surface enough context.
- After modifying code, run `graphify update .` to keep the graph current (AST-only, no API cost).

View File

@@ -120,5 +120,5 @@ M1: Build system + protocol (DGR-021..033)
- Ralph runs headless: reads backlog, spawns fresh Claude Code per ticket, verifies, reports
- DGR-019/020 marked `ready-for-human` — needs review before certifying
- Changes left uncommitted for review per Ralph policy (unless explicitly pushed)
- As of July 23, 2026: `autoCommit = true` in `.ralph-tui/config.toml` — the engine now commits after every completed task, and a supervisor process pushes each commit to `origin/ralph/distributed-gguf-runtime` immediately.
- `ralph-tui resume` picks up where it left off

File diff suppressed because one or more lines are too long

View File

@@ -1,125 +0,0 @@
{
"0": "Tracker Routing & Placement",
"1": "Validator Proof Audits",
"2": "Capability Admission",
"3": "Shard Artifact Download",
"4": "PyTorch Shard Execution",
"5": "Native Shard Lifecycle",
"6": "Tracker Operations & CLI",
"7": "Node Capability Reports",
"8": "Gateway Request Routing",
"9": "Activation Transport & Binary Frames",
"10": "Tracker Capability Evaluation",
"11": "Node Diagnostics",
"12": "Relay Peer Registry",
"13": "Native Worker Adapter",
"14": "KV Session Cache",
"15": "Performance Contracts",
"16": "Route Session Benchmarks",
"17": "GLM Alpha Planning",
"18": "Tracker Admin & Network APIs",
"19": "Benchmark Recipe Drivers",
"20": "Community 20",
"21": "Community 21",
"22": "Community 22",
"23": "Community 23",
"24": "Community 24",
"25": "Community 25",
"26": "Community 26",
"27": "Community 27",
"28": "Community 28",
"29": "Community 29",
"30": "Community 30",
"31": "Community 31",
"32": "Community 32",
"33": "Community 33",
"34": "Community 34",
"35": "Community 35",
"36": "Community 36",
"37": "Community 37",
"38": "Community 38",
"39": "Community 39",
"40": "Community 40",
"41": "Community 41",
"42": "Community 42",
"43": "Community 43",
"44": "Community 44",
"45": "Community 45",
"46": "Community 46",
"47": "Community 47",
"48": "Community 48",
"49": "Community 49",
"50": "Community 50",
"51": "Community 51",
"52": "Community 52",
"53": "Community 53",
"54": "Community 54",
"55": "Community 55",
"56": "Community 56",
"57": "Community 57",
"58": "Community 58",
"59": "Community 59",
"60": "Community 60",
"61": "Community 61",
"62": "Community 62",
"63": "Community 63",
"64": "Community 64",
"65": "Community 65",
"66": "Community 66",
"67": "Community 67",
"68": "Community 68",
"69": "Community 69",
"70": "Community 70",
"71": "Community 71",
"72": "Community 72",
"73": "Community 73",
"74": "Community 74",
"75": "Community 75",
"76": "Community 76",
"77": "Community 77",
"78": "Community 78",
"79": "Community 79",
"80": "Community 80",
"81": "Community 81",
"82": "Community 82",
"83": "Community 83",
"84": "Community 84",
"85": "Community 85",
"86": "Community 86",
"87": "Community 87",
"88": "Community 88",
"89": "Community 89",
"90": "Community 90",
"91": "Community 91",
"92": "Community 92",
"93": "Community 93",
"94": "Community 94",
"95": "Community 95",
"96": "Community 96",
"97": "Community 97",
"98": "Community 98",
"99": "Community 99",
"100": "Community 100",
"101": "Community 101",
"102": "Community 102",
"103": "Community 103",
"104": "Community 104",
"105": "Community 105",
"106": "Community 106",
"107": "Community 107",
"108": "Community 108",
"109": "Community 109",
"110": "Community 110",
"111": "Community 111",
"112": "Community 112",
"113": "Community 113",
"114": "Community 114",
"115": "Community 115",
"116": "Community 116",
"117": "Community 117",
"118": "Community 118",
"119": "Community 119",
"120": "Community 120",
"121": "Community 121",
"122": "Community 122"
}

View File

@@ -1 +0,0 @@
C:\Users\popov\AppData\Roaming\uv\tools\graphifyy\Scripts\python.exe

View File

@@ -1 +0,0 @@
D:\DEV\workspace\REPOS\git.d-popov.com\neuron-tai\packages

View File

@@ -1,586 +0,0 @@
# Graphify Code-Architecture Scope
This graph deliberately covers the executable `packages/` code (98 detected source/configuration files). The six package-local documentation/build artifacts were excluded from semantic extraction for this code-only pass.
# Graph Report - packages (2026-07-29)
## Corpus Check
- 106 files · ~188,575 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 2556 nodes · 5763 edges · 123 communities (100 shown, 23 thin omitted)
- Extraction: 92% EXTRACTED · 8% INFERRED · 0% AMBIGUOUS · INFERRED: 451 edges (avg confidence: 0.51)
- Token cost: 0 input · 0 output
## Community Hubs (Navigation)
- Tracker Routing & Placement
- Validator Proof Audits
- Capability Admission
- Shard Artifact Download
- PyTorch Shard Execution
- Native Shard Lifecycle
- Tracker Operations & CLI
- Node Capability Reports
- Gateway Request Routing
- Activation Transport & Binary Frames
- Tracker Capability Evaluation
- Node Diagnostics
- Relay Peer Registry
- Native Worker Adapter
- KV Session Cache
- Performance Contracts
- Route Session Benchmarks
- GLM Alpha Planning
- Tracker Admin & Network APIs
- Benchmark Recipe Drivers
- Community 20
- Community 21
- Community 22
- Community 23
- Community 24
- Community 25
- Community 26
- Community 27
- Community 28
- Community 29
- Community 30
- Community 31
- Community 32
- Community 33
- Community 34
- Community 35
- Community 36
- Community 37
- Community 38
- Community 39
- Community 40
- Community 41
- Community 42
- Community 43
- Community 44
- Community 45
- Community 46
- Community 47
- Community 48
- Community 49
- Community 50
- Community 51
- Community 52
- Community 53
- Community 54
- Community 55
- Community 56
- Community 57
- Community 58
- Community 59
- Community 60
- Community 61
- Community 62
- Community 63
- Community 64
- Community 65
- Community 66
- Community 67
- Community 68
- Community 69
- Community 70
- Community 71
- Community 72
- Community 73
- Community 74
- Community 75
- Community 76
- Community 77
- Community 78
- Community 79
- Community 80
- Community 81
- Community 82
- Community 83
- Community 84
- Community 85
- Community 86
- Community 87
- Community 88
- Community 89
- Community 90
- Community 91
- Community 92
- Community 93
- Community 94
- Community 95
- Community 96
- Community 97
- Community 98
- Community 99
- Community 100
- Community 101
- Community 102
- Community 103
- Community 104
- Community 105
- Community 106
- Community 107
- Community 109
- Community 110
- Community 111
- Community 112
- Community 113
- Community 114
- Community 115
- Community 116
- Community 117
- Community 118
- Community 119
- Community 120
- Community 121
- Community 122
## God Nodes (most connected - your core abstractions)
1. `_TrackerHandler` - 93 edges
2. `_NodeEntry` - 50 edges
3. `run_startup()` - 46 edges
4. `BillingLedger` - 45 edges
5. `TorchModelShard` - 41 edges
6. `AccountStore` - 37 edges
7. `TrackerServer` - 37 edges
8. `CapabilityReport` - 35 edges
9. `RaftNode` - 33 edges
10. `_StatsCollector` - 32 edges
## Surprising Connections (you probably didn't know these)
- `main()` --calls--> `LocalSolanaContracts` [INFERRED]
tracker/meshnet_tracker/cli.py → contracts/meshnet_contracts/__init__.py
- `main()` --calls--> `SolanaCustodialTreasury` [INFERRED]
tracker/meshnet_tracker/cli.py → contracts/meshnet_contracts/solana_adapter.py
- `_RollingCounter` --uses--> `ToplocProofClaim` [INFERRED]
tracker/meshnet_tracker/server.py → validator/meshnet_validator/audit.py
- `_RollingThroughput` --uses--> `ToplocProofClaim` [INFERRED]
tracker/meshnet_tracker/server.py → validator/meshnet_validator/audit.py
- `_ModelStats` --uses--> `ToplocProofClaim` [INFERRED]
tracker/meshnet_tracker/server.py → validator/meshnet_validator/audit.py
## Import Cycles
- None detected.
## Communities (123 total, 23 thin omitted)
### Community 0 - "Tracker Routing & Placement"
Cohesion: 0.06
Nodes (84): tracker_logger(), _add_shard_directive(), _assign_redundant_managed_nodes(), _assignment_memory_bytes(), _available_quantizations(), _billable_stream_tokens(), _coverage_gaps(), _coverage_map() (+76 more)
### Community 1 - "Validator Proof Audits"
Cohesion: 0.05
Nodes (57): ProofEncoding, build_activation_proofs(), _call_toploc(), _chunk_field(), _extract_divergence(), _load_toploc(), _proof_encoding(), Any (+49 more)
### Community 2 - "Capability Admission"
Cohesion: 0.05
Nodes (25): CapabilityState, The tracker's sanitized verdict on one node's presented proof. This is what the…, The presented proof covers exactly what the node advertised., What route formation compares. `None` when the node declares no identity., CertificationLedger, DistributedForwardEvidence, PresentedIdentity, One node's declared artifact/recipe identity, with digests re-derived here.… (+17 more)
### Community 3 - "Shard Artifact Download"
Cohesion: 0.06
Nodes (51): _allow_patterns_from_remote_index(), _allow_patterns_from_sources(), compute_shard_checksum(), _download_huggingface_subset(), _download_model_source(), download_shard(), _download_shard_from_peer(), _download_source_files() (+43 more)
### Community 4 - "PyTorch Shard Execution"
Cohesion: 0.08
Nodes (55): _active_modules_for_shard(), build_quantization_config(), _call_layer(), _causal_lm_config(), _checkpoint_tensor_name_for_model(), _config_candidates(), _decoder_attention_mask(), _embed_tokens() (+47 more)
### Community 5 - "Native Shard Lifecycle"
Cohesion: 0.06
Nodes (35): CacheExpectation, CacheResult, CancellationToken, CancelRequest, CapabilityRequest, CapabilityResponse, DeadlinePolicy, FlowControl (+27 more)
### Community 6 - "Tracker Operations & CLI"
Cohesion: 0.05
Nodes (42): HTMLParser, Logger, LogRecord, Namespace, RotatingFileHandler, TextIO, _load_env_defaults(), _load_env_file() (+34 more)
### Community 7 - "Node Capability Reports"
Cohesion: 0.06
Nodes (40): CapabilityContext, probe_capability(), Production validator: one bounded real forward through the loaded shard., What is about to be advertised, and the loaded backend that would serve it., BackendIdentity, build_capability_report(), CapabilityReport, config_fingerprint() (+32 more)
### Community 8 - "Gateway Request Routing"
Cohesion: 0.07
Nodes (50): Any, route_signature(), _admitted_nodes(), _billable_non_stream_split(), _billable_non_stream_tokens(), _capability_routable(), _clear_proxy_progress_log_state(), _effective_queue_depth() (+42 more)
### Community 9 - "Activation Transport & Binary Frames"
Cohesion: 0.06
Nodes (43): ConnectionError, MissingModelDependencyError, Tail-shard decode result: decoded text plus the raw token id. The token id lets…, Raised when optional model dependencies are not installed., TailTokenResult, _tensor_from_bfloat16_bytes(), decode_binary_frame(), encode_binary_frame() (+35 more)
### Community 10 - "Tracker Capability Evaluation"
Cohesion: 0.09
Nodes (46): absent_state(), catalogue_is_compatible(), _diagnostics(), evaluate_report(), _index(), _maybe_int(), _object(), _optional_text() (+38 more)
### Community 11 - "Node Diagnostics"
Cohesion: 0.08
Nodes (40): _backend_device(), _backend_device_name(), build_probe_input(), classify_failure(), default_load_backend(), _describe(), _describe_output(), DoctorError (+32 more)
### Community 12 - "Relay Peer Registry"
Cohesion: 0.07
Nodes (22): main(), meshnet-relay CLI entry point., PeerEntry, PeerRegistry, In-memory registry of connected gossip peers., _broadcast(), decode_binary_frame(), encode_binary_frame() (+14 more)
### Community 13 - "Native Worker Adapter"
Cohesion: 0.08
Nodes (28): ImmutableArtifactPin, NativeIdentityInputs, NativeLoadedArtifactReport, NativeNumericalRecipe, NativeSessionRejected, NativeWorkerBackendAdapter, Authoritative identity boundary for a native GGUF Shard backend. The native…, A native worker refused a ``SessionOpen`` before allocating session state. (+20 more)
### Community 14 - "KV Session Cache"
Cohesion: 0.07
Nodes (20): _cache_unsupported_for_shard(), KVCacheMiss, BaseException, True when a layer failure means session cache is unsupported, not fatal., Per-session cached state for one shard's layer range. `cache` is whatever…, TTL + LRU bounded map of session_id → SessionCacheEntry. Each node caches state…, Executable subset of a HuggingFace causal language model., Decode step: embed one new token against this head's cached session. Raises… (+12 more)
### Community 15 - "Performance Contracts"
Cohesion: 0.10
Nodes (35): baseline_from_report(), _canonical_sha256(), _cell(), ContractEvaluation, ContractThresholds, _decode_base64(), evaluate_contract(), _evaluate_recipe() (+27 more)
### Community 16 - "Route Session Benchmarks"
Cohesion: 0.09
Nodes (33): CacheMode, _activation(), assert_benchmark(), assert_performance_gate(), BenchmarkRun, BenchmarkScenario, format_summary(), main() (+25 more)
### Community 17 - "GLM Alpha Planning"
Cohesion: 0.11
Nodes (28): IndexerLayout, The locked GLM-5.2 Max alpha target: identity, resource plan, and acceptance…, ArchitectureSnapshot, Architecture-critical metadata derived from the pinned ``config.json``., kv_bytes(), NodeMemory, plan_all_tiers(), plan_route() (+20 more)
### Community 18 - "Tracker Admin & Network APIs"
Cohesion: 0.11
Nodes (11): _normalize_current_requests(), ParseResult, Gate a privileged handler; sends 401/403 and returns False on failure. 401 when…, Return head workers: worker nodes that can start inference for a model. The…, Admin session first, then the explicit enable gate — both fail closed., Privileged: forfeit a node's pending balance + record a strike (US-034).…, Dispute-auditability log for the dynamic HF-benchmarked pricing (issue 23)., Return an optimal shard assignment for a node given its hardware profile. Query… (+3 more)
### Community 19 - "Benchmark Recipe Drivers"
Cohesion: 0.12
Nodes (34): Ed25519PrivateKey, BenchmarkError, RuntimeError, Raised when a benchmark cannot be run as specified., _artifact_sha256(), build_driver(), _directory_bytes(), _gpu_offload_evidence() (+26 more)
### Community 20 - "Community 20"
Cohesion: 0.10
Nodes (23): deque, Popen, CollectionError, discover_repo_root(), _function_metadata(), Exception, Path, Opt-in tracker test runner backing the dashboard Testing tab. Security posture… (+15 more)
### Community 21 - "Community 21"
Cohesion: 0.10
Nodes (20): Random, Minimal Raft consensus for tracker shard assignments. Only shard-assignment…, choose_route(), Learned route statistics for dynamic bandit-style route selection (ADR-0021).…, Fold one completed request into the route's EWMA. Returns False (and records…, Point-in-time view of one route's learned state., All measured route samples, including pinned experiment routes., Drop routes with no samples for `prune_after_seconds`. (+12 more)
### Community 22 - "Community 22"
Cohesion: 0.10
Nodes (9): AccountStore, _hash_password(), _normalize_nickname(), Tracker user accounts: registration, login, API-key management. Accounts are…, Update display fields for an account. Pass nickname=None to clear., Return the public account view when credentials match, else None., Revoke a key owned by ``account_id``. Returns False if not owned., Thread-safe account/API-key store with SQLite persistence and event replication. (+1 more)
### Community 23 - "Community 23"
Cohesion: 0.12
Nodes (24): ProbeInput, One recipe's validation outcome, with the report it produced., A synthetic hidden-state payload in the same wire format peers send., RecipeResult, load_recipe_manifest(), _parse_recipe(), parse_recipe_manifest(), Any (+16 more)
### Community 24 - "Community 24"
Cohesion: 0.11
Nodes (31): Return a hardware profile forced to CPU execution. Keeps detected GPU metadata…, with_forced_cpu(), peer_id_from_wallet(), Build a per-node relay peer id from the wallet plus node identity. Multiple…, _assignment_bytes_per_layer(), _cap_auto_assigned_shard(), _configure_torch_threads(), _discover_relay_url() (+23 more)
### Community 25 - "Community 25"
Cohesion: 0.12
Nodes (18): ChatCompletion, _chat_completion(), Client, CostEstimate, _ModelsClient, _openai_base_url(), Any, Typed Python SDK for the meshnet OpenAI-compatible gateway. (+10 more)
### Community 26 - "Community 26"
Cohesion: 0.11
Nodes (15): Deposit, load_keypair(), Custodial Solana treasury adapter (ADR-0015, US-032/US-033). The entire on-…, Confirmed incoming USDT transfers whose signature is not yet seen.…, Send one batched transaction of USDT transfers treasury → wallets. Creates the…, Create a fresh 6-decimal mock-USDT mint (treasury = mint authority). Returns a…, Mint mock USDT to a wallet (devnet only — treasury is mint authority)., A confirmed incoming USDT transfer into the treasury token account. (+7 more)
### Community 27 - "Community 27"
Cohesion: 0.12
Nodes (22): compress_activation(), CompressionPolicies, CompressionPolicy, CompressionResult, decompress_activation(), _env_bool(), _env_float(), _env_int() (+14 more)
### Community 28 - "Community 28"
Cohesion: 0.09
Nodes (19): Lock, normalize_policy(), policy_from_env(), Return a known policy name, falling back to the default for anything else., HfPricingLog, Thread-safe SQLite-backed audit log of dynamic price changes (issue 23). Every…, _clone_model_presets(), _preset_price_keys() (+11 more)
### Community 29 - "Community 29"
Cohesion: 0.12
Nodes (28): _apply_relay_concurrency_flag(), _cmd_default(), _cmd_doctor(), _cmd_models(), _cmd_start(), _doctor_overrides(), _first_available_port(), _load_env_defaults() (+20 more)
### Community 30 - "Community 30"
Cohesion: 0.11
Nodes (14): _LoopbackHttpClientPool, _make_envelope(), _max_concurrency_from_env(), _peer_id_suffix(), HTTPConnection, Outbound relay bridge for NAT-safe node HTTP requests., Connect outbound to a relay and proxy relay HTTP requests to localhost.…, Send one relay-http-response frame; False if the socket is gone. The lock is… (+6 more)
### Community 31 - "Community 31"
Cohesion: 0.09
Nodes (17): _capability_from_registration(), _local_relay_url(), _node_id_for_registration(), _normalize_friendly_name(), Resolve the active precision a registration is routable at. Only the raw body…, Return a node-consumable ws:// URL for an embedded relay bind address., HTTP tracker that manages node registration and resolves inference routes.…, Start the shared RelayServer class in-process for tracker+relay deployments. (+9 more)
### Community 32 - "Community 32"
Cohesion: 0.09
Nodes (12): validate_quantization(), _load_backend(), Any, Path, HTTP server backed by a HuggingFace causal language model shard., The loaded backend serving `model_id` — full repo id or short name., Apply tracker shard directives (LOAD_SHARD replace, ADD_SHARD load-more)., Set the LAN-facing endpoint used for route self-detection. (+4 more)
### Community 33 - "Community 33"
Cohesion: 0.16
Nodes (25): Checksum, checksum_of(), crc32c(), decode_bundle(), decode_step_bundle(), decode_tensor(), encode_tensor(), expected_bytes() (+17 more)
### Community 34 - "Community 34"
Cohesion: 0.12
Nodes (22): _api_key_from_authorization(), _BinaryActivation, _completion_response(), _compress_body(), _decompress_body(), _estimate_token_count(), _last_message_content(), _majority_response() (+14 more)
### Community 35 - "Community 35"
Cohesion: 0.15
Nodes (23): AlphaContract, AlphaContractError, compute_contract_digest(), contract_signing_payload(), _freeze_json(), load_alpha_contract(), parse_alpha_contract(), Any (+15 more)
### Community 36 - "Community 36"
Cohesion: 0.13
Nodes (8): LogEntry, RaftNode, Leader: append and replicate an entry. Returns True when committed. Blocks…, Send AppendEntries to all peers and update commit_index on majority ack., Single Raft participant. ``apply_fn(command, payload)`` is called (under no…, Must be called with _lock held., Must be called with _lock held., Must be called with _lock held.
### Community 37 - "Community 37"
Cohesion: 0.13
Nodes (18): _build_rich_renderable(), _EMA, _format_uptime(), _gpu_stats(), is_interactive_tty(), _make_bar(), _node_stats(), _nvml_gpu_util() (+10 more)
### Community 38 - "Community 38"
Cohesion: 0.14
Nodes (25): browse_hf_hub(), detect_num_layers(), Return num_hidden_layers from HuggingFace config.json (downloads ~1 KB only)., Fetch top downloaded text-generation models from HuggingFace Hub., _ask(), _ask_int(), _ask_quant(), _ask_yn() (+17 more)
### Community 39 - "Community 39"
Cohesion: 0.08
Nodes (12): Drop session state out of band. Idempotent., Cancel out of band, on a fresh call. In-band CancelSignal is preferred, but a…, ---------------------------------------------------------------------------…, ---------------------------------------------------------------------------…, Constructor. Args: channel: A grpc.Channel., ---------------------------------------------------------------------------…, What this worker can execute. Read before a route is built., Live load and serving state. (+4 more)
### Community 40 - "Community 40"
Cohesion: 0.13
Nodes (19): check_handshake(), check_route(), check_session_open(), _coverage_gap(), explain_mismatch(), handshake_error(), Exact Model Artifact and runtime recipe identity (DGR-003). A route is a chain…, The protocol status a rejected handshake closes the stream with. Each rejection… (+11 more)
### Community 41 - "Community 41"
Cohesion: 0.13
Nodes (16): adapter_for(), Architecture, ArchitectureBoundaryAdapter, BoundaryStage, ProtocolIdentity, Enum, str, Certified architecture adapters for the public TensorBundle boundary. The… (+8 more)
### Community 42 - "Community 42"
Cohesion: 0.24
Nodes (22): GlmTargetError, load_architecture_snapshot(), _load_json(), load_target_manifest(), parse_architecture_snapshot(), _parse_shards(), parse_target_manifest(), Any (+14 more)
### Community 43 - "Community 43"
Cohesion: 0.16
Nodes (10): _canonical_json(), _fragment_bytes(), NamedTensor, _normalize_shape(), Any, Versioned activation-stream envelope for shard hops. The transport still moves…, A tensor named within a versioned activation envelope., One bounded chunk of a named tensor. (+2 more)
### Community 44 - "Community 44"
Cohesion: 0.14
Nodes (6): Read + verify a hive gossip body (HMAC per ADR-0017 §3). Fails closed: without…, Record a rolling wish-list signal for an unavailable precision., Privileged: run the same prompt through 1/2/3-node pinned routes (US-030). Data…, Privileged: honest-noise TOPLOC calibration dispatch (issue 21). Fans the same…, One node's calibration outcome: fetch its on-demand commitment, teacher-force…, _TrackerHandler
### Community 45 - "Community 45"
Cohesion: 0.18
Nodes (8): Registry wrapper for node stake, strikes, and bans., Stake, strike, and ban state for a node operator wallet., Snapshot of all known wallets (dashboard / monitoring)., ADR-0018 §6: the only reputation signal — clean audits build score slowly, a…, ADR-0018 §6: ×0.8 routing/payout weight per strike, separate from the…, ADR-0018 §6: reputation decays for wallets with no completed job in…, RegistryContract, RegistryWallet
### Community 46 - "Community 46"
Cohesion: 0.12
Nodes (22): benchmark_throughput(), benchmark_throughput_checked(), detect_hardware(), _detect_nvidia_smi_gpu_memory(), _detect_ram_mb(), _detect_torch_cuda_inventory(), _detect_windows_gpu_memory(), _detect_windows_ram_mb() (+14 more)
### Community 47 - "Community 47"
Cohesion: 0.14
Nodes (15): measure_recipe(), _PeakMemory, Event, One runtime recipe under test. ``is_reference`` marks the single recipe every…, The seam every runtime implements; the measurement core knows nothing else., Load the artifact and return its cost., Run one complete generation under the given sampling policy., Return ``(rss_bytes, vram_bytes)`` observed right now. (+7 more)
### Community 48 - "Community 48"
Cohesion: 0.25
Nodes (12): _as_mapping(), Any, ValueError, Malformed identity input. Messages name the field, never echo a payload., A revision must identify one immutable thing, not a ref that moves., Parse an identity block, re-deriving — never trusting — its fingerprint. A…, RecipeIdentityError, _require_hex64() (+4 more)
### Community 49 - "Community 49"
Cohesion: 0.19
Nodes (3): Stream tokens from an iterator as SSE chunks., Open an OpenAI-compatible SSE response and return a token emitter., _TorchHandler
### Community 50 - "Community 50"
Cohesion: 0.26
Nodes (4): _GatewayHandler, _lamports_to_sol(), ParseResult, Forward a raw request body to a head worker and relay SSE without buffering.
### Community 51 - "Community 51"
Cohesion: 0.23
Nodes (10): _as_mapping(), CapabilityReportError, _optional_text(), Any, ValueError, Inclusive layer range, matching the CLI and backend convention., Raised when report input is malformed. Messages name the offending field and…, _require_int() (+2 more)
### Community 52 - "Community 52"
Cohesion: 0.13
Nodes (17): ConcurrencyMetrics, format_summary(), Lane, main(), _mean(), _percentile(), Enum, str (+9 more)
### Community 53 - "Community 53"
Cohesion: 0.13
Nodes (14): BenchmarkPlan, build_report(), compute_drift(), DriftReport, _first_divergence(), Any, Everything measured for one recipe across every concurrency level., First successful output per prompt, at the lowest concurrency measured. Drift… (+6 more)
### Community 54 - "Community 54"
Cohesion: 0.13
Nodes (9): GossipClient, _make_envelope(), WebSocket gossip client — connects to relay, publish/subscribe to topics., Thread-safe WebSocket gossip client. Usage:: client =…, Register a sync callback for messages on topic., Send a gossip message to all peers via the relay. Thread-safe., Start the gossip client in a background thread., Block until connected to relay or timeout. Returns True if connected. (+1 more)
### Community 55 - "Community 55"
Cohesion: 0.19
Nodes (17): files_for_layer_range(), _is_head_tensor(), _is_tail_tensor(), _layer_index(), _layers_from_config(), _metadata_files(), _normalise_relative_file(), Any (+9 more)
### Community 56 - "Community 56"
Cohesion: 0.18
Nodes (6): Resolve the session token in the Authorization header, or None., Balance, usage totals, and API keys for the logged-in account., Per-request charge history for the logged-in account (billing tab)., Devnet faucet (US-040): credit the configured amount to one of the logged-in…, Admin-only: all accounts with their keys and balances., _session_cookie_header()
### Community 57 - "Community 57"
Cohesion: 0.20
Nodes (15): _canonical_sha256(), load_runtime_pin(), Path, ValueError, Canonical runtime pin identity for the recipe fingerprint (DGR-025). The recipe…, Derive the exact runtime pin from a DGR-027 lock workspace, or refuse. Refuses…, The lock workspace is missing, malformed, or internally inconsistent., One exact runtime: a name, an upstream commit, and an ordered patch stack. (+7 more)
### Community 58 - "Community 58"
Cohesion: 0.16
Nodes (8): GenerationSample, PromptSpec, One completed generation as reported by a driver. ``prefill_ms``/``decode_ms``…, The sampling policy every recipe must be given, identically. Greedy by default:…, One fixed prompt, tagged with the context length it is meant to exercise., SamplingPolicy, The current Transformers/safetensors recipe: the correctness reference.…, TransformersDriver
### Community 59 - "Community 59"
Cohesion: 0.16
Nodes (8): LoadStats, What loading the recipe cost, before any token is generated., _free_port(), _gpu_layer_config_detail(), LlamaCppServerDriver, _process_rss(), Resident bytes for a process and its children, or 0 when unobservable., The whole-model llama.cpp/GGUF recipe, driven through ``llama-server``.…
### Community 60 - "Community 60"
Cohesion: 0.15
Nodes (9): main(), meshnet-gateway CLI entry point., _GatewayHTTPServer, GatewayServer, _get_head_workers(), Any, Return head-worker endpoint URLs for this model, or empty list on failure., HTTP gateway that routes /v1/chat/completions through an ordered inference… (+1 more)
### Community 61 - "Community 61"
Cohesion: 0.15
Nodes (15): _admit_capability(), _capability_device(), _capability_refresher(), _post_json(), _probationary_status_line(), Any, CapabilityValidator, Daemon thread: sends heartbeats and re-registers automatically after tracker… (+7 more)
### Community 62 - "Community 62"
Cohesion: 0.33
Nodes (14): Fingerprint, CheckFingerprint(), path, Crc32c(), main(), ReadFile(), ReassembleUncompressed(), TestCapabilityReportVector() (+6 more)
### Community 63 - "Community 63"
Cohesion: 0.17
Nodes (10): _local_model_path(), model_metadata_for(), ModelPreset, Path, Curated list of models supported by the network with VRAM requirements., Resolve a curated name, repository, or alias case-insensitively., Return operator-facing model metadata for a HuggingFace repo., Return VRAM requirement in GB for the given quantization. (+2 more)
### Community 64 - "Community 64"
Cohesion: 0.15
Nodes (6): GenerationTelemetry, Bounded, in-process telemetry for distributed activation seams. The generation…, Attach compression work to the same bounded seam aggregate., Aggregate activation measurements for one stable Route Session., Record one activation locally and say whether a summary is due., _SeamAggregate
### Community 65 - "Community 65"
Cohesion: 0.16
Nodes (6): _Listener, _local_ip(), MdnsDiscovery, mDNS peer discovery using zeroconf (optional dependency). Falls back gracefully…, Internal zeroconf service listener., Announce this node on mDNS and discover peers on the same LAN. If `zeroconf` is…
### Community 66 - "Community 66"
Cohesion: 0.13
Nodes (5): BillingLedger, Wallets due a payout: pending ≥ threshold OR pending age ≥ max_period, never…, Settlement batches whose pending was debited but whose transaction has not been…, Thread-safe USDT ledger with SQLite persistence and event replication., Aggregate charge totals without per-request records (dashboard summary).
### Community 67 - "Community 67"
Cohesion: 0.14
Nodes (6): NodeGossip, CRDT gossip for node liveness heartbeats. Uses a last-write-wins (LWW) register…, LWW gossip table for inference-node heartbeat timestamps. ``record(node_id)``…, Record a heartbeat for *node_id* at *wall_ts* (default: now)., Merge a gossip snapshot from a peer tracker (LWW per key)., Return wall-clock timestamp of last known heartbeat, or None.
### Community 68 - "Community 68"
Cohesion: 0.21
Nodes (10): AdmissionRequirement, admit(), CapabilityAdmissionError, _diagnostics_suffix(), _mismatch(), RuntimeError, Fail-closed admission: no routable registration without a fresh matching proof.…, Return `report` if it admits `requirement`; otherwise refuse to register.… (+2 more)
### Community 69 - "Community 69"
Cohesion: 0.14
Nodes (6): Grant the one-time Caller Credit for an account (US-039). The event id is…, Bind a client wallet pubkey to an API key (US-032 deposits, C6). A wallet…, Credit an on-chain deposit exactly once. The event id embeds the transaction…, Record the on-chain transaction signature for a settlement batch., Deduct a paid-out amount from a node's pending balance (US-033 hook). ADR-0015:…, Apply peer events not yet seen locally. Returns how many applied.
### Community 70 - "Community 70"
Cohesion: 0.18
Nodes (11): _api_key_from_headers(), Resolve the caller to (role, account). Roles: "validator" (service token),…, Bind a client wallet pubkey to an API key (US-032, C6). Deposits from that…, _session_token_from_headers(), b58decode(), binding_message(), Ed25519 proof-of-ownership for client wallet binding (ADR-0017 §5, issue C6).…, Decode a base58 string (Solana/Bitcoin alphabet) to bytes. (+3 more)
### Community 71 - "Community 71"
Cohesion: 0.21
Nodes (8): _notify_slash(), Solana contract boundary for the Distributed Inference Network. The prototype…, Completed inference data consumed by fraud validators., Local slash transaction receipt., Validation event log consumed by the optimistic fraud detector., SlashReceipt, ValidationContract, ValidationEvent
### Community 72 - "Community 72"
Cohesion: 0.19
Nodes (6): ApiKeyBalance, ComputeAttribution, PaymentContract, Client API key payment account balance., On-chain work attribution recorded by the gateway after inference., Payment wrapper for funded API keys and compute attribution.
### Community 73 - "Community 73"
Cohesion: 0.17
Nodes (13): DecodeStep, encode_bundle(), encode_decode_step(), Encode a decode boundary, retaining the deliberate compact fallback., Reject an oversized complete stream frame, including protobuf overhead. Bundle…, validate_session_message_size(), canonical_decode_step(), canonical_session_request() (+5 more)
### Community 74 - "Community 74"
Cohesion: 0.18
Nodes (7): Path, Single-process stub node that returns fixed inference responses. shard_start /…, True if this node received an activation tensor since it was started., Number of binary /forward chunks handled since this node was started., _StubHTTPServer, StubNodeServer, _attach_relay_bridge()
### Community 75 - "Community 75"
Cohesion: 0.21
Nodes (11): cert_fingerprint(), generate_self_signed_cert(), make_client_ssl_context(), make_server_ssl_context(), Path, TLS certificate generation and fingerprint helpers for node-to-node comms., Return a client SSLContext. verify=False for self-signed TOFU connections., Generate a self-signed RSA-2048 cert valid for 10 years. Returns (cert_path,… (+3 more)
### Community 76 - "Community 76"
Cohesion: 0.23
Nodes (8): _ceiling(), _false_positive_rate(), _floor(), _percentile(), TOPLOC honest-noise calibration corpus (ADR-0018 consequences, issue 21).…, Fraction of the (honest, by construction) corpus that would be flagged by the…, Whether the corpus is broad enough to enable production thresholds. Alpha…, Recommended tolerance constants derived from the corpus. `exp_intersections`…
### Community 77 - "Community 77"
Cohesion: 0.20
Nodes (7): _positive_env(), PrefillTransferLimits, Bounded, ordered prefill transfer primitives. Prefill chunks mutate the…, Configuration for one ordered prefill seam., Current peers require ordered session-cache mutation, hence one ack., Hard accounting bound, including any future wider ack window., _chunk_token_count()
### Community 78 - "Community 78"
Cohesion: 0.31
Nodes (10): _cmd_config(), Print current config., config_path(), delete_config(), load_config(), Path, Persistent node configuration — stored in ~/.config/meshnet/config.json., Return parsed config dict, or None if no config file exists. (+2 more)
### Community 79 - "Community 79"
Cohesion: 0.18
Nodes (8): _detect_layers(), DoctorResult, Path, The outcome of a doctor run over one or more recipes., Write the capability report(s) as JSON. A failed run writes too., The human summary: what was validated, what to do if it failed., render_result(), write_reports()
### Community 80 - "Community 80"
Cohesion: 0.18
Nodes (6): The pinned, self-consistent GLM-5.2 ``UD-IQ1_S`` target., Reject any target whose revisions are not the ones alpha was locked against.…, One GGUF shard of the alpha artifact., require_pinned_target(), Shard, TargetManifest
### Community 81 - "Community 81"
Cohesion: 0.22
Nodes (9): _hive_digest(), is_validator_token(), Unified tracker auth primitives (ADR-0017, alpha issues 01/02/20). Two…, Headers a tracker attaches when pushing gossip to a hive peer., True only when the request carries a fresh, valid hive signature. Fails closed:…, Constant-time check of a presented bearer token against the configured…, sign_hive_request(), verify_hive_request() (+1 more)
### Community 82 - "Community 82"
Cohesion: 0.29
Nodes (3): Thread-safe registry wallet event log with SQLite persistence., _registry_wallet_with(), RegistryEventLog
### Community 83 - "Community 83"
Cohesion: 0.22
Nodes (7): BoundedPrefillSender, Event, Release accounting after cancellation or route failure. The sender deliberately…, Send lazily-produced chunks with bounded ownership and ordered acks., Forward chunks in source order, releasing each body after its ack. ``forward``…, R, T
### Community 84 - "Community 84"
Cohesion: 0.20
Nodes (8): default_flow_control(), canonical_capability_report(), canonical_payload(), Canonical conformance vectors for the native Shard protocol. Two independently-…, The canonical capability report a worker answers admission with., Serialize deterministically, so committed golden bytes are stable., Deterministic bfloat16-sized payload for the canonical tensor., serialize()
### Community 85 - "Community 85"
Cohesion: 0.25
Nodes (5): Summary returned by an epoch settlement transaction., Settlement wrapper that debits clients and credits token rewards., Return the configured manual testnet deployment targets., SettlementContract, SettlementResult
### Community 86 - "Community 86"
Cohesion: 0.39
Nodes (7): _banned_route_wallet(), _get_json(), _ModelUnavailable, Exception, HTTPError, _safe_error_body(), _TrackerUnavailable
### Community 87 - "Community 87"
Cohesion: 0.31
Nodes (4): An immutable, request-owned binary activation payload. ``body`` is always the…, TensorPayload, ActivationEnvelope, Versioned envelope for shard activation traffic.
### Community 88 - "Community 88"
Cohesion: 0.22
Nodes (6): canonical_sha256(), _digest(), SHA-256 over canonical JSON — the repository's digest convention., The artifact half of the compatibility fingerprint. Commits to the source…, The recipe half of the compatibility fingerprint., This Shard's own bytes and exact range, bound to the source artifact. The…
### Community 89 - "Community 89"
Cohesion: 0.29
Nodes (8): _detect_num_layers(), _downloaded_model_inventory(), _model_cache_path(), Path, Only checksum tiny stub shards; real model folders are too large to hash at…, Fetch num_hidden_layers from HuggingFace model config (downloads ~1 KB…, Return a cheap local inventory record without reading model file contents., _registration_shard_checksum()
### Community 90 - "Community 90"
Cohesion: 0.32
Nodes (7): _generate_keypair(), load_or_create_identity(), _peer_id_from_pubkey(), Path, Peer identity — stable peer_id and RSA keypair, persisted to disk., Return (private_key_pem, public_key_pem) for a new RSA-2048 keypair., Return identity dict with peer_id, private_key_pem, public_key_pem. Creates and…
### Community 92 - "Community 92"
Cohesion: 0.29
Nodes (4): ChunkInfo, PrefillChunk, One token-aligned slice of a prefill., PositionSpan
### Community 93 - "Community 93"
Cohesion: 0.29
Nodes (3): _LocalContractState, LocalSolanaContracts, Facade that exposes all three contract wrappers over local validator state.
### Community 94 - "Community 94"
Cohesion: 0.29
Nodes (3): _normalize_rates(), Off-chain USDT billing ledger (ADR-0015, US-031). Tracks client API-key…, Coerce a price spec into an (input_per_1k, output_per_1k) pair.
### Community 95 - "Community 95"
Cohesion: 0.40
Nodes (5): _b58encode(), load_or_create_wallet(), Path, Solana wallet management — load or generate an Ed25519 keypair. Solana keypair…, Return (secret_32, public_32, address_base58). Loads from *path* if it exists;…
### Community 96 - "Community 96"
Cohesion: 0.33
Nodes (3): Debit the client and split the fee 90/10. With…, Blended (average) per-1k rate — kept for estimators and history logs., (input_per_1k, output_per_1k) for a model (US-045).
### Community 97 - "Community 97"
Cohesion: 0.40
Nodes (3): canonical_sha256(), Stable identity of this manifest, for the DGR-003 runtime recipe., SHA-256 over canonical JSON — the repository's digest convention.
### Community 99 - "Community 99"
Cohesion: 0.50
Nodes (4): _coverage_map_detailed(), _node_health(), Per-node health detail for the availability map., Like _coverage_map but with per-node identity and health in each band. Includes…
### Community 100 - "Community 100"
Cohesion: 0.67
Nodes (3): HTTPError, Describe an HTTP rejection from the tracker, including its JSON error., _tracker_http_error_message()
## Knowledge Gaps
- **16 isolated node(s):** `meshnet-contracts`, `meshnet-gateway`, `CapabilityRequest`, `HealthRequest`, `HealthResponse` (+11 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **23 thin communities (<3 nodes) omitted from report** — run `graphify query` to explore isolated nodes.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `_coverage_map_detailed()` connect `Community 99` to `Tracker Routing & Placement`, `Community 48`, `Tracker Admin & Network APIs`?**
_High betweenness centrality (0.315) - this node is a cross-community bridge._
- **Why does `DerivativeBinding` connect `Native Worker Adapter` to `Community 40`, `Community 48`?**
_High betweenness centrality (0.261) - this node is a cross-community bridge._
- **Why does `NativeWorkerBackendAdapter` connect `Native Worker Adapter` to `Node Capability Reports`, `Node Diagnostics`, `Community 79`, `Community 48`, `Community 23`?**
_High betweenness centrality (0.186) - this node is a cross-community bridge._
- **Are the 18 inferred relationships involving `_TrackerHandler` (e.g. with `AccountStore` and `BillingLedger`) actually correct?**
_`_TrackerHandler` has 18 INFERRED edges - model-reasoned connections that need verification._
- **Are the 18 inferred relationships involving `_NodeEntry` (e.g. with `AccountStore` and `BillingLedger`) actually correct?**
_`_NodeEntry` has 18 INFERRED edges - model-reasoned connections that need verification._
- **Are the 10 inferred relationships involving `BillingLedger` (e.g. with `_ActiveProxyContext` and `_ModelStats`) actually correct?**
_`BillingLedger` has 10 INFERRED edges - model-reasoned connections that need verification._
- **Are the 9 inferred relationships involving `TorchModelShard` (e.g. with `ActivationEnvelope` and `_DirectHopClient`) actually correct?**
_`TorchModelShard` has 9 INFERRED edges - model-reasoned connections that need verification._

View File

@@ -1 +0,0 @@
1785331773.2279537

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,302 @@
"""Deterministic fake ``ShardEngine`` fixture (DGR-032).
``FakeShardEngine`` is a pure-Python, allocation-cheap subclass of
:class:`~meshnet_node.shard_engine.ShardEngine`: no llama.cpp, no native
buffers, no GPU, no filesystem or network I/O. Every prefill/decode output is
a deterministic pure function of ``(loaded range, request inputs,
idempotency_step)`` — hashed with SHA-256 — so replaying identical inputs on
a fresh session always yields byte-identical output. It exists so worker
wiring, gRPC harnesses (DGR-033), and lifecycle/session logic can be
exercised end-to-end before a real llama.cpp-backed engine (DGR-037) exists.
This is FIXTURE evidence only. ``EVIDENCE_CLASS`` is set to ``"fixture"`` (as
opposed to ``"real"``) precisely so a later story comparing engines
programmatically — DGR-036's fixture-vs-real-model parity check — can assert
it is actually comparing a fixture against a real engine rather than two
fixtures. This module proves lifecycle/session/epoch/fault-injection
semantics; it says nothing about numerical parity with a real model. Real-
model certification is DGR-036 onward (DGR-053/DGR-054 for V4 alpha).
Fault injection (delay, memory pressure, malformed output, crash) is
deterministic and opt-in via :class:`FakeShardEngineConfig`. Every knob
defaults to off, so a bare ``FakeShardEngine()`` reproduces plain
deterministic fixture behavior and passes
:func:`tests.shard_engine_contract.assert_shard_engine_contract` unmodified.
"""
from __future__ import annotations
import hashlib
import time
from dataclasses import dataclass
from typing import Callable
from .shard_engine import (
BoundaryBundle,
DecodeRequest,
EngineCapabilities,
EngineTensor,
HealthResult,
LoadRequest,
LoadResult,
MetricsResult,
PrefillRequest,
ShardEngine,
StepResult,
TokenOutput,
)
from .shard_lifecycle import CacheResult, StatusCode, StructuredStatus
__all__ = ["FakeShardEngineConfig", "FakeShardEngine", "TOKEN_ID_VOCAB_SIZE", "MALFORMED_TOKEN_ID_FLOOR"]
TOKEN_ID_VOCAB_SIZE = 50_000
# A malformed tail output is deterministically pushed past the fixture's own
# advertised vocabulary range, so a downstream consumer checking "is this
# token_id within the vocab this fixture promises" can detect it without any
# extra signalling from the engine.
MALFORMED_TOKEN_ID_FLOOR = 100_000_000
def _default_crash_exception() -> BaseException:
return RuntimeError(
"FakeShardEngine: injected crash (simulated process failure, not a StructuredStatus)"
)
@dataclass(frozen=True)
class FakeShardEngineConfig:
"""Deterministic fault-injection knobs.
Every knob is off (``0``/``None``/``False``) by default. ``sleep`` is
injectable so tests can assert a delay was requested without an actual
process sleep; ``crash_exception_factory`` is injectable so tests can
assert on a specific exception type/instance.
"""
step_delay_seconds: float = 0.0
sleep: Callable[[float], None] = time.sleep
memory_budget_bytes: int | None = None
malformed_output: bool = False
crash_after_calls: int | None = None
crash_exception_factory: Callable[[], BaseException] = _default_crash_exception
def __post_init__(self) -> None:
if self.step_delay_seconds < 0:
raise ValueError("step_delay_seconds must be non-negative")
if self.memory_budget_bytes is not None and self.memory_budget_bytes < 0:
raise ValueError("memory_budget_bytes must be non-negative")
if self.crash_after_calls is not None and self.crash_after_calls <= 0:
raise ValueError("crash_after_calls must be positive when set")
@dataclass
class _SessionState:
epoch: int
cancelled: bool = False
class FakeShardEngine(ShardEngine):
"""Deterministic fixture ``ShardEngine``. See module docstring."""
EVIDENCE_CLASS = "fixture"
def __init__(self, config: FakeShardEngineConfig | None = None) -> None:
self._config = config or FakeShardEngineConfig()
self._loaded: LoadRequest | None = None
self._sessions: dict[str, _SessionState] = {}
self._cancelled_total = 0
self._generated_tokens = 0
self._call_count = 0
self._bytes_used = 0
# -- lifecycle -----------------------------------------------------
def load(self, request: LoadRequest) -> LoadResult:
self._loaded = request
return LoadResult(
status=StructuredStatus(StatusCode.OK, "fake engine loaded"),
effective_start=request.shard_start,
architecture=str(request.recipe.get("architecture", "fake")),
)
def capabilities(self) -> EngineCapabilities:
if self._loaded is None:
return EngineCapabilities(
status=StructuredStatus(StatusCode.FAILED_PRECONDITION, "engine not loaded")
)
request = self._loaded
return EngineCapabilities(
status=StructuredStatus(StatusCode.OK, "ready"),
shard_start=request.shard_start,
shard_end=request.shard_end,
effective_start=request.shard_start,
total_layers=request.total_layers,
architecture=str(request.recipe.get("architecture", "fake")),
max_concurrent_sessions=64,
max_context_tokens=131072,
supports_mtp=False,
)
def prefill(self, request: PrefillRequest) -> StepResult:
return self._step(
session_id=request.session_id,
route_epoch=request.route_epoch,
idempotency_step=request.idempotency_step,
token_ids=request.token_ids,
input_bundle=request.input,
cache_result_on_success=CacheResult.STORED,
opens_session=True,
)
def decode(self, request: DecodeRequest) -> StepResult:
token_ids = (request.token_id,) if request.token_id is not None else None
return self._step(
session_id=request.session_id,
route_epoch=request.route_epoch,
idempotency_step=request.idempotency_step,
token_ids=token_ids,
input_bundle=request.input,
cache_result_on_success=CacheResult.HIT,
opens_session=False,
)
def cancel(self, session_id: str, *, work_id: str = "", reason: str = "") -> StructuredStatus:
session = self._sessions.get(session_id)
if session is None:
session = _SessionState(epoch=0)
self._sessions[session_id] = session
if not session.cancelled:
self._cancelled_total += 1
session.cancelled = True
return StructuredStatus(StatusCode.CANCELLED, reason or "fake engine: session cancelled")
def release(self, session_id: str) -> StructuredStatus:
self._sessions.pop(session_id, None)
return StructuredStatus(StatusCode.OK, "fake engine: session released")
def health(self) -> HealthResult:
loaded = self._loaded is not None
return HealthResult(
status=StructuredStatus(StatusCode.OK, "ok"),
serving=loaded,
state="SERVING" if loaded else "NOT_LOADED",
active_sessions=len(self._sessions),
)
def metrics(self) -> MetricsResult:
return MetricsResult(
status=StructuredStatus(StatusCode.OK, "ok"),
active_sessions=len(self._sessions),
queued_frames=0,
inflight_bytes=0,
kv_entries=len(self._sessions),
generated_tokens=self._generated_tokens,
cancelled_sessions=self._cancelled_total,
)
# -- shared step machinery ------------------------------------------
def _step(
self,
*,
session_id: str,
route_epoch: int,
idempotency_step: int,
token_ids: tuple[int, ...] | None,
input_bundle: BoundaryBundle | None,
cache_result_on_success: CacheResult,
opens_session: bool,
) -> StepResult:
if self._loaded is None:
return StepResult(status=StructuredStatus(StatusCode.FAILED_PRECONDITION, "engine not loaded"))
self._call_count += 1
if self._config.crash_after_calls is not None and self._call_count == self._config.crash_after_calls:
raise self._config.crash_exception_factory()
session = self._sessions.get(session_id)
if session is None:
if not opens_session:
return StepResult(
status=StructuredStatus(StatusCode.NOT_FOUND, "no cached session state for decode"),
cache_result=CacheResult.MISS,
)
session = _SessionState(epoch=route_epoch)
self._sessions[session_id] = session
if session.cancelled:
return StepResult(status=StructuredStatus(StatusCode.CANCELLED, "session cancelled"))
if route_epoch < session.epoch:
return StepResult(status=StructuredStatus(StatusCode.FAILED_PRECONDITION, "stale route epoch"))
session.epoch = route_epoch
if self._config.step_delay_seconds:
self._config.sleep(self._config.step_delay_seconds)
seed = self._seed_bytes(token_ids, input_bundle)
self._bytes_used += len(seed)
budget = self._config.memory_budget_bytes
if budget is not None and self._bytes_used > budget:
return StepResult(
status=StructuredStatus(
StatusCode.RESOURCE_EXHAUSTED,
"fake engine memory pressure budget exceeded",
retryable=True,
details={"memory_budget_bytes": str(budget), "bytes_used": str(self._bytes_used)},
)
)
output = self._transform(seed, idempotency_step, input_bundle)
if isinstance(output, TokenOutput):
self._generated_tokens += 1
return StepResult(status=StructuredStatus(StatusCode.OK, "ok"), cache_result=cache_result_on_success, output=output)
@staticmethod
def _seed_bytes(token_ids: tuple[int, ...] | None, bundle: BoundaryBundle | None) -> bytes:
if token_ids:
seed = b"".join(int(t).to_bytes(8, "big") for t in token_ids)
elif bundle is not None:
seed = b"".join(tensor.data for tensor in bundle.tensors)
if bundle.token_id_sideband:
seed += b"".join(int(t).to_bytes(8, "big") for t in bundle.token_id_sideband)
else:
seed = b""
return seed
def _transform(
self, seed: bytes, idempotency_step: int, input_bundle: BoundaryBundle | None
) -> BoundaryBundle | TokenOutput:
assert self._loaded is not None
digest = hashlib.sha256(seed + idempotency_step.to_bytes(8, "big")).digest()
loaded = self._loaded
is_tail = loaded.shard_end >= loaded.total_layers - 1
is_head = loaded.shard_start == 0
if is_tail:
token_id = int.from_bytes(digest[:4], "big") % TOKEN_ID_VOCAB_SIZE
if self._config.malformed_output:
token_id = MALFORMED_TOKEN_ID_FLOOR + token_id
return TokenOutput(token_id=token_id)
boundary_point = "post_head_residual" if is_head else "post_middle_residual"
architecture = (
input_bundle.architecture if input_bundle is not None else str(loaded.recipe.get("architecture", "fake"))
)
data = digest
if self._config.malformed_output:
architecture = f"malformed:{architecture}"
data = digest[:1]
tensor = EngineTensor(
name="hidden_states",
shape=(1, max(len(seed) // 8, 1)),
dtype="bfloat16",
data=data,
)
token_id_sideband = input_bundle.token_id_sideband if input_bundle is not None else None
return BoundaryBundle(
tensors=(tensor,),
architecture=architecture,
boundary_point=boundary_point,
token_id_sideband=token_id_sideband,
)

View File

@@ -0,0 +1,218 @@
"""Authoritative dense-Llama owned-range reports from the loaded engine state.
DGR-034 loads only the tensors a shard range owns through the Meshnet
owned-range loader (``llama_model_params::meshnet_owned_layer_start/end`` in
the pinned llama.cpp patch stack). The project-owned ``meshnet-range-report``
native tool runs that load and prints a JSON document derived from the loaded
model state — the registered tensor set and the backend buffers — never from
caller-asserted values. This module is the strict consumer of that document:
it parses it into :class:`OwnedRangeReport` and fails closed on any
inconsistency, so a range or endpoint claim that the loaded engine state does
not back is rejected before it can reach identity, admission, or routing.
Ownership contract enforced here (dense Llama only):
- every registered ``blk.N.*`` tensor lies inside the half-open owned range
``[start, end)``, and every layer in that range is present — a gapped or
out-of-range registration is rejected;
- ``token_embd.weight`` is registered only by the head shard (``start == 0``),
or by a tail shard whose model ties the output head to the embedding
(``end == n_layer`` and no separate ``output.weight``);
- ``output_norm.weight`` and ``output.weight`` are registered only by the
tail shard (``end == n_layer``);
- any other registered tensor name is unexpected and rejected;
- byte counts are consistent: an mmap load maps a file span at least the
registered tensor bytes and at most the artifact size; a non-mmap load
reports a resident allocation at least the registered tensor bytes.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Mapping
class RangeReportError(ValueError):
"""A range report is malformed, or the loaded state breaks ownership."""
_DENSE_ARCHITECTURE = "llama"
_INT_FIELDS = (
"n_layer",
"file_bytes",
"mapped_bytes",
"resident_bytes",
"registered_tensors",
"registered_bytes",
)
_BOOL_FIELDS = (
"mmap",
"touched",
"has_token_embeddings",
"has_output_head",
"tied_output_head",
)
@dataclass(frozen=True)
class OwnedRangeReport:
"""One validated owned-range load, derived from loaded engine state.
``start_layer``/``end_layer`` are the authoritative half-open owned range
the engine actually registered (the tool already refused a report whose
loaded bounds differ from the requested ones). ``has_token_embeddings`` is
true for the head shard, and also for a tail shard on a tied-output model
(the embedding tensor *is* its output head); ``tied_output_head``
disambiguates those two cases. ``mapped_bytes``/``resident_bytes`` come
from the backend buffers: with mmap they are the mapped file span holding
the owned tensors, without mmap the resident allocation holding them.
"""
architecture: str
n_layer: int
start_layer: int
end_layer: int
has_token_embeddings: bool
has_output_head: bool
tied_output_head: bool
mapped_bytes: int
resident_bytes: int
registered_tensors: int
registered_bytes: int
file_bytes: int
mmap: bool
touched: bool
vm_size_bytes: int | None
vm_rss_bytes: int | None
vm_hwm_bytes: int | None
@property
def is_head(self) -> bool:
return self.start_layer == 0
@property
def is_tail(self) -> bool:
return self.end_layer == self.n_layer
def __post_init__(self) -> None:
if self.architecture != _DENSE_ARCHITECTURE:
raise RangeReportError(
f"owned-range loading supports dense Llama only, got {self.architecture!r}"
)
if isinstance(self.n_layer, bool) or self.n_layer < 1:
raise RangeReportError("report must record a positive GGUF block count")
for name in _INT_FIELDS:
value = getattr(self, name)
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
raise RangeReportError(f"report field {name!r} must be a non-negative integer")
for name in _BOOL_FIELDS:
if not isinstance(getattr(self, name), bool):
raise RangeReportError(f"report field {name!r} must be a boolean")
if not 0 <= self.start_layer < self.end_layer <= self.n_layer:
raise RangeReportError(
f"owned range [{self.start_layer}, {self.end_layer}) is empty or "
f"outside the model's {self.n_layer} layers"
)
if self.tied_output_head and not self.is_tail:
raise RangeReportError("a tied output head can only belong to the tail shard")
expected_embeddings = self.is_head or self.tied_output_head
if self.has_token_embeddings != expected_embeddings:
raise RangeReportError(
"token-embedding registration disagrees with endpoint ownership: "
"embeddings belong to the head shard (or to a tied-output tail)"
)
if self.has_output_head != self.is_tail:
raise RangeReportError(
"output-head registration disagrees with endpoint ownership: "
"the final norm and output head belong to the tail shard"
)
if self.registered_tensors < 1 or self.registered_bytes < 1:
raise RangeReportError("the owned range registered no tensors")
if self.file_bytes < 1:
raise RangeReportError("report must record the artifact size")
if self.mmap:
if self.mapped_bytes < self.registered_bytes:
raise RangeReportError(
"mapped span undercounts the registered owned tensors"
)
if self.mapped_bytes > self.file_bytes:
raise RangeReportError("mapped span exceeds the artifact size")
else:
if self.mapped_bytes != 0:
raise RangeReportError("a non-mmap load must not claim a mapped span")
if self.resident_bytes < self.registered_bytes:
raise RangeReportError(
"resident allocation undercounts the registered owned tensors"
)
for name in ("vm_size_bytes", "vm_rss_bytes", "vm_hwm_bytes"):
value = getattr(self, name)
if value is not None and (
isinstance(value, bool) or not isinstance(value, int) or value < 0
):
raise RangeReportError(f"report field {name!r} must be a non-negative integer or null")
def _require_range(doc: Mapping[str, Any], key: str) -> tuple[int, int]:
value = doc.get(key)
if (
not isinstance(value, (list, tuple))
or len(value) != 2
or any(isinstance(v, bool) or not isinstance(v, int) for v in value)
):
raise RangeReportError(f"report field {key!r} must be a [start, end] integer pair")
return value[0], value[1]
def parse_owned_range_report(doc: Mapping[str, Any]) -> OwnedRangeReport:
"""Parse and validate one ``meshnet-range-report`` JSON document.
Fails closed: a load the tool rejected (``ok: false``), a requested range
the loaded state did not match, a gapped or out-of-range registration, an
unexpected registered tensor, and any byte-count inconsistency all raise
:class:`RangeReportError` instead of producing a report.
"""
if not isinstance(doc, Mapping):
raise RangeReportError("range report must be a JSON object")
if doc.get("ok") is not True:
error = doc.get("error")
detail = f": {error}" if isinstance(error, str) and error else ""
raise RangeReportError(f"the owned-range load was rejected{detail}")
requested = _require_range(doc, "requested_range")
reported = _require_range(doc, "reported_range")
if requested != reported:
raise RangeReportError(
f"reported range {reported} does not match the requested range {requested}; "
"ownership must be derived from the loaded engine state"
)
for key in ("unexpected_registered_tensors", "missing_owned_layers"):
value = doc.get(key)
if not isinstance(value, list):
raise RangeReportError(f"report field {key!r} must be a list")
if value:
raise RangeReportError(
f"ownership audit failed: {key} is {value!r}; the registered "
"tensor set must exactly cover the owned range and its endpoints"
)
architecture = doc.get("architecture")
if not isinstance(architecture, str):
raise RangeReportError("report field 'architecture' must be a string")
fields: dict[str, Any] = {}
for name in _INT_FIELDS + _BOOL_FIELDS:
if name not in doc:
raise RangeReportError(f"range report is missing field {name!r}")
fields[name] = doc[name]
for name in ("vm_size_bytes", "vm_rss_bytes", "vm_hwm_bytes"):
fields[name] = doc.get(name)
return OwnedRangeReport(
architecture=architecture,
start_layer=reported[0],
end_layer=reported[1],
**fields,
)

View File

@@ -0,0 +1,372 @@
"""The project-owned ``ShardEngine`` contract (DGR-031).
A worker process (the gRPC surface in ``shard_runtime_server.py``, or any
future transport) never talks to llama.cpp directly. It talks to a
``ShardEngine``. This module is the *only* place that boundary is defined, and
every operation on it is built from project-owned dataclasses and plain
Python values (``str``, ``int``, ``bytes``, ``Mapping``) — never a
``ggml_tensor``, a llama context/scheduler handle, or a generated-protobuf
(ABI) message. A fake fixture engine (DGR-032) and a real llama.cpp-backed
engine (DGR-037) are both, structurally, nothing more than subclasses of
:class:`ShardEngine`; the worker code that calls them does not change when one
replaces the other.
This is deliberately a fourth, distinct layer from the three that already
exist:
- ``native_protocol`` — the generated gRPC/Protobuf wire ABI (DGR-021/024).
- ``protocol.ActivationEnvelope`` — the versioned wire envelope for activation
traffic between shard *hops* over the network (DGR-021).
- ``shard_lifecycle`` — the versioned RPC/session lifecycle contract a
generated gRPC binding consumes (DGR-022).
``ShardEngine`` sits *inside* one worker process, below all three: it is the
seam between "the code that speaks Meshnet's wire protocol" and "the code
that actually runs model layers." It reuses :class:`~meshnet_node.shard_lifecycle.StructuredStatus`,
:class:`~meshnet_node.shard_lifecycle.StatusCode`, :class:`~meshnet_node.shard_lifecycle.CacheExpectation`,
and :class:`~meshnet_node.shard_lifecycle.CacheResult` rather than inventing a
parallel status vocabulary, since those are already project-owned and
version-stable.
"""
from __future__ import annotations
import abc
from dataclasses import dataclass, field
from typing import Any, Mapping
from .shard_lifecycle import (
CacheExpectation,
CacheResult,
StatusCode,
StructuredStatus,
)
__all__ = [
"EngineError",
"EngineTensor",
"BoundaryBundle",
"TokenOutput",
"MtpHook",
"ArchitectureAuxStateHook",
"LoadRequest",
"LoadResult",
"EngineCapabilities",
"PrefillRequest",
"DecodeRequest",
"StepResult",
"HealthResult",
"MetricsResult",
"ShardEngine",
]
class EngineError(RuntimeError):
"""An engine-boundary failure represented by a structured status.
Mirrors :class:`~meshnet_node.shard_lifecycle.LifecycleContractError`:
callers pattern-match on ``error.status.code`` rather than on exception
subclasses, so a fake and a real engine can fail the exact same way for
the exact same reason.
"""
def __init__(self, status: StructuredStatus) -> None:
self.status = status
super().__init__(status.message)
@dataclass(frozen=True)
class EngineTensor:
"""One named tensor crossing the engine boundary.
Intentionally not a ``ggml_tensor`` or a framework tensor object: ``data``
is plain owned bytes, ``shape``/``dtype`` are plain metadata. An
implementation constructs this from whatever internal representation it
uses (a ``torch.Tensor``, a llama.cpp buffer, a synthetic fixture array)
without leaking that representation across the boundary.
"""
name: str
shape: tuple[int, ...]
dtype: str
data: bytes
def __post_init__(self) -> None:
if not self.name:
raise ValueError("engine tensor requires a name")
if not self.shape or any(dim <= 0 for dim in self.shape):
raise ValueError("engine tensor shape must be a non-empty tuple of positive ints")
if not self.dtype:
raise ValueError("engine tensor requires a dtype")
@dataclass(frozen=True)
class BoundaryBundle:
"""A named-tensor activation crossing a shard boundary (head/middle/tail-in).
``token_id_sideband`` carries token IDs alongside the activation only
where the architecture boundary requires them (V4's first three
hash-routed MoE layers); it is ``None`` everywhere else. Per-shard hot
KV/recurrent/CSA/HCA/SWA/indexer/compressor state never appears here — it
stays local to a shard via :class:`ArchitectureAuxStateHook` and is never
part of what crosses the wire.
"""
tensors: tuple[EngineTensor, ...]
architecture: str
boundary_point: str
token_id_sideband: tuple[int, ...] | None = None
def __post_init__(self) -> None:
if not self.tensors:
raise ValueError("boundary bundle requires at least one tensor")
if not self.architecture:
raise ValueError("boundary bundle requires an architecture name")
if not self.boundary_point:
raise ValueError("boundary bundle requires a boundary point name")
def tensor(self, name: str) -> EngineTensor:
for tensor in self.tensors:
if tensor.name == name:
return tensor
raise KeyError(name)
@dataclass(frozen=True)
class TokenOutput:
"""A tail shard's sampled decode result.
Never a raw logits tensor: the engine boundary only ever hands back the
already-sampled token (mirroring
:meth:`meshnet_node.architecture_boundary.TailOutput.sampled_token`, which
likewise refuses anything but a sampled token id).
"""
token_id: int
text: str | None = None
def __post_init__(self) -> None:
if self.token_id < 0:
raise ValueError("sampled token id must be non-negative")
@dataclass(frozen=True)
class MtpHook:
"""Reserved multi-token-prediction hook — typed, but refused when enabled.
RALPH-CONTEXT is explicit that "MTP is reserved and off for alpha; its
ownership contract, implementation, and benchmark are required before
beta" (DGR-065/DGR-066). Reserving the shape now means DGR-037's real
engine and DGR-051's V4 adapter do not have to change this dataclass's
field layout later; they only flip ``enabled`` once DGR-066 lands.
"""
enabled: bool = False
draft_token_count: int = 0
aux_state: Mapping[str, Any] | None = None
def __post_init__(self) -> None:
if self.enabled:
raise ValueError(
"MTP is reserved and must remain disabled before DGR-066; "
"this hook exists to fix its shape, not to enable it"
)
if self.draft_token_count < 0:
raise ValueError("draft_token_count must be non-negative")
@dataclass(frozen=True)
class ArchitectureAuxStateHook:
"""Reserved per-shard architecture auxiliary-state hook.
Covers V4's CSA/HCA/SWA/indexer/compressor state and any other
architecture-local state a future adapter needs. RALPH-CONTEXT locks this
as shard-local, keyed by route session/epoch, and explicitly never carried
over the WAN seam — so this hook has no wire encoding of its own and must
never be embedded inside a :class:`BoundaryBundle`.
"""
kind: str = ""
state: Mapping[str, Any] | None = None
@dataclass(frozen=True)
class LoadRequest:
"""One exact artifact/recipe/range identity for a worker to load."""
artifact_path: str
shard_start: int
shard_end: int
total_layers: int
recipe: Mapping[str, Any] = field(default_factory=dict)
def __post_init__(self) -> None:
if not self.artifact_path:
raise ValueError("load request requires an artifact path")
if self.shard_start < 0 or self.shard_end < self.shard_start:
raise ValueError("shard_start must be <= shard_end and non-negative")
if self.total_layers <= self.shard_end:
raise ValueError("total_layers must exceed shard_end (shard_end is inclusive)")
@dataclass(frozen=True)
class LoadResult:
status: StructuredStatus
effective_start: int = 0
architecture: str = ""
@dataclass(frozen=True)
class EngineCapabilities:
status: StructuredStatus
shard_start: int = 0
shard_end: int = 0
effective_start: int = 0
total_layers: int = 0
architecture: str = ""
max_concurrent_sessions: int = 0
max_context_tokens: int = 0
supports_mtp: bool = False
@property
def is_head(self) -> bool:
return self.shard_start == 0
@property
def is_tail(self) -> bool:
return self.shard_end >= self.total_layers - 1
@dataclass(frozen=True)
class PrefillRequest:
"""A prefill step. Exactly one of ``token_ids`` (head) or ``input`` (middle/tail) is set."""
session_id: str
route_epoch: int
position: int
idempotency_step: int
token_ids: tuple[int, ...] | None = None
input: BoundaryBundle | None = None
cache_expectation: CacheExpectation = CacheExpectation.NONE
mtp: MtpHook = field(default_factory=MtpHook)
architecture_aux_state: ArchitectureAuxStateHook | None = None
def __post_init__(self) -> None:
_require_exactly_one_input(self.token_ids, self.input)
if not self.session_id:
raise ValueError("prefill request requires a session id")
if self.route_epoch < 0 or self.position < 0 or self.idempotency_step < 0:
raise ValueError("route_epoch, position, and idempotency_step must be non-negative")
@dataclass(frozen=True)
class DecodeRequest:
"""A decode step. Exactly one of ``token_id`` (head) or ``input`` (middle/tail) is set."""
session_id: str
route_epoch: int
position: int
idempotency_step: int
token_id: int | None = None
input: BoundaryBundle | None = None
mtp: MtpHook = field(default_factory=MtpHook)
architecture_aux_state: ArchitectureAuxStateHook | None = None
def __post_init__(self) -> None:
_require_exactly_one_input(
None if self.token_id is None else (self.token_id,), self.input
)
if not self.session_id:
raise ValueError("decode request requires a session id")
if self.route_epoch < 0 or self.position < 0 or self.idempotency_step < 0:
raise ValueError("route_epoch, position, and idempotency_step must be non-negative")
def _require_exactly_one_input(
token_ids: tuple[int, ...] | None, bundle: BoundaryBundle | None
) -> None:
if (token_ids is None) == (bundle is None):
raise ValueError("exactly one of token ids or a boundary bundle must be set")
@dataclass(frozen=True)
class StepResult:
"""The result of a prefill or decode step.
``output`` is a :class:`BoundaryBundle` for a head/middle shard handing an
activation to the next hop, or a :class:`TokenOutput` for a tail shard
that sampled a token. It is ``None`` only when ``status.code`` is not
``OK``.
"""
status: StructuredStatus
cache_result: CacheResult = CacheResult.NOT_REQUESTED
output: BoundaryBundle | TokenOutput | None = None
def __post_init__(self) -> None:
if self.status.code is StatusCode.OK and self.output is None:
raise ValueError("a successful step result must carry an output")
@dataclass(frozen=True)
class HealthResult:
status: StructuredStatus
serving: bool = False
state: str = "UNKNOWN"
active_sessions: int = 0
@dataclass(frozen=True)
class MetricsResult:
status: StructuredStatus
active_sessions: int = 0
queued_frames: int = 0
inflight_bytes: int = 0
kv_entries: int = 0
generated_tokens: int = 0
cancelled_sessions: int = 0
class ShardEngine(abc.ABC):
"""The contract every shard execution engine (fake or real) must implement.
Every method returns a project-owned result carrying a
:class:`~meshnet_node.shard_lifecycle.StructuredStatus` rather than
raising for expected, protocol-visible outcomes (a cache miss, a stale
epoch, an unknown session); an :class:`EngineError` is reserved for
genuine programming errors at the call site (malformed request objects),
which the request dataclasses' own ``__post_init__`` validation already
catches before an implementation ever sees them.
"""
@abc.abstractmethod
def load(self, request: LoadRequest) -> LoadResult:
"""Load one exact artifact/recipe/range identity. Idempotent per engine instance."""
@abc.abstractmethod
def capabilities(self) -> EngineCapabilities:
"""Report this engine's authoritative range and limits after ``load``."""
@abc.abstractmethod
def prefill(self, request: PrefillRequest) -> StepResult:
"""Run one prefill step for a session."""
@abc.abstractmethod
def decode(self, request: DecodeRequest) -> StepResult:
"""Run one decode step for a session."""
@abc.abstractmethod
def cancel(self, session_id: str, *, work_id: str = "", reason: str = "") -> StructuredStatus:
"""Cancel a session (or one work item within it) in flight."""
@abc.abstractmethod
def release(self, session_id: str) -> StructuredStatus:
"""Release a session's held state. Idempotent."""
@abc.abstractmethod
def health(self) -> HealthResult:
"""Report liveness/serving state. Must never raise."""
@abc.abstractmethod
def metrics(self) -> MetricsResult:
"""Report point-in-time operational counters. Must never raise."""

View File

@@ -62,6 +62,21 @@ message(STATUS "Pinned gRPC ${gRPC_VERSION}: building ShardRuntime service stubs
enable_testing()
# The standalone fake Shard worker (DGR-033): a real gRPC server over the
# ShardRuntime service, backed by the model-free FakeShardEngine. It links the
# grpc service stubs only — no llama.cpp, no graph-execution entry point.
add_executable(shard_worker
worker/shard_worker_main.cpp
worker/shard_service.cpp)
target_include_directories(shard_worker PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/worker")
target_link_libraries(shard_worker PRIVATE shard_runtime_grpc gRPC::grpc++)
# Pure-C++ CTest: the worker binds an ephemeral port, self-drives the full
# lifecycle (capability, health, fragmented prefill, decode, release) over a
# real loopback gRPC channel, and exits non-zero on any mismatch. This proves
# the worker serves the contract without needing a Python environment.
add_test(NAME shard_worker_selftest COMMAND shard_worker --selftest)
add_executable(shard_protocol_conformance tests/test_shard_protocol_conformance.cpp)
target_link_libraries(shard_protocol_conformance PRIVATE shard_runtime_proto)

View File

@@ -28,6 +28,12 @@ One numbered patch per concern (ADR-0024 local seams only):
5. `0005-worker-range-report-hook.patch` (worker hooks) exposes the
`llama_model_meshnet_range_report` C API the project-owned worker binds to
and registers a model-free native fixture test for it.
6. `0006-meshnet-range-report-tool.patch` (range reporting) adds the
project-owned `meshnet-range-report` tool: it loads one GGUF artifact
through the owned-range loader and prints a JSON document derived from the
loaded model state — the owned-range report, the registered tensor set
audited against the requested ownership, and backend-buffer byte counts.
It never builds or runs a compute graph.
Meshnet routing, Tracker, gRPC, relay, billing, authentication, and telemetry
remain outside this directory; the stack is checked for such control-plane

View File

@@ -10,21 +10,23 @@
"method": "git-clone-detached-commit",
"workspace": "build/llama.cpp"
},
"patched_tree": "c0045714735ae5ee7b7334a480d8ac04e03e1b18",
"patched_tree": "8f7e87fea6743f0b9744afe44f9e6f9ca3b7d08a",
"upstream_license": "MIT",
"patch_series": [
"0001-cmake-reserve-meshnet-patch-stack-abi-marker.patch",
"0002-dense-llama-owned-range-loading.patch",
"0003-owned-range-filtered-state-report.patch",
"0004-dense-boundary-io-endpoint-guard.patch",
"0005-worker-range-report-hook.patch"
"0005-worker-range-report-hook.patch",
"0006-meshnet-range-report-tool.patch"
],
"patch_scope": [
"Reserved CMake ABI marker only; no execution or model semantics.",
"Range loading: dense-Llama owned-range params, validation, and filtered tensor registration with endpoint ownership.",
"Filtered state: owned-range report populated from registered tensors and backend buffers, derived never asserted.",
"Boundary I/O: endpoint ownership flags and a fail-closed dense graph guard until typed endpoint adapters exist.",
"Worker hooks: public C range-report API and the model-free native fixture test the project-owned worker binds to."
"Worker hooks: public C range-report API and the model-free native fixture test the project-owned worker binds to.",
"Range reporting: project-owned tool that loads one artifact through the owned-range loader and reports derived ownership and buffer-byte state as JSON."
],
"patch_assumptions": "patches/UPSTREAM-ASSUMPTIONS.json",
"build": {
@@ -46,12 +48,30 @@
"-DGGML_VULKAN=OFF",
"-DGGML_METAL=OFF"
],
"native_targets": ["llama-gguf-hash", "test-meshnet-range-ownership"],
"native_targets": ["llama-gguf-hash", "test-meshnet-range-ownership", "meshnet-range-report"],
"smoke_binary": "bin/llama-gguf-hash",
"smoke_args": ["--help"],
"smoke_output_token": "usage",
"ctest_regex": "^test-meshnet-range-ownership$"
},
"accelerator_presets": {
"cuda": {
"backend_flag": "GGML_CUDA",
"sdk_probe": {"binary": "nvcc", "env_var": "CUDACXX"}
},
"rocm": {
"backend_flag": "GGML_HIP",
"sdk_probe": {"binary": "hipcc", "env_var": "HIPCXX"}
},
"vulkan": {
"backend_flag": "GGML_VULKAN",
"sdk_probe": {"binary": "glslc", "env_var": "VULKAN_SDK_GLSLC"}
},
"metal": {
"backend_flag": "GGML_METAL",
"sdk_probe": {"binary": "xcrun", "platform_only": "darwin"}
}
},
"required_upstream_blobs": {
"CMakeLists.txt": "81f23d7e70b7378511af5d01be680c03aebc2b15"
},
@@ -63,7 +83,9 @@
"src/llama-model.h",
"src/models/llama.cpp",
"tests/CMakeLists.txt",
"tests/test-meshnet-range-ownership.cpp"
"tests/test-meshnet-range-ownership.cpp",
"tools/meshnet-range-report/CMakeLists.txt",
"tools/meshnet-range-report/meshnet-range-report.cpp"
],
"stock_glm_limitations": "This pin may load GLM-5.2 through the dense-MLA compatibility fallback. It does not prove native DSA, IndexShare, MoE semantic correctness, numerical equivalence, performance, or route certification."
}

View File

@@ -0,0 +1,414 @@
From: Meshnet <meshnet@invalid>
Subject: [PATCH] llama: add dense-Llama owned-range report tool
Concern: range reporting. Adds the project-owned meshnet-range-report tool:
it loads one GGUF artifact through the Meshnet owned-range loader and prints
a JSON document derived from the loaded model state — the owned-range
report, the registered tensor set audited against the requested ownership,
and backend-buffer byte counts (optionally split from repack buffers, plus
process resident readings). It never builds or runs a compute graph and
never trusts caller-asserted range or endpoint claims.
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a9afcff..868793b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -281,3 +281,6 @@ configure_file(cmake/llama.pc.in
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/llama.pc"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+
+# Meshnet-owned owned-range report tool (patch stack, range-report concern).
+add_subdirectory(tools/meshnet-range-report)
diff --git a/tools/meshnet-range-report/CMakeLists.txt b/tools/meshnet-range-report/CMakeLists.txt
new file mode 100644
index 000000000..24401007e
--- /dev/null
+++ b/tools/meshnet-range-report/CMakeLists.txt
@@ -0,0 +1,7 @@
+# Meshnet-owned dense-Llama owned-range load/report tool.
+#
+# Built unconditionally with the patched tree: it exercises the Meshnet
+# owned-range loader against real GGUF artifacts and reports only state
+# derived from the loaded model (registered tensors, backend buffers).
+add_executable(meshnet-range-report meshnet-range-report.cpp)
+target_link_libraries(meshnet-range-report PRIVATE llama)
diff --git a/tools/meshnet-range-report/meshnet-range-report.cpp b/tools/meshnet-range-report/meshnet-range-report.cpp
new file mode 100644
index 000000000..49a5eb2a0
--- /dev/null
+++ b/tools/meshnet-range-report/meshnet-range-report.cpp
@@ -0,0 +1,373 @@
+// Meshnet-owned dense-Llama owned-range load/report tool.
+//
+// Loads one GGUF artifact through the Meshnet owned-range loader
+// (llama_model_params::meshnet_owned_layer_start/end) and prints a single
+// JSON report derived from the loaded model state — registered tensors and
+// backend buffers, never caller-asserted values. The audit fails closed when
+// the registered tensor set disagrees with the requested ownership: every
+// registered per-layer tensor must lie inside [start, end), the token
+// embedding may be registered only by the head shard (start == 0) or by a
+// tail shard whose model ties the output head to the embedding, and the
+// final norm plus output head may be registered only by the tail shard
+// (end == n_layer).
+
+#include "ggml.h"
+#include "llama.h"
+
+#include "../../src/llama-model.h"
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <set>
+#include <string>
+#include <sys/stat.h>
+#include <vector>
+
+namespace {
+
+constexpr int kExitUsage = 2;
+constexpr int kExitLoad = 3;
+constexpr int kExitAudit = 4;
+
+std::string g_log_tail;
+
+void capture_log(enum ggml_log_level level, const char * text, void *) {
+ if (level >= GGML_LOG_LEVEL_ERROR) {
+ g_log_tail += text;
+ if (g_log_tail.size() > 512) {
+ g_log_tail.erase(0, g_log_tail.size() - 512);
+ }
+ }
+}
+
+std::string json_escape(const std::string & value) {
+ std::string out;
+ for (const char c : value) {
+ if (c == '"' || c == '\\') {
+ out += '\\';
+ out += c;
+ } else if (c == '\n') {
+ out += "\\n";
+ } else if (c == '\r') {
+ // drop carriage returns from embedded log text
+ } else {
+ out += c;
+ }
+ }
+ return out;
+}
+
+std::string json_string_array(const std::vector<std::string> & items) {
+ std::string out = "[";
+ for (size_t i = 0; i < items.size(); ++i) {
+ if (i) {
+ out += ", ";
+ }
+ out += "\"" + json_escape(items[i]) + "\"";
+ }
+ return out + "]";
+}
+
+std::string json_int_array(const std::vector<int> & items) {
+ std::string out = "[";
+ for (size_t i = 0; i < items.size(); ++i) {
+ if (i) {
+ out += ", ";
+ }
+ out += std::to_string(items[i]);
+ }
+ return out + "]";
+}
+
+int fail(int code, const std::string & error) {
+ std::string detail = error;
+ if (!g_log_tail.empty()) {
+ detail += ": " + g_log_tail;
+ }
+ std::printf("{\"ok\": false, \"error\": \"%s\"}\n", json_escape(detail).c_str());
+ return code;
+}
+
+bool parse_nonnegative(const char * text, int & out) {
+ if (text == nullptr || *text == '\0' || *text == '-') {
+ return false;
+ }
+ char * end = nullptr;
+ const long value = std::strtol(text, &end, 10);
+ if (end == text || *end != '\0' || value > INT32_MAX) {
+ return false;
+ }
+ out = static_cast<int>(value);
+ return true;
+}
+
+uint64_t file_size(const std::string & path) {
+ struct stat st;
+ return ::stat(path.c_str(), &st) == 0 ? static_cast<uint64_t>(st.st_size) : 0;
+}
+
+struct proc_status {
+ uint64_t vm_size = 0;
+ uint64_t vm_rss = 0;
+ uint64_t vm_hwm = 0;
+ bool valid = false;
+};
+
+proc_status read_proc_status() {
+ proc_status out;
+#ifdef __linux__
+ FILE * f = std::fopen("/proc/self/status", "r");
+ if (!f) {
+ return out;
+ }
+ char line[256];
+ while (std::fgets(line, sizeof(line), f)) {
+ uint64_t kb = 0;
+ if (std::sscanf(line, "VmSize: %lu kB", &kb) == 1) {
+ out.vm_size = kb * 1024;
+ } else if (std::sscanf(line, "VmRSS: %lu kB", &kb) == 1) {
+ out.vm_rss = kb * 1024;
+ } else if (std::sscanf(line, "VmHWM: %lu kB", &kb) == 1) {
+ out.vm_hwm = kb * 1024;
+ }
+ }
+ std::fclose(f);
+ out.valid = true;
+#endif
+ return out;
+}
+
+void usage(const char * argv0) {
+ std::fprintf(stderr,
+ "usage: %s --model PATH --start N --end M [--no-mmap] [--no-extra-bufts] [--touch]\n"
+ "loads one dense-Llama GGUF through the Meshnet owned-range loader and\n"
+ "prints a JSON report derived from the loaded model state\n",
+ argv0);
+}
+
+} // namespace
+
+int main(int argc, char ** argv) {
+ std::string model_path;
+ int start = -1;
+ int end = -1;
+ bool use_mmap = true;
+ bool use_extra_bufts = true;
+ bool touch = false;
+
+ for (int i = 1; i < argc; ++i) {
+ const std::string arg = argv[i];
+ if (arg == "--model" && i + 1 < argc) {
+ model_path = argv[++i];
+ } else if (arg == "--start" && i + 1 < argc) {
+ if (!parse_nonnegative(argv[++i], start)) {
+ usage(argv[0]);
+ return kExitUsage;
+ }
+ } else if (arg == "--end" && i + 1 < argc) {
+ if (!parse_nonnegative(argv[++i], end)) {
+ usage(argv[0]);
+ return kExitUsage;
+ }
+ } else if (arg == "--no-mmap") {
+ use_mmap = false;
+ } else if (arg == "--no-extra-bufts") {
+ use_extra_bufts = false;
+ } else if (arg == "--touch") {
+ touch = true;
+ } else {
+ usage(argv[0]);
+ return kExitUsage;
+ }
+ }
+ if (model_path.empty() || start < 0 || end < 0) {
+ usage(argv[0]);
+ return kExitUsage;
+ }
+
+ llama_log_set(capture_log, nullptr);
+ llama_backend_init();
+
+ llama_model_params params = llama_model_default_params();
+ params.meshnet_owned_layer_start = start;
+ params.meshnet_owned_layer_end = end;
+ params.use_mmap = use_mmap;
+ params.use_extra_bufts = use_extra_bufts;
+ params.progress_callback = nullptr;
+
+ llama_model * model = llama_model_load_from_file(model_path.c_str(), params);
+ if (model == nullptr) {
+ return fail(kExitLoad, "owned-range load rejected the artifact or range");
+ }
+
+ llama_meshnet_range_report report = {};
+ if (!llama_model_meshnet_range_report(model, &report)) {
+ llama_model_free(model);
+ return fail(kExitLoad, "loaded model carries no owned-range report");
+ }
+
+ char arch_buf[128] = {};
+ std::string arch;
+ if (llama_model_meta_val_str(model, "general.architecture", arch_buf, sizeof(arch_buf)) >= 0) {
+ arch = arch_buf;
+ }
+ const int n_layer = llama_model_n_layer(model);
+ const uint64_t bytes_on_disk = file_size(model_path);
+
+ // Audit the registered tensor set against the requested ownership.
+ const auto & tensors = llama_internal_get_tensor_map(model);
+ bool has_embd = false;
+ bool has_out_norm = false;
+ bool has_out = false;
+ std::set<int> owned_layers;
+ std::vector<std::string> unexpected;
+ uint64_t registered_bytes = 0;
+ for (const auto & entry : tensors) {
+ const std::string & name = entry.first;
+ registered_bytes += ggml_nbytes(entry.second);
+ if (name == "token_embd.weight") {
+ has_embd = true;
+ continue;
+ }
+ if (name == "output_norm.weight") {
+ has_out_norm = true;
+ continue;
+ }
+ if (name == "output.weight") {
+ has_out = true;
+ continue;
+ }
+ int block = -1;
+ if (std::sscanf(name.c_str(), "blk.%d.", &block) == 1 && block >= 0) {
+ owned_layers.insert(block);
+ continue;
+ }
+ unexpected.push_back(name);
+ }
+
+ // A tail shard whose model ties the output head to the token embedding
+ // registers token_embd.weight as its output head instead of output.weight.
+ const bool tied_tail = end == n_layer && has_embd && !has_out;
+ const bool expect_embd = start == 0 || tied_tail;
+
+ std::vector<int> missing_layers;
+ for (int i = start; i < end; ++i) {
+ if (!owned_layers.count(i)) {
+ missing_layers.push_back(i);
+ }
+ }
+ std::vector<int> outside_layers;
+ for (const int block : owned_layers) {
+ if (block < start || block >= end) {
+ outside_layers.push_back(block);
+ }
+ }
+
+ std::vector<std::string> mismatches;
+ if (report.start_layer != start || report.end_layer != end) {
+ mismatches.push_back("reported range differs from the requested range");
+ }
+ if (has_embd != expect_embd) {
+ mismatches.push_back("token-embedding registration disagrees with endpoint ownership");
+ }
+ if ((end == n_layer) && !has_out_norm) {
+ mismatches.push_back("tail range is missing the final norm");
+ }
+ if ((end == n_layer) && !has_out && !has_embd) {
+ mismatches.push_back("tail range is missing the output head");
+ }
+ if ((end != n_layer) && (has_out_norm || has_out)) {
+ mismatches.push_back("non-tail range registered tail-only tensors");
+ }
+ if (report.has_token_embeddings != has_embd) {
+ mismatches.push_back("reported embedding ownership disagrees with registered tensors");
+ }
+ if (report.has_output_head != (end == n_layer)) {
+ mismatches.push_back("reported output-head ownership disagrees with endpoint ownership");
+ }
+ if (!missing_layers.empty()) {
+ mismatches.push_back("owned range has missing per-layer tensors");
+ }
+ if (!outside_layers.empty()) {
+ mismatches.push_back("registered per-layer tensors lie outside the owned range");
+ }
+ if (!unexpected.empty()) {
+ mismatches.push_back("registered tensors outside the dense-Llama ownership vocabulary");
+ }
+ if (use_mmap && report.mapped_bytes < registered_bytes) {
+ mismatches.push_back("mapped span undercounts the registered tensors");
+ }
+ if (!use_mmap && report.resident_bytes < registered_bytes) {
+ mismatches.push_back("resident allocation undercounts the registered tensors");
+ }
+
+ if (touch) {
+ volatile uint64_t sink = 0;
+ for (const auto & entry : tensors) {
+ const auto * data = static_cast<const volatile uint8_t *>(entry.second->data);
+ const size_t nbytes = ggml_nbytes(entry.second);
+ for (size_t i = 0; i < nbytes; i += 4096) {
+ sink += data[i];
+ }
+ }
+ (void) sink;
+ }
+
+ const proc_status proc = read_proc_status();
+
+ if (!mismatches.empty()) {
+ llama_model_free(model);
+ return fail(kExitAudit, "ownership audit failed: " + json_string_array(mismatches));
+ }
+
+ std::printf(
+ "{\n"
+ " \"ok\": true,\n"
+ " \"model\": \"%s\",\n"
+ " \"architecture\": \"%s\",\n"
+ " \"n_layer\": %d,\n"
+ " \"file_bytes\": %llu,\n"
+ " \"requested_range\": [%d, %d],\n"
+ " \"reported_range\": [%d, %d],\n"
+ " \"mmap\": %s,\n"
+ " \"touched\": %s,\n"
+ " \"use_extra_bufts\": %s,\n"
+ " \"has_token_embeddings\": %s,\n"
+ " \"has_output_head\": %s,\n"
+ " \"tied_output_head\": %s,\n"
+ " \"mapped_bytes\": %llu,\n"
+ " \"resident_bytes\": %llu,\n"
+ " \"registered_tensors\": %d,\n"
+ " \"registered_bytes\": %llu,\n"
+ " \"unexpected_registered_tensors\": [],\n"
+ " \"missing_owned_layers\": [],\n"
+ " \"vm_size_bytes\": %llu,\n"
+ " \"vm_rss_bytes\": %llu,\n"
+ " \"vm_hwm_bytes\": %llu\n"
+ "}\n",
+ json_escape(model_path).c_str(),
+ json_escape(arch).c_str(),
+ n_layer,
+ (unsigned long long) bytes_on_disk,
+ start, end,
+ report.start_layer, report.end_layer,
+ use_mmap ? "true" : "false",
+ touch ? "true" : "false",
+ use_extra_bufts ? "true" : "false",
+ report.has_token_embeddings ? "true" : "false",
+ report.has_output_head ? "true" : "false",
+ tied_tail ? "true" : "false",
+ (unsigned long long) report.mapped_bytes,
+ (unsigned long long) report.resident_bytes,
+ (int) tensors.size(),
+ (unsigned long long) registered_bytes,
+ (unsigned long long) proc.vm_size,
+ (unsigned long long) proc.vm_rss,
+ (unsigned long long) proc.vm_hwm);
+
+ llama_model_free(model);
+ llama_backend_free();
+ return 0;
+}

View File

@@ -4,3 +4,4 @@
4871a37544df658980a01b4f94151a90b609fb144c931b4a814309ee608ebb46 0003-owned-range-filtered-state-report.patch
19d451ce259150ffede793c4eb547425375c0fcd97caf326b43e8f1a204f05b6 0004-dense-boundary-io-endpoint-guard.patch
cf263357a6a8de193f710836c7c467c38cac7099975303ee2628e0609daf5a47 0005-worker-range-report-hook.patch
23b4b8c56243d52ba682f0034022a86bf8ded007885be5b659cf5158ff3eb429 0006-meshnet-range-report-tool.patch

View File

@@ -112,6 +112,30 @@
"llama_internal_get_tensor_map(const llama_model *) in src/llama-model.h",
"gguf empty-context writer API: gguf_init_empty, gguf_add_tensor, gguf_write_to_file"
]
},
"0006-meshnet-range-report-tool.patch": {
"concern": "range-reporting",
"files": {
"CMakeLists.txt": {
"before": "a9afcffa68bed7cbd8fad39ad9f95ad784251234",
"after": "868793b826f565df7f041e7ba55820b5ad744b10"
},
"tools/meshnet-range-report/CMakeLists.txt": {
"before": null,
"after": "24401007ee85e217c2741a42c7119fad323ff08a"
},
"tools/meshnet-range-report/meshnet-range-report.cpp": {
"before": null,
"after": "49a5eb2a05bf6514e166453ea0e35b8bc9c5fdf6"
}
},
"api_assumptions": [
"llama_model_params carries meshnet_owned_layer_start/end, use_mmap, and use_extra_bufts",
"llama_model_meshnet_range_report C API and llama_meshnet_range_report fields (patch 0005)",
"llama_internal_get_tensor_map(const llama_model *) in src/llama-model.h",
"llama_model_meta_val_str and llama_model_n_layer public accessors",
"top-level CMakeLists add_subdirectory of a project-owned tool directory after the llama target"
]
}
}
}

View File

@@ -3,3 +3,4 @@
0003-owned-range-filtered-state-report.patch
0004-dense-boundary-io-endpoint-guard.patch
0005-worker-range-report-hook.patch
0006-meshnet-range-report-tool.patch

View File

@@ -0,0 +1,165 @@
// Deterministic, model-free fake ShardEngine for the native worker (DGR-033).
//
// This is the C++ analogue of `meshnet_node.fake_shard_engine.FakeShardEngine`
// (DGR-032): a pure fixture that performs a *bounded real forward* over the
// bytes it received off the socket and never links, loads, or dispatches to
// llama.cpp. It exists to prove the standalone worker process, stream,
// lifecycle, and supervision shape before any real engine is bound (DGR-037).
//
// The "forward" is deliberately transport-verifiable rather than semantic: it
// reassembles a tensor's fragments, checks they tile exactly, and derives a
// CRC32C over the uncompressed bytes — the same rule the schema's `Checksum`
// declares and the same bounded forward the DGR-024 Python surface performs.
// Feeding the same bytes back (echo) lets a client prove the payload truly
// traversed the wire and returned unmodified; a direct hop and an opaque relay
// of the identical frames therefore yield byte-identical responses.
//
// There is no arbitrary-graph entry point here and no llama.cpp RPC: the engine
// only knows how to reassemble/checksum a bundle. That is the whole point of a
// fixture worker (acceptance criterion 4).
#ifndef MESHNET_NATIVE_WORKER_FAKE_ENGINE_H_
#define MESHNET_NATIVE_WORKER_FAKE_ENGINE_H_
#include <algorithm>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include "shard_runtime.pb.h"
namespace meshnet::worker {
namespace sp = ::meshnet::shard::v1;
// Standard CRC-32 (ISO-HDLC / zlib polynomial 0xEDB88320, reflected).
//
// The schema's `Checksum` field is labelled CRC32C, but the DGR-024 Python
// runtime surface (`shard_runtime_server.py`) computes it with `zlib.crc32`
// (standard CRC-32, not the Castagnoli CRC32C). This worker deliberately mirrors
// that exact computation so its checksum acceptance is byte-for-byte identical
// to the existing Python gRPC surface and to a relayed frame's expectations.
inline uint32_t Crc32(const std::string& data, uint32_t seed = 0) {
static uint32_t table[256];
static bool built = false;
if (!built) {
for (uint32_t i = 0; i < 256; ++i) {
uint32_t c = i;
for (int k = 0; k < 8; ++k) {
c = (c & 1) ? (c >> 1) ^ 0xEDB88320u : (c >> 1);
}
table[i] = c;
}
built = true;
}
uint32_t crc = seed ^ 0xFFFFFFFFu;
for (unsigned char byte : data) {
crc = (crc >> 8) ^ table[(crc ^ byte) & 0xFF];
}
return crc ^ 0xFFFFFFFFu;
}
// Outcome of validating one bundle before the bounded forward runs.
struct BundleCheck {
// Set when the bundle is malformed/corrupt (maps to PAYLOAD_CORRUPT).
std::optional<std::string> corrupt_detail;
// Set when the declared payload exceeds the negotiated per-chunk ceiling
// (maps to RESOURCE_EXHAUSTED) — the worker refuses unbounded messages.
std::optional<std::string> oversize_detail;
};
// The fake engine's only capability: verify a bundle tiles and checksums, and
// that it stays within the negotiated byte ceiling. Mirrors `_validate_bundle`
// in `shard_runtime_server.py` plus the bounded-message rule DGR-033 adds.
class FakeShardEngine {
public:
// Marker mirroring `FakeShardEngine.EVIDENCE_CLASS` so a future parity check
// (DGR-036) can assert this is a fixture, not a real engine.
static constexpr const char* kEvidenceClass = "fixture";
FakeShardEngine() = default;
// `max_chunk_bytes` is the per-session *negotiated* ceiling (the strictest of
// the worker's own limit and the peer's proposal), passed in on every call so
// the engine enforces exactly what the SessionOpen handshake settled — never a
// value the peer proposed unilaterally.
BundleCheck Validate(const sp::TensorBundle& bundle, uint64_t max_chunk_bytes) const {
BundleCheck result;
for (const auto& tensor : bundle.tensors()) {
// Bounded message: a declared payload larger than the ceiling is refused
// before any reassembly work is done.
if (max_chunk_bytes != 0 && tensor.total_bytes() > max_chunk_bytes) {
result.oversize_detail =
"tensor '" + tensor.name() + "': declared total_bytes " +
std::to_string(tensor.total_bytes()) + " exceeds max_chunk_bytes " +
std::to_string(max_chunk_bytes);
return result;
}
// Fragments must tile the wire body exactly: no hole, no overlap.
std::vector<const sp::TensorFragment*> ordered;
ordered.reserve(tensor.fragments_size());
for (const auto& fragment : tensor.fragments()) {
ordered.push_back(&fragment);
}
std::sort(ordered.begin(), ordered.end(),
[](const sp::TensorFragment* a, const sp::TensorFragment* b) {
return a->byte_offset() < b->byte_offset();
});
uint64_t expected_offset = 0;
std::string payload;
for (const auto* fragment : ordered) {
if (fragment->byte_offset() != expected_offset) {
result.corrupt_detail =
"tensor '" + tensor.name() + "': fragment at offset " +
std::to_string(fragment->byte_offset()) +
" does not tile the preceding " + std::to_string(expected_offset) +
" bytes (gap or overlap)";
return result;
}
payload.append(fragment->payload());
expected_offset += fragment->payload().size();
}
if (tensor.compression() == sp::COMPRESSION_NONE &&
expected_offset != tensor.total_bytes()) {
result.corrupt_detail =
"tensor '" + tensor.name() + "': fragments cover " +
std::to_string(expected_offset) + " bytes, declared total_bytes is " +
std::to_string(tensor.total_bytes());
return result;
}
if (tensor.compression() == sp::COMPRESSION_NONE &&
tensor.checksum().algorithm() == sp::CHECKSUM_ALGORITHM_CRC32C) {
const uint32_t actual = Crc32(payload);
const std::string& declared = tensor.checksum().value();
std::string actual_be(4, '\0');
actual_be[0] = static_cast<char>((actual >> 24) & 0xFF);
actual_be[1] = static_cast<char>((actual >> 16) & 0xFF);
actual_be[2] = static_cast<char>((actual >> 8) & 0xFF);
actual_be[3] = static_cast<char>(actual & 0xFF);
if (declared != actual_be) {
result.corrupt_detail = "tensor '" + tensor.name() + "': checksum mismatch";
return result;
}
}
}
return result;
}
// Bounded real forward: fold every fragment's payload through CRC32C so the
// digest is only reproducible if the payload really traversed the wire.
uint32_t BoundedForward(const sp::TensorBundle& bundle) const {
uint32_t digest = 0;
for (const auto& tensor : bundle.tensors()) {
for (const auto& fragment : tensor.fragments()) {
digest = Crc32(fragment.payload(), digest);
}
}
return digest;
}
};
} // namespace meshnet::worker
#endif // MESHNET_NATIVE_WORKER_FAKE_ENGINE_H_

View File

@@ -0,0 +1,470 @@
#include "shard_service.h"
#include <algorithm>
#include <chrono>
#include <utility>
namespace meshnet::worker {
namespace {
// The exact identity this fixture worker serves. SessionOpen is validated
// against these — not echoed back from the caller — so an incompatible peer
// fails closed at open rather than being silently accepted with its own claimed
// identity. Kept in one place so GetCapability and the open handshake agree.
constexpr const char* kModelArtifactDigest = "sha256:native-test-artifact";
constexpr const char* kRuntimeRecipeDigest = "sha256:native-test-recipe";
constexpr const char* kRecipeId = "native-test";
constexpr const char* kRecipeVersion = "1";
constexpr const char* kCatalogueVersion = "1";
constexpr uint32_t kShardStartLayer = 0;
constexpr uint32_t kShardEndLayer = 32;
constexpr uint32_t kShardEffectiveStartLayer = 0;
void FillWorkerFingerprint(sp::Fingerprint* fp) {
fp->set_model_artifact_digest(kModelArtifactDigest);
fp->set_runtime_recipe_digest(kRuntimeRecipeDigest);
fp->set_recipe_id(kRecipeId);
fp->set_recipe_version(kRecipeVersion);
fp->set_catalogue_version(kCatalogueVersion);
}
void FillWorkerShardRange(sp::ShardRange* range) {
range->set_start_layer(kShardStartLayer);
range->set_end_layer(kShardEndLayer);
range->set_effective_start_layer(kShardEffectiveStartLayer);
}
// Strictest-of-both bound: the smallest positive of `a`/`b`, or `fallback` when
// neither is set. Mirrors the `_min` helper in `native_protocol/codec.py`.
uint64_t MinPositive(uint64_t a, uint64_t b, uint64_t fallback) {
if (a > 0 && b > 0) return std::min(a, b);
if (a > 0) return a;
if (b > 0) return b;
return fallback;
}
int64_t NowUnixNanos() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
}
// Build the standard fail response (a terminal-or-not ShardStatus).
sp::SessionResponse MakeFail(const std::string& route_session_id, const std::string& work_id,
uint64_t step, sp::ErrorCode code, const std::string& detail,
bool terminal, bool retryable) {
sp::SessionResponse response;
sp::ShardStatus* status = response.mutable_status();
status->set_work_id(work_id);
status->set_route_session_id(route_session_id);
status->set_idempotency_step(step);
status->set_terminal(terminal);
sp::ShardError* error = status->mutable_error();
error->set_code(code);
error->set_detail(detail);
error->set_retryable(retryable);
return response;
}
sp::SessionResponse MakeAck(const std::string& work_id, uint64_t step, bool duplicate) {
sp::SessionResponse response;
sp::Ack* ack = response.mutable_ack();
ack->set_work_id(work_id);
ack->set_idempotency_step(step);
ack->set_duplicate(duplicate);
return response;
}
void FillDefaultFlow(sp::FlowControl* fc, const FlowLimits& limits) {
fc->set_credits_granted(limits.credits_granted);
fc->set_max_inflight_chunks(limits.max_inflight_chunks);
fc->set_max_chunk_bytes(limits.max_chunk_bytes);
fc->set_max_prefill_chunk_tokens(limits.max_prefill_chunk_tokens);
}
} // namespace
grpc::Status ShardRuntimeServiceImpl::GetCapability(grpc::ServerContext*,
const sp::CapabilityRequest*,
sp::CapabilityReport* response) {
response->set_schema_version(sp::SCHEMA_VERSION_1);
FillWorkerFingerprint(response->mutable_fingerprint());
FillWorkerShardRange(response->mutable_shard_range());
response->set_backend("grpc-native-cpp");
response->set_device("cpu");
response->set_validated(true);
response->set_detail("bounded real forward passed for fixture artifact");
response->set_max_concurrent_sessions(8);
response->set_max_context_tokens(131072);
FillDefaultFlow(response->mutable_flow_control(), limits_);
response->add_accepted_compression(sp::COMPRESSION_NONE);
response->add_supported_schema_versions(sp::SCHEMA_VERSION_1);
response->set_validated_at_unix_nanos(0);
return grpc::Status::OK;
}
grpc::Status ShardRuntimeServiceImpl::Health(grpc::ServerContext*, const sp::HealthRequest*,
sp::HealthReport* response) {
response->set_schema_version(sp::SCHEMA_VERSION_1);
response->set_state(sp::SERVING_STATE_SERVING);
response->set_active_sessions(1);
response->set_queued_chunks(0);
response->set_batch_occupancy(0);
response->set_kv_pressure(0.0f);
response->set_resident_bytes(0);
response->set_detail("native fixture worker serving");
return grpc::Status::OK;
}
FlowLimits ShardRuntimeServiceImpl::NegotiateFlow(const sp::FlowControl& proposed) const {
FlowLimits out;
out.max_inflight_chunks = static_cast<uint32_t>(MinPositive(
proposed.max_inflight_chunks(), limits_.max_inflight_chunks, limits_.max_inflight_chunks));
const uint64_t credits = MinPositive(proposed.credits_granted(), limits_.credits_granted,
limits_.credits_granted);
out.credits_granted =
static_cast<uint32_t>(std::min<uint64_t>(credits, out.max_inflight_chunks));
out.max_chunk_bytes =
MinPositive(proposed.max_chunk_bytes(), limits_.max_chunk_bytes, limits_.max_chunk_bytes);
out.max_prefill_chunk_tokens = static_cast<uint32_t>(MinPositive(
proposed.max_prefill_chunk_tokens(), limits_.max_prefill_chunk_tokens,
limits_.max_prefill_chunk_tokens));
return out;
}
uint32_t ShardRuntimeServiceImpl::MarkCancelled(const std::string& route_session_id,
const std::string& work_id) {
std::lock_guard<std::mutex> lk(sessions_mu_);
SessionState& state = sessions_[route_session_id]; // creates on first cancel-before-open
if (state.max_inflight == 0) {
// Freshly created placeholder for a Cancel that raced ahead of Open.
state.credits = limits_.credits_granted;
state.max_inflight = limits_.max_inflight_chunks;
state.max_chunk_bytes = limits_.max_chunk_bytes;
}
if (work_id.empty()) {
const bool already = state.cancelled_session;
state.cancelled_session = true;
return already ? 0 : 1;
}
const bool already = state.cancelled_work.count(work_id) != 0;
state.cancelled_work.insert(work_id);
return already ? 0 : 1;
}
grpc::Status ShardRuntimeServiceImpl::Session(
grpc::ServerContext*,
grpc::ServerReaderWriter<sp::SessionResponse, sp::SessionRequest>* stream) {
std::string route_session_id;
sp::SessionRequest request;
while (stream->Read(&request)) {
switch (request.kind_case()) {
case sp::SessionRequest::kOpen: {
const sp::SessionOpen& open = request.open();
route_session_id = open.route_session_id();
// Reject an incompatible peer at open rather than mid-generation. The
// worker validates the caller's schema, artifact/recipe identity and
// requested layer range against its own — it never adopts the caller's
// claimed identity.
auto reject_open = [&](sp::ErrorCode code, const std::string& detail) {
stream->Write(MakeFail(route_session_id, /*work_id=*/"", /*step=*/0, code, detail,
/*terminal=*/true, /*retryable=*/false));
};
if (open.schema_version() != sp::SCHEMA_VERSION_1) {
reject_open(sp::ERROR_CODE_SCHEMA_UNSUPPORTED,
"worker serves schema version 1 only");
return grpc::Status::OK;
}
const sp::Fingerprint& fp = open.fingerprint();
if ((!fp.model_artifact_digest().empty() &&
fp.model_artifact_digest() != kModelArtifactDigest) ||
(!fp.runtime_recipe_digest().empty() &&
fp.runtime_recipe_digest() != kRuntimeRecipeDigest)) {
reject_open(sp::ERROR_CODE_FINGERPRINT_MISMATCH,
"model artifact or runtime recipe digest does not match this worker");
return grpc::Status::OK;
}
if (open.has_shard_range()) {
const sp::ShardRange& r = open.shard_range();
const bool within = r.start_layer() >= kShardStartLayer &&
r.end_layer() <= kShardEndLayer &&
r.start_layer() < r.end_layer() &&
r.effective_start_layer() >= r.start_layer() &&
r.effective_start_layer() < r.end_layer();
if (!within) {
reject_open(sp::ERROR_CODE_SHARD_RANGE_MISMATCH,
"requested layer range is not served by this worker");
return grpc::Status::OK;
}
}
// Settle the flow-control window with strict worker bounds, then keep
// the negotiated ceilings on the session so every later check enforces
// exactly what was agreed — not what the peer proposed.
const FlowLimits negotiated =
open.has_proposed_flow_control()
? NegotiateFlow(open.proposed_flow_control())
: limits_;
{
std::lock_guard<std::mutex> lk(sessions_mu_);
SessionState state;
state.epoch = open.route_epoch();
state.credits = negotiated.credits_granted;
state.max_inflight = negotiated.max_inflight_chunks;
state.max_chunk_bytes = negotiated.max_chunk_bytes;
state.max_prefill_chunk_tokens = negotiated.max_prefill_chunk_tokens;
state.opened = true;
auto it = sessions_.find(route_session_id);
if (it != sessions_.end()) {
// A prior out-of-band Cancel may have marked this session cancelled
// before Open arrived; preserve that so the work still fails closed.
state.cancelled_session = it->second.cancelled_session;
state.cancelled_work = it->second.cancelled_work;
}
sessions_[route_session_id] = std::move(state);
}
sp::SessionResponse response;
sp::SessionAccepted* accepted = response.mutable_accepted();
accepted->set_schema_version(sp::SCHEMA_VERSION_1);
accepted->set_route_session_id(open.route_session_id());
accepted->set_route_epoch(open.route_epoch());
FillDefaultFlow(accepted->mutable_flow_control(), negotiated);
if (open.accepted_compression_size() > 0) {
for (int c : open.accepted_compression()) {
accepted->add_accepted_compression(static_cast<sp::Compression>(c));
}
} else {
accepted->add_accepted_compression(sp::COMPRESSION_NONE);
}
// Report the fingerprint the worker actually serves, so a mismatch is
// visible at open — never a copy of the caller's claimed identity.
FillWorkerFingerprint(accepted->mutable_fingerprint());
stream->Write(response);
break;
}
case sp::SessionRequest::kChunk: {
const sp::ActivationChunk& chunk = request.chunk();
const sp::Envelope& envelope = chunk.envelope();
const std::string work_id = envelope.work_id();
const uint64_t step = envelope.idempotency_step();
// Compute the response under the lock, then write it *after* releasing —
// holding the lock across a (possibly blocking) Write would deadlock an
// out-of-band Cancel RPC that needs the same lock.
sp::SessionResponse response;
bool terminate = false;
{
std::lock_guard<std::mutex> lk(sessions_mu_);
auto it = sessions_.find(route_session_id);
SessionState* state = it != sessions_.end() ? &it->second : nullptr;
if (state == nullptr || !state->opened) {
// Fail closed: an activation before a valid SessionOpen must never
// bypass lifecycle, cancellation, epoch or flow-control state.
response = MakeFail(route_session_id, work_id, step, sp::ERROR_CODE_INTERNAL,
"activation received before SessionOpen", true, false);
terminate = true;
} else if (state->cancelled_session || state->cancelled_work.count(work_id)) {
response = MakeFail(route_session_id, work_id, step, sp::ERROR_CODE_CANCELLED,
"work was cancelled", false, false);
} else if (envelope.route_epoch() < state->epoch) {
response = MakeFail(route_session_id, work_id, step, sp::ERROR_CODE_EPOCH_STALE,
"stale route epoch", false, false);
} else if (envelope.deadline_unix_nanos() != 0 &&
NowUnixNanos() > envelope.deadline_unix_nanos()) {
response = MakeFail(route_session_id, work_id, step, sp::ERROR_CODE_DEADLINE_EXCEEDED,
"deadline already passed", false, false);
} else if (state->seen_steps.count(step)) {
response = MakeAck(work_id, step, /*duplicate=*/true);
} else if (state->credits <= 0) {
response = MakeFail(route_session_id, work_id, step, sp::ERROR_CODE_FLOW_CONTROL_VIOLATION,
"no flow-control credit remaining", false, true);
} else {
const BundleCheck check = engine_.Validate(chunk.bundle(), state->max_chunk_bytes);
if (check.oversize_detail) {
response = MakeFail(route_session_id, work_id, step, sp::ERROR_CODE_RESOURCE_EXHAUSTED,
*check.oversize_detail, false, false);
} else if (check.corrupt_detail) {
response = MakeFail(route_session_id, work_id, step, sp::ERROR_CODE_PAYLOAD_CORRUPT,
*check.corrupt_detail, false, false);
} else {
state->seen_steps.insert(step);
state->credits -= 1;
engine_.BoundedForward(chunk.bundle()); // real bounded forward over wire bytes
*response.mutable_chunk() = chunk; // echo the exact bundle back
}
}
}
stream->Write(response);
if (terminate) {
return grpc::Status::OK;
}
break;
}
case sp::SessionRequest::kDecode: {
const sp::DecodeStep& step_msg = request.decode();
const std::string work_id = step_msg.work_id();
const uint64_t step = step_msg.idempotency_step();
sp::TensorBundle bundle;
if (step_msg.bundle().tensors_size() > 0) {
bundle = step_msg.bundle();
} else {
bundle.set_bundle_version(1);
*bundle.add_tensors() = step_msg.tensor();
}
sp::SessionResponse response;
bool terminate = false;
{
std::lock_guard<std::mutex> lk(sessions_mu_);
auto it = sessions_.find(route_session_id);
SessionState* state = it != sessions_.end() ? &it->second : nullptr;
if (state == nullptr || !state->opened) {
// Fail closed: a decode step before a valid SessionOpen must never
// bypass lifecycle, cancellation, epoch or flow-control state.
response = MakeFail(route_session_id, work_id, step, sp::ERROR_CODE_INTERNAL,
"activation received before SessionOpen", true, false);
terminate = true;
} else if (state->cancelled_session || state->cancelled_work.count(work_id)) {
response = MakeFail(route_session_id, work_id, step, sp::ERROR_CODE_CANCELLED,
"work was cancelled", false, false);
} else if (step_msg.deadline_unix_nanos() != 0 &&
NowUnixNanos() > step_msg.deadline_unix_nanos()) {
response = MakeFail(route_session_id, work_id, step, sp::ERROR_CODE_DEADLINE_EXCEEDED,
"deadline already passed", false, false);
} else if (state->seen_steps.count(step)) {
response = MakeAck(work_id, step, /*duplicate=*/true);
} else if (state->credits <= 0) {
response = MakeFail(route_session_id, work_id, step, sp::ERROR_CODE_FLOW_CONTROL_VIOLATION,
"no flow-control credit remaining", false, true);
} else {
const BundleCheck check = engine_.Validate(bundle, state->max_chunk_bytes);
if (check.oversize_detail) {
response = MakeFail(route_session_id, work_id, step, sp::ERROR_CODE_RESOURCE_EXHAUSTED,
*check.oversize_detail, false, false);
} else if (check.corrupt_detail) {
response = MakeFail(route_session_id, work_id, step, sp::ERROR_CODE_PAYLOAD_CORRUPT,
*check.corrupt_detail, false, false);
} else {
state->seen_steps.insert(step);
state->credits -= 1;
engine_.BoundedForward(bundle);
// No decode response field exists; echo the step back as a
// chunk-bearing SessionResponse per the proto's relayed-frame design.
sp::ActivationChunk* out = response.mutable_chunk();
sp::Envelope* out_env = out->mutable_envelope();
out_env->set_schema_version(sp::SCHEMA_VERSION_1);
out_env->set_work_id(work_id);
out_env->set_idempotency_step(step);
out_env->set_phase(sp::PHASE_DECODE);
sp::PositionSpan* pos = out_env->mutable_position();
pos->set_first_position(step_msg.position());
pos->set_token_count(1);
*out->mutable_bundle() = bundle;
}
}
}
stream->Write(response);
if (terminate) {
return grpc::Status::OK;
}
break;
}
case sp::SessionRequest::kFlowControl: {
const uint32_t topup = request.flow_control().credits_granted();
sp::SessionResponse response;
{
std::lock_guard<std::mutex> lk(sessions_mu_);
auto it = sessions_.find(route_session_id);
sp::FlowControl* fc = response.mutable_flow_control();
if (it != sessions_.end()) {
SessionState& state = it->second;
int64_t granted = std::min<int64_t>(state.credits + topup,
static_cast<int64_t>(state.max_inflight));
state.credits = granted;
fc->set_credits_granted(static_cast<uint32_t>(granted));
fc->set_max_inflight_chunks(state.max_inflight);
fc->set_max_chunk_bytes(state.max_chunk_bytes);
} else {
fc->set_credits_granted(topup != 0 ? topup : limits_.credits_granted);
fc->set_max_inflight_chunks(limits_.max_inflight_chunks);
fc->set_max_chunk_bytes(limits_.max_chunk_bytes);
}
fc->set_max_prefill_chunk_tokens(limits_.max_prefill_chunk_tokens);
}
stream->Write(response);
break;
}
case sp::SessionRequest::kRelease: {
const sp::ReleaseSignal& release = request.release();
// An explicit release drops session state immediately (KV, credits,
// dedup) instead of holding it for the TTL — the whole point of the
// signal. Erase the session this stream opened so its resources are
// freed the moment the terminal status is sent.
{
std::lock_guard<std::mutex> lk(sessions_mu_);
sessions_.erase(route_session_id);
}
sp::SessionResponse response;
sp::ShardStatus* status = response.mutable_status();
status->set_work_id(release.work_id());
status->set_route_session_id(release.route_session_id());
status->set_terminal(true);
stream->Write(response);
return grpc::Status::OK;
}
case sp::SessionRequest::kCancel: {
const sp::CancelSignal& signal = request.cancel();
MarkCancelled(route_session_id, signal.work_id());
const bool whole_session = signal.work_id().empty();
stream->Write(MakeFail(route_session_id, signal.work_id(), 0, sp::ERROR_CODE_CANCELLED,
signal.reason().empty() ? "cancelled" : signal.reason(),
whole_session, false));
if (whole_session) {
return grpc::Status::OK;
}
break;
}
default: {
sp::SessionResponse response;
response.mutable_status()->set_terminal(true);
stream->Write(response);
return grpc::Status::OK;
}
}
}
return grpc::Status::OK;
}
grpc::Status ShardRuntimeServiceImpl::Release(grpc::ServerContext*,
const sp::ReleaseRequest* request,
sp::ReleaseResponse* response) {
bool existed;
{
std::lock_guard<std::mutex> lk(sessions_mu_);
existed = sessions_.erase(request->route_session_id()) != 0;
}
response->set_released(existed);
return grpc::Status::OK;
}
grpc::Status ShardRuntimeServiceImpl::Cancel(grpc::ServerContext*,
const sp::CancelRequest* request,
sp::CancelResponse* response) {
const uint32_t newly = MarkCancelled(request->route_session_id(), request->work_id());
response->set_cancelled_work_items(newly);
return grpc::Status::OK;
}
} // namespace meshnet::worker

View File

@@ -0,0 +1,96 @@
// The native Shard worker's ShardRuntime service (DGR-033).
//
// A faithful C++ port of `ShardRuntimeServicer` in `shard_runtime_server.py`:
// the same per-`route_session_id` identity/credit/dedup state, the same
// fail-closed negative paths (stale epoch, expired deadline, corrupt/oversize
// payload, exhausted flow-control credit, duplicate idempotency step, in-band
// and out-of-band cancellation), and the same lifecycle (open/prefill/decode/
// flow-control/release/cancel). The only compute it does is the fake engine's
// bounded forward — there is no llama.cpp linkage and no arbitrary-graph RPC.
#ifndef MESHNET_NATIVE_WORKER_SHARD_SERVICE_H_
#define MESHNET_NATIVE_WORKER_SHARD_SERVICE_H_
#include <cstdint>
#include <map>
#include <mutex>
#include <set>
#include <string>
#include <grpcpp/grpcpp.h>
#include "fake_engine.h"
#include "shard_runtime.grpc.pb.h"
#include "shard_runtime.pb.h"
namespace meshnet::worker {
namespace sp = ::meshnet::shard::v1;
struct FlowLimits {
uint32_t credits_granted = 16;
uint32_t max_inflight_chunks = 16;
uint64_t max_chunk_bytes = 4u * 1024u * 1024u;
uint32_t max_prefill_chunk_tokens = 512;
};
// Per-route-session identity/credit/dedup state, kept on the servicer instance
// (guarded by a lock) so an out-of-band unary Cancel from a different handler
// thread can reach a session a concurrent Session stream is still iterating.
struct SessionState {
uint64_t epoch = 0;
int64_t credits = 0;
uint32_t max_inflight = 0;
uint64_t max_chunk_bytes = 0;
uint32_t max_prefill_chunk_tokens = 0;
std::set<uint64_t> seen_steps;
std::set<std::string> cancelled_work;
bool cancelled_session = false;
// True only after a valid SessionOpen handshake completed for this
// route_session_id. An activation (chunk/decode) that arrives while this is
// false fails closed: no work may bypass the lifecycle handshake, even when a
// placeholder state already exists from an out-of-band Cancel that raced Open.
bool opened = false;
};
class ShardRuntimeServiceImpl final : public sp::ShardRuntime::Service {
public:
explicit ShardRuntimeServiceImpl(FlowLimits limits) : limits_(limits) {}
grpc::Status GetCapability(grpc::ServerContext* context,
const sp::CapabilityRequest* request,
sp::CapabilityReport* response) override;
grpc::Status Health(grpc::ServerContext* context, const sp::HealthRequest* request,
sp::HealthReport* response) override;
grpc::Status Session(
grpc::ServerContext* context,
grpc::ServerReaderWriter<sp::SessionResponse, sp::SessionRequest>* stream) override;
grpc::Status Release(grpc::ServerContext* context, const sp::ReleaseRequest* request,
sp::ReleaseResponse* response) override;
grpc::Status Cancel(grpc::ServerContext* context, const sp::CancelRequest* request,
sp::CancelResponse* response) override;
private:
// Returns the number of items newly marked cancelled, creating session state
// if the Cancel raced ahead of SessionOpen.
uint32_t MarkCancelled(const std::string& route_session_id, const std::string& work_id);
// Settle a stream's flow-control window against this worker's own limits: the
// strictest bound of either peer wins for every field, so a peer can never
// raise the worker's ceilings by proposing a larger window. Mirrors
// `negotiate_flow_control` in `native_protocol/codec.py`.
FlowLimits NegotiateFlow(const sp::FlowControl& proposed) const;
FlowLimits limits_;
FakeShardEngine engine_;
std::mutex sessions_mu_;
std::map<std::string, SessionState> sessions_;
};
} // namespace meshnet::worker
#endif // MESHNET_NATIVE_WORKER_SHARD_SERVICE_H_

View File

@@ -0,0 +1,302 @@
// Standalone native Shard worker executable (DGR-033).
//
// Serves the complete ShardRuntime lifecycle/stream contract over real
// gRPC/HTTP2 using the model-free FakeShardEngine. It links neither llama.cpp
// nor any graph-execution entry point: the only surface it exposes is the
// ShardRuntime service defined in shard_runtime.proto.
//
// Usage:
// shard_worker [listen_addr] serve until SIGTERM/SIGINT (graceful drain)
// shard_worker --selftest bind an ephemeral port, self-drive the
// lifecycle over a real loopback channel, exit
//
// Environment:
// MESHNET_SHARD_LISTEN_ADDR host:port to bind (default localhost:50051)
// MESHNET_MAX_CHUNK_BYTES per-chunk byte ceiling the worker enforces
//
// On a normal run it prints one readiness line — "ShardRuntime worker listening
// on <addr>" — once the socket is bound, so a supervisor/harness has a real
// readiness signal instead of a sleep.
#include <atomic>
#include <cerrno>
#include <csignal>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <unistd.h>
#include <grpcpp/grpcpp.h>
#include "shard_service.h"
#include "shard_runtime.grpc.pb.h"
namespace {
namespace sp = ::meshnet::shard::v1;
// Self-pipe: the signal handler must stay async-signal-safe, so it only writes
// one byte; a helper thread reads it and performs the (non-signal-safe) server
// Shutdown(). Set once in main() before installing the handler.
volatile std::sig_atomic_t g_signal_pipe_write_fd = -1;
extern "C" void HandleTermination(int /*signum*/) {
if (g_signal_pipe_write_fd >= 0) {
const char byte = 1;
ssize_t rc = ::write(g_signal_pipe_write_fd, &byte, 1);
(void)rc; // best-effort; nothing safe to do on failure inside a handler
}
}
meshnet::worker::FlowLimits LimitsFromEnv() {
meshnet::worker::FlowLimits limits;
if (const char* raw = std::getenv("MESHNET_MAX_CHUNK_BYTES")) {
char* end = nullptr;
const unsigned long long value = std::strtoull(raw, &end, 10);
if (end != raw && value > 0) {
limits.max_chunk_bytes = static_cast<uint64_t>(value);
}
}
return limits;
}
int RunSelfTest() {
meshnet::worker::ShardRuntimeServiceImpl service(LimitsFromEnv());
int selected_port = 0;
grpc::ServerBuilder builder;
builder.AddListeningPort("127.0.0.1:0", grpc::InsecureServerCredentials(), &selected_port);
builder.RegisterService(&service);
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
if (!server || selected_port == 0) {
std::cerr << "selftest: failed to bind ephemeral port\n";
return 1;
}
const std::string target = "127.0.0.1:" + std::to_string(selected_port);
auto channel = grpc::CreateChannel(target, grpc::InsecureChannelCredentials());
auto stub = sp::ShardRuntime::NewStub(channel);
int failures = 0;
auto check = [&](bool cond, const char* what) {
if (!cond) {
std::cerr << "selftest FAIL: " << what << "\n";
++failures;
}
};
// Capability + health.
{
grpc::ClientContext ctx;
sp::CapabilityRequest req;
req.set_schema_version(sp::SCHEMA_VERSION_1);
sp::CapabilityReport rep;
grpc::Status status = stub->GetCapability(&ctx, req, &rep);
check(status.ok(), "GetCapability RPC");
check(rep.validated(), "capability validated");
check(rep.schema_version() == sp::SCHEMA_VERSION_1, "capability schema version");
}
{
grpc::ClientContext ctx;
sp::HealthRequest req;
req.set_schema_version(sp::SCHEMA_VERSION_1);
sp::HealthReport rep;
grpc::Status status = stub->Health(&ctx, req, &rep);
check(status.ok(), "Health RPC");
check(rep.state() == sp::SERVING_STATE_SERVING, "health serving");
}
// A minimal session: open -> fragmented prefill -> decode -> release.
{
grpc::ClientContext ctx;
auto stream = stub->Session(&ctx);
sp::SessionRequest open;
sp::SessionOpen* o = open.mutable_open();
o->set_schema_version(sp::SCHEMA_VERSION_1);
o->set_route_session_id("selftest");
o->set_route_epoch(1);
sp::FlowControl* fc = o->mutable_proposed_flow_control();
fc->set_credits_granted(16);
fc->set_max_inflight_chunks(16);
fc->set_max_chunk_bytes(4u * 1024u * 1024u);
check(stream->Write(open), "write open");
sp::SessionResponse accepted;
check(stream->Read(&accepted), "read accepted");
check(accepted.kind_case() == sp::SessionResponse::kAccepted, "accepted kind");
// Fragmented prefill: two fragments tiling a 6-byte payload.
const std::string payload = "ABCDEF";
sp::SessionRequest chunk;
sp::ActivationChunk* ac = chunk.mutable_chunk();
sp::Envelope* env = ac->mutable_envelope();
env->set_schema_version(sp::SCHEMA_VERSION_1);
env->set_work_id("w1");
env->set_route_session_id("selftest");
env->set_route_epoch(1);
env->set_idempotency_step(1);
env->set_phase(sp::PHASE_PREFILL);
sp::TensorBundle* bundle = ac->mutable_bundle();
bundle->set_bundle_version(1);
sp::NamedTensor* tensor = bundle->add_tensors();
tensor->set_name("hidden_states");
tensor->set_dtype(sp::DTYPE_BFLOAT16);
tensor->set_byte_order(sp::BYTE_ORDER_LITTLE_ENDIAN);
tensor->set_total_bytes(payload.size());
tensor->set_compression(sp::COMPRESSION_NONE);
sp::Checksum* cksum = tensor->mutable_checksum();
cksum->set_algorithm(sp::CHECKSUM_ALGORITHM_CRC32C);
const uint32_t crc = meshnet::worker::Crc32(payload);
std::string crc_be(4, '\0');
crc_be[0] = static_cast<char>((crc >> 24) & 0xFF);
crc_be[1] = static_cast<char>((crc >> 16) & 0xFF);
crc_be[2] = static_cast<char>((crc >> 8) & 0xFF);
crc_be[3] = static_cast<char>(crc & 0xFF);
cksum->set_value(crc_be);
sp::TensorFragment* f0 = tensor->add_fragments();
f0->set_fragment_index(0);
f0->set_fragment_count(2);
f0->set_byte_offset(0);
f0->set_payload(payload.substr(0, 3));
sp::TensorFragment* f1 = tensor->add_fragments();
f1->set_fragment_index(1);
f1->set_fragment_count(2);
f1->set_byte_offset(3);
f1->set_payload(payload.substr(3));
check(stream->Write(chunk), "write chunk");
sp::SessionResponse echoed;
check(stream->Read(&echoed), "read chunk echo");
check(echoed.kind_case() == sp::SessionResponse::kChunk, "chunk echo kind");
sp::SessionRequest decode;
sp::DecodeStep* ds = decode.mutable_decode();
ds->set_idempotency_step(2);
ds->set_position(1);
ds->set_work_id("w2");
sp::TensorBundle* dbundle = ds->mutable_bundle();
dbundle->set_bundle_version(1);
sp::NamedTensor* dt = dbundle->add_tensors();
dt->set_name("hidden_states");
dt->set_dtype(sp::DTYPE_BFLOAT16);
dt->set_byte_order(sp::BYTE_ORDER_LITTLE_ENDIAN);
dt->set_total_bytes(payload.size());
dt->set_compression(sp::COMPRESSION_NONE);
sp::Checksum* dck = dt->mutable_checksum();
dck->set_algorithm(sp::CHECKSUM_ALGORITHM_CRC32C);
dck->set_value(crc_be);
sp::TensorFragment* df = dt->add_fragments();
df->set_fragment_index(0);
df->set_fragment_count(1);
df->set_byte_offset(0);
df->set_payload(payload);
check(stream->Write(decode), "write decode");
sp::SessionResponse decode_echo;
check(stream->Read(&decode_echo), "read decode echo");
check(decode_echo.kind_case() == sp::SessionResponse::kChunk, "decode echo kind");
sp::SessionRequest release;
sp::ReleaseSignal* rs = release.mutable_release();
rs->set_route_session_id("selftest");
rs->set_work_id("w-final");
check(stream->Write(release), "write release");
stream->WritesDone();
sp::SessionResponse terminal;
check(stream->Read(&terminal), "read terminal");
check(terminal.kind_case() == sp::SessionResponse::kStatus && terminal.status().terminal(),
"terminal status");
grpc::Status status = stream->Finish();
check(status.ok(), "stream finish");
}
server->Shutdown();
server->Wait();
if (failures == 0) {
std::cout << "selftest: all lifecycle checks passed\n";
return 0;
}
std::cerr << "selftest: " << failures << " check(s) failed\n";
return 1;
}
} // namespace
int main(int argc, char** argv) {
GOOGLE_PROTOBUF_VERIFY_VERSION;
for (int i = 1; i < argc; ++i) {
if (std::strcmp(argv[i], "--selftest") == 0) {
return RunSelfTest();
}
}
std::string listen_addr = "localhost:50051";
if (const char* env = std::getenv("MESHNET_SHARD_LISTEN_ADDR")) {
listen_addr = env;
}
if (argc > 1 && argv[1][0] != '-') {
listen_addr = argv[1];
}
meshnet::worker::FlowLimits limits = LimitsFromEnv();
meshnet::worker::ShardRuntimeServiceImpl service(limits);
grpc::ServerBuilder builder;
int selected_port = 0;
builder.AddListeningPort(listen_addr, grpc::InsecureServerCredentials(), &selected_port);
// Bounded messages, two layers: a hard transport receive ceiling (never below
// 4 MiB so the handshake and normal chunks always fit) plus the finer
// app-level per-tensor RESOURCE_EXHAUSTED check the service enforces against
// the negotiated max_chunk_bytes. Neither path lets an unbounded frame in.
constexpr int kTransportFloor = 4 * 1024 * 1024;
const int transport_max = limits.max_chunk_bytes > static_cast<uint64_t>(kTransportFloor)
? static_cast<int>(limits.max_chunk_bytes)
: kTransportFloor;
builder.SetMaxReceiveMessageSize(transport_max);
builder.RegisterService(&service);
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
if (!server || selected_port == 0) {
std::cerr << "failed to bind " << listen_addr << "\n";
return 1;
}
int pipe_fds[2];
if (::pipe(pipe_fds) != 0) {
std::cerr << "failed to create shutdown pipe\n";
return 1;
}
g_signal_pipe_write_fd = pipe_fds[1];
struct sigaction sa;
std::memset(&sa, 0, sizeof(sa));
sa.sa_handler = HandleTermination;
::sigaction(SIGTERM, &sa, nullptr);
::sigaction(SIGINT, &sa, nullptr);
// Drain thread: wakes on the first termination signal and shuts the server
// down gracefully so in-flight sessions finish rather than being severed.
std::thread drain([&server, read_fd = pipe_fds[0]]() {
char byte = 0;
ssize_t rc = 0;
do {
rc = ::read(read_fd, &byte, 1);
} while (rc < 0 && errno == EINTR);
server->Shutdown();
});
std::cout << "ShardRuntime worker listening on " << listen_addr << std::endl;
server->Wait();
drain.join();
::close(pipe_fds[0]);
::close(pipe_fds[1]);
std::cout << "ShardRuntime worker shut down cleanly" << std::endl;
return 0;
}

View File

@@ -109,9 +109,41 @@ def _load_lock() -> dict[str, Any]:
"workspace": "build/llama.cpp",
}:
raise DependencyError("retrieval must use the locked detached-commit build workspace")
_verify_accelerator_presets(lock)
return lock
def _verify_accelerator_presets(lock: dict[str, Any]) -> None:
"""Each preset must isolate one backend that the CPU default leaves OFF.
This is what keeps DGR-030's presets from ever being able to change the
deterministic CPU default recorded in ``build.configure_flags``: a preset
can only exist for a flag this lock already pins OFF, and
``accelerator_configure_flags`` only ever returns a fresh list, never
mutates ``build.configure_flags`` in place.
"""
presets = lock.get("accelerator_presets", {})
if not isinstance(presets, dict):
raise DependencyError("accelerator_presets must be a JSON object")
if not presets:
return
base_flags = dict(flag[len("-D"):].split("=", 1) for flag in lock["build"]["configure_flags"])
for name, preset in presets.items():
if not isinstance(preset, dict):
raise DependencyError(f"accelerator_presets.{name} must be a JSON object")
backend_flag = preset.get("backend_flag")
if not isinstance(backend_flag, str) or not backend_flag:
raise DependencyError(f"accelerator_presets.{name} is missing backend_flag")
if base_flags.get(backend_flag) != "OFF":
raise DependencyError(
f"accelerator_presets.{name} backend flag {backend_flag} must be OFF in "
"the deterministic CPU default build.configure_flags"
)
probe = preset.get("sdk_probe")
if not isinstance(probe, dict) or not isinstance(probe.get("binary"), str) or not probe["binary"]:
raise DependencyError(f"accelerator_presets.{name} is missing an sdk_probe.binary")
def _patches(lock: dict[str, Any]) -> list[pathlib.Path]:
series = [line for line in (PATCH_DIR / "series").read_text().splitlines() if line]
if series != lock["patch_series"] or series != sorted(series) or not series:
@@ -507,6 +539,116 @@ def ctest_lane(build_dir: pathlib.Path) -> None:
print(_run(_ctest(), "--test-dir", str(build_dir), "-R", regex, "--output-on-failure"))
def _sdk_probe(probe: dict[str, Any]) -> str | None:
"""Resolve one accelerator lane's SDK binary, or None if it is unavailable."""
platform_only = probe.get("platform_only")
if platform_only and sys.platform != platform_only:
return None
env_var = probe.get("env_var")
if env_var:
override = os.environ.get(env_var)
if override:
return override
return shutil.which(probe["binary"])
def accelerator_status(name: str, lock: dict[str, Any] | None = None) -> dict[str, Any]:
"""Report whether lane `name`'s SDK is present, never raising for absence.
This is the single source of truth for DGR-030's "unavailable/skipped, not
false success" contract: absence is reported as data, not swallowed and
not escalated into a build attempt.
"""
lock = lock if lock is not None else _load_lock()
presets = lock.get("accelerator_presets", {})
if name not in presets:
raise DependencyError(f"unknown accelerator lane: {name}")
probe = presets[name]["sdk_probe"]
resolved = _sdk_probe(probe)
if resolved is None:
platform_only = probe.get("platform_only")
if platform_only and sys.platform != platform_only:
reason = f"platform {sys.platform!r} is not {platform_only!r}"
else:
reason = f"{probe['binary']} is unavailable on PATH"
return {"lane": name, "available": False, "reason": reason}
return {"lane": name, "available": True, "sdk_binary": resolved}
def accelerator_configure_flags(lock: dict[str, Any], name: str) -> list[str]:
"""The CPU default's configure flags with exactly one backend flag flipped ON.
Returns a new list; `lock["build"]["configure_flags"]` (the deterministic
CPU default DGR-029 locked) is never mutated.
"""
presets = lock.get("accelerator_presets", {})
if name not in presets:
raise DependencyError(f"unknown accelerator lane: {name}")
backend_flag = presets[name]["backend_flag"]
target = f"-D{backend_flag}="
flags: list[str] = []
replaced = False
for flag in lock["build"]["configure_flags"]:
if flag.startswith(target):
flags.append(f"-D{backend_flag}=ON")
replaced = True
else:
flags.append(flag)
if not replaced:
raise DependencyError(f"accelerator lane {name} backend flag {backend_flag} is not a locked base flag")
return flags
def accelerator_build(source: pathlib.Path, name: str, build_dir: pathlib.Path) -> pathlib.Path:
"""Compile lane `name` into its own out-of-tree directory. Compile-only.
This never runs `smoke`/`ctest_lane`: exercising a binary linked against an
accelerator backend would touch real hardware, and DGR-030 keeps every
backend/model/recipe lane registered-dark (compiled, never certified)
until a separate real-hardware certification record exists.
"""
lock = _load_lock()
_patches(lock)
_verify_source(source, lock, require_clean=False)
_verify_patched_source(source, lock)
expected_marker = source / "cmake/meshnet-patch-stack.cmake"
if not expected_marker.is_file():
raise DependencyError("patch stack is not applied: Meshnet CMake marker is absent")
if build_dir.exists():
raise DependencyError(f"accelerator build directory already exists; use a clean build dir: {build_dir}")
status = accelerator_status(name, lock)
if not status["available"]:
raise DependencyError(f"accelerator lane {name} SDK is unavailable: {status['reason']}")
flags = accelerator_configure_flags(lock, name)
cmake = _cmake()
_run(cmake, "-G", lock["build"]["generator"], "-S", str(source), "-B", str(build_dir), *flags)
for target in lock["build"]["native_targets"]:
_run(cmake, "--build", str(build_dir), "--target", target, "-j2")
metadata = {
"lane": name,
"backend_flag": lock["accelerator_presets"][name]["backend_flag"],
"commit": lock["commit"],
"commit_tree": lock["commit_tree"],
"patches": {patch.name: hashlib.sha256(patch.read_bytes()).hexdigest() for patch in _patches(lock)},
"configure_flags": flags,
"cmake": _run(cmake, "--version").splitlines()[0],
"cxx": _run("c++", "--version").splitlines()[0],
"sdk_binary": status["sdk_binary"],
"model_downloads": False,
"hardware_execution": False,
"hardware_certified": False,
"semantic_certification": False,
"note": (
"compiled only; no accelerator device was exercised or driven. "
"Backend/model/recipe capability remains registered-dark until a "
"separate real-hardware certification record exists (see "
"DGR-041/053/067)."
),
}
(build_dir / "meshnet-build-metadata.json").write_text(json.dumps(metadata, indent=2, sort_keys=True) + "\n")
return build_dir
def verify(workspace: pathlib.Path) -> None:
"""Apply, verify, reverse, and leave the exact cached pin pristine."""
source = fetch(workspace)
@@ -562,6 +704,12 @@ def main() -> int:
smoke_parser.add_argument("--binary", type=pathlib.Path, required=True)
ctest_parser = subcommands.add_parser("ctest")
ctest_parser.add_argument("--build-dir", type=pathlib.Path, required=True)
accel_status_parser = subcommands.add_parser("accelerator-status")
accel_status_parser.add_argument("--name", required=True)
accel_build_parser = subcommands.add_parser("accelerator-build")
accel_build_parser.add_argument("--name", required=True)
accel_build_parser.add_argument("--source-dir", type=pathlib.Path, required=True)
accel_build_parser.add_argument("--build-dir", type=pathlib.Path, required=True)
reproduce_parser = subcommands.add_parser("reproduce")
reproduce_parser.add_argument("--workspace", type=pathlib.Path, default=ROOT / "build/llama.cpp")
args = parser.parse_args()
@@ -582,6 +730,10 @@ def main() -> int:
smoke(args.binary)
elif args.command == "ctest":
ctest_lane(args.build_dir)
elif args.command == "accelerator-status":
print(json.dumps(accelerator_status(args.name), indent=2, sort_keys=True))
elif args.command == "accelerator-build":
accelerator_build(args.source_dir, args.name, args.build_dir)
else:
reproduce(args.workspace)
except DependencyError as error:

View File

@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""DGR-030: native CI/build matrix over the CPU default plus accelerator lanes.
Runs the exact deterministic CPU lane DGR-029 locked (unchanged), then probes
each accelerator preset (CUDA, ROCm, Vulkan, Metal) from `UPSTREAM_LOCK.json`
and compiles the ones whose SDK is present on this machine into their own
out-of-tree build directory.
A lane whose SDK is absent is reported as `skipped` with the exact probe
reason, never treated as a false pass. A lane that compiles is reported as
`built`, carrying exact compiler/SDK/upstream-pin/patch-stack/build-option
evidence — never as a certified capability. This script never runs an
accelerator binary and never certifies a backend/model/recipe: real-hardware
certification is separate future work (DGR-041/053/067).
"""
from __future__ import annotations
import argparse
import json
import pathlib
import sys
from typing import Any
ROOT = pathlib.Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "scripts"))
import llama_cpp_dependency as dep # noqa: E402
def _cpu_lane(source: pathlib.Path, workspace: pathlib.Path) -> dict[str, Any]:
build_dir = workspace.resolve() / "build"
if build_dir.exists():
return {
"lane": "cpu",
"status": "skipped",
"reason": f"build directory already exists; remove for a clean rebuild: {build_dir}",
}
binary = dep.build(source, build_dir)
dep.smoke(binary)
dep.ctest_lane(build_dir)
metadata = json.loads((build_dir / "meshnet-build-metadata.json").read_text())
return {"lane": "cpu", "status": "built", "build_dir": str(build_dir), "metadata": metadata}
def _accelerator_lane(source: pathlib.Path, workspace: pathlib.Path, name: str, lock: dict[str, Any]) -> dict[str, Any]:
status = dep.accelerator_status(name, lock)
if not status["available"]:
return {"lane": name, "status": "skipped", "reason": status["reason"]}
build_dir = workspace.resolve() / f"build-{name}"
if build_dir.exists():
return {
"lane": name,
"status": "skipped",
"reason": f"build directory already exists; remove for a clean rebuild: {build_dir}",
}
dep.accelerator_build(source, name, build_dir)
metadata = json.loads((build_dir / "meshnet-build-metadata.json").read_text())
return {"lane": name, "status": "built", "build_dir": str(build_dir), "metadata": metadata}
def run_matrix(workspace: pathlib.Path) -> dict[str, Any]:
"""Fetch/apply once, run every lane, then always reverse the checkout."""
source = dep.fetch(workspace)
dep.apply(source)
lanes: list[dict[str, Any]] = []
try:
lock = dep._load_lock()
try:
lanes.append(_cpu_lane(source, workspace))
except dep.DependencyError as error:
lanes.append({"lane": "cpu", "status": "failed", "reason": str(error)})
for name in lock.get("accelerator_presets", {}):
try:
lanes.append(_accelerator_lane(source, workspace, name, lock))
except dep.DependencyError as error:
lanes.append({"lane": name, "status": "failed", "reason": str(error)})
finally:
dep.reverse(source)
failed_lanes = [lane["lane"] for lane in lanes if lane["status"] == "failed"]
return {
"lanes": lanes,
"hardware_certified": False,
"note": (
"A `built` lane means it compiled with the exact recorded compiler/SDK/"
"upstream-pin/patch-stack/build-option evidence — it never means an "
"accelerator device was exercised. Every backend/model/recipe lane "
"stays registered-dark until a separate real-hardware certification "
"record exists."
),
"failed_lanes": failed_lanes,
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--workspace", type=pathlib.Path, default=ROOT / "build/llama.cpp")
parser.add_argument("--out", type=pathlib.Path, default=None, help="also write the JSON report here")
args = parser.parse_args()
try:
report = run_matrix(args.workspace)
except dep.DependencyError as error:
print(f"DGR-030 dependency error: {error}", file=sys.stderr)
return 2
text = json.dumps(report, indent=2, sort_keys=True)
print(text)
if args.out:
args.out.write_text(text + "\n")
return 1 if report["failed_lanes"] else 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,273 @@
"""Reusable ``ShardEngine`` lifecycle contract (DGR-031).
Any :class:`~meshnet_node.shard_engine.ShardEngine` implementation — the
DGR-032 deterministic fixture, the DGR-037 llama.cpp binding, or a throwaway
test double — can be checked against this contract by calling
:func:`assert_shard_engine_contract` with a zero-argument factory that
returns a fresh, unloaded engine instance. It proves the *lifecycle
semantics* (load/capabilities gating, cache-miss/stale-epoch/cancel/release
behavior, head vs. middle boundary-vs-token output) are identical across
implementations. It says nothing about whether the numbers an implementation
produces are numerically correct — that is DGR-036's job.
This module is not itself collected as a test file (it does not match
``test_*.py``); import ``assert_shard_engine_contract`` from a real test file
that supplies the engine factory, as ``test_shard_engine.py`` does here.
"""
from __future__ import annotations
from typing import Callable
from meshnet_node.shard_engine import (
BoundaryBundle,
DecodeRequest,
EngineTensor,
LoadRequest,
PrefillRequest,
ShardEngine,
TokenOutput,
)
from meshnet_node.shard_lifecycle import CacheResult, StatusCode
def assert_shard_engine_contract(make_engine: Callable[[], ShardEngine]) -> None:
"""Run every lifecycle check against a fresh engine instance per check.
Each check gets its own ``make_engine()`` instance so one check's session
state can never leak into another's.
"""
_assert_health_before_load_is_not_serving(make_engine())
_assert_load_then_capabilities_matches_range(make_engine())
_assert_prefill_then_decode_succeeds_and_is_deterministic(make_engine())
_assert_middle_shard_accepts_boundary_bundle_not_token_ids(make_engine())
_assert_decode_without_prefill_is_a_deterministic_cache_miss(make_engine())
_assert_stale_epoch_is_rejected(make_engine())
_assert_cancel_then_decode_is_rejected_and_cancel_is_idempotent(make_engine())
_assert_release_then_decode_is_rejected_and_release_is_idempotent(make_engine())
_assert_metrics_reports_cancelled_sessions(make_engine())
def _load(
engine: ShardEngine, *, shard_start: int = 0, shard_end: int = 3, total_layers: int = 4
):
result = engine.load(
LoadRequest(
artifact_path="fixture://contract-test",
shard_start=shard_start,
shard_end=shard_end,
total_layers=total_layers,
)
)
assert result.status.code is StatusCode.OK, result.status
return result
def _output_bytes(output: BoundaryBundle | TokenOutput | None) -> bytes:
assert output is not None
if isinstance(output, TokenOutput):
return output.token_id.to_bytes(8, "big")
return b"".join(tensor.data for tensor in output.tensors)
def _assert_health_before_load_is_not_serving(engine: ShardEngine) -> None:
health = engine.health()
assert health.status.code is StatusCode.OK
assert health.serving is False
def _assert_load_then_capabilities_matches_range(engine: ShardEngine) -> None:
_load(engine, shard_start=0, shard_end=3, total_layers=4)
caps = engine.capabilities()
assert caps.status.code is StatusCode.OK
assert caps.shard_start == 0
assert caps.shard_end == 3
assert caps.total_layers == 4
assert caps.is_head is True
assert caps.is_tail is True
assert caps.supports_mtp is False, "MTP must stay reserved-off until DGR-066"
assert engine.health().serving is True
def _assert_prefill_then_decode_succeeds_and_is_deterministic(engine: ShardEngine) -> None:
_load(engine)
prefill = engine.prefill(
PrefillRequest(
session_id="session-a",
route_epoch=1,
position=0,
idempotency_step=0,
token_ids=(1, 2, 3),
)
)
assert prefill.status.code is StatusCode.OK
assert isinstance(prefill.output, (BoundaryBundle, TokenOutput))
decode = engine.decode(
DecodeRequest(
session_id="session-a",
route_epoch=1,
position=3,
idempotency_step=1,
token_id=4,
)
)
assert decode.status.code is StatusCode.OK
assert decode.cache_result is CacheResult.HIT
assert isinstance(decode.output, (BoundaryBundle, TokenOutput))
# Determinism: the identical prefill replayed on a brand-new session
# produces byte-identical output. The transform is a pure function of
# its inputs, not of hidden randomness or cross-session state.
replay = engine.prefill(
PrefillRequest(
session_id="session-b",
route_epoch=1,
position=0,
idempotency_step=0,
token_ids=(1, 2, 3),
)
)
assert _output_bytes(replay.output) == _output_bytes(prefill.output)
def _assert_middle_shard_accepts_boundary_bundle_not_token_ids(engine: ShardEngine) -> None:
_load(engine, shard_start=1, shard_end=2, total_layers=8)
caps = engine.capabilities()
assert caps.is_head is False
assert caps.is_tail is False
input_bundle = BoundaryBundle(
tensors=(
EngineTensor(name="hidden_states", shape=(1, 3), dtype="bfloat16", data=b"\x00" * 8),
),
architecture="dense",
boundary_point="pre_tail_residual",
)
result = engine.prefill(
PrefillRequest(
session_id="session-middle",
route_epoch=1,
position=0,
idempotency_step=0,
input=input_bundle,
)
)
assert result.status.code is StatusCode.OK
assert isinstance(result.output, BoundaryBundle), "a non-tail shard must hand off a boundary bundle, never a sampled token"
def _assert_decode_without_prefill_is_a_deterministic_cache_miss(engine: ShardEngine) -> None:
_load(engine)
result = engine.decode(
DecodeRequest(
session_id="never-opened",
route_epoch=1,
position=0,
idempotency_step=0,
token_id=9,
)
)
assert result.status.code is not StatusCode.OK
assert result.cache_result is CacheResult.MISS
assert result.output is None
def _assert_stale_epoch_is_rejected(engine: ShardEngine) -> None:
_load(engine)
engine.prefill(
PrefillRequest(
session_id="session-epoch",
route_epoch=5,
position=0,
idempotency_step=0,
token_ids=(1,),
)
)
stale = engine.decode(
DecodeRequest(
session_id="session-epoch",
route_epoch=4,
position=1,
idempotency_step=1,
token_id=2,
)
)
assert stale.status.code is not StatusCode.OK
assert stale.output is None
def _assert_cancel_then_decode_is_rejected_and_cancel_is_idempotent(engine: ShardEngine) -> None:
_load(engine)
engine.prefill(
PrefillRequest(
session_id="session-cancel",
route_epoch=1,
position=0,
idempotency_step=0,
token_ids=(1,),
)
)
cancelled = engine.cancel("session-cancel")
assert cancelled.code is StatusCode.CANCELLED
after = engine.decode(
DecodeRequest(
session_id="session-cancel",
route_epoch=1,
position=1,
idempotency_step=1,
token_id=2,
)
)
assert after.status.code is StatusCode.CANCELLED
assert after.output is None
again = engine.cancel("session-cancel")
assert again.code is StatusCode.CANCELLED
def _assert_release_then_decode_is_rejected_and_release_is_idempotent(engine: ShardEngine) -> None:
_load(engine)
engine.prefill(
PrefillRequest(
session_id="session-release",
route_epoch=1,
position=0,
idempotency_step=0,
token_ids=(1,),
)
)
released = engine.release("session-release")
assert released.code is StatusCode.OK
after = engine.decode(
DecodeRequest(
session_id="session-release",
route_epoch=1,
position=1,
idempotency_step=1,
token_id=2,
)
)
assert after.status.code is not StatusCode.OK
again = engine.release("session-release")
assert again.code is StatusCode.OK
def _assert_metrics_reports_cancelled_sessions(engine: ShardEngine) -> None:
_load(engine)
engine.prefill(
PrefillRequest(
session_id="session-metrics",
route_epoch=1,
position=0,
idempotency_step=0,
token_ids=(1,),
)
)
engine.cancel("session-metrics")
metrics = engine.metrics()
assert metrics.status.code is StatusCode.OK
assert metrics.cancelled_sessions >= 1

View File

@@ -0,0 +1,200 @@
"""DGR-032 ``FakeShardEngine`` tests.
``FakeShardEngine`` obeys the exact same lifecycle contract every
``ShardEngine`` implementation must (see ``shard_engine_contract.py``); the
tests here additionally cover this story's own scope: head/middle/tail
output shape, isolated multi-session state, and the delay/memory-pressure/
malformed-output/crash fault-injection knobs. None of this is real-model
evidence — see the module docstring in ``fake_shard_engine.py`` and
``test_fake_shard_engine_declares_fixture_evidence_class`` below, which pins
the marker DGR-036 will rely on to tell a fixture engine apart from a real
one when it certifies fixture-vs-real parity.
"""
from __future__ import annotations
import pytest
from meshnet_node.fake_shard_engine import (
MALFORMED_TOKEN_ID_FLOOR,
TOKEN_ID_VOCAB_SIZE,
FakeShardEngine,
FakeShardEngineConfig,
)
from meshnet_node.shard_engine import (
BoundaryBundle,
DecodeRequest,
EngineTensor,
LoadRequest,
PrefillRequest,
TokenOutput,
)
from meshnet_node.shard_lifecycle import StatusCode
from shard_engine_contract import assert_shard_engine_contract
def _load(engine: FakeShardEngine, *, shard_start=0, shard_end=3, total_layers=4, recipe=None):
return engine.load(
LoadRequest(
artifact_path="fixture://fake-shard-engine",
shard_start=shard_start,
shard_end=shard_end,
total_layers=total_layers,
recipe=recipe or {},
)
)
def test_fake_shard_engine_obeys_the_shared_shard_engine_contract():
assert_shard_engine_contract(FakeShardEngine)
def test_fake_shard_engine_declares_fixture_evidence_class():
# DGR-036's fixture-vs-real parity check needs a structural way to tell
# a fixture engine apart from a real one; this constant is that marker.
assert FakeShardEngine.EVIDENCE_CLASS == "fixture"
def test_head_shard_returns_boundary_bundle_with_post_head_residual_point():
engine = FakeShardEngine()
_load(engine, shard_start=0, shard_end=1, total_layers=4)
result = engine.prefill(
PrefillRequest(session_id="s", route_epoch=1, position=0, idempotency_step=0, token_ids=(1, 2))
)
assert result.status.code is StatusCode.OK
assert isinstance(result.output, BoundaryBundle)
assert result.output.boundary_point == "post_head_residual"
def test_middle_shard_returns_boundary_bundle_and_passes_through_token_sideband():
engine = FakeShardEngine()
_load(engine, shard_start=1, shard_end=2, total_layers=8)
bundle_in = BoundaryBundle(
tensors=(EngineTensor(name="hidden_states", shape=(1, 2), dtype="bfloat16", data=b"\x01\x02\x03\x04"),),
architecture="dense",
boundary_point="pre_tail_residual",
token_id_sideband=(7, 8, 9),
)
result = engine.prefill(
PrefillRequest(session_id="s", route_epoch=1, position=0, idempotency_step=0, input=bundle_in)
)
assert result.status.code is StatusCode.OK
assert isinstance(result.output, BoundaryBundle)
assert result.output.boundary_point == "post_middle_residual"
assert result.output.token_id_sideband == (7, 8, 9)
def test_tail_shard_returns_token_output_within_advertised_vocab():
engine = FakeShardEngine()
_load(engine, shard_start=0, shard_end=3, total_layers=4)
result = engine.prefill(
PrefillRequest(session_id="s", route_epoch=1, position=0, idempotency_step=0, token_ids=(1, 2, 3))
)
assert isinstance(result.output, TokenOutput)
assert 0 <= result.output.token_id < TOKEN_ID_VOCAB_SIZE
def test_session_state_is_isolated_between_two_concurrent_sessions():
engine = FakeShardEngine()
_load(engine)
engine.prefill(PrefillRequest(session_id="a", route_epoch=5, position=0, idempotency_step=0, token_ids=(1,)))
engine.prefill(PrefillRequest(session_id="b", route_epoch=1, position=0, idempotency_step=0, token_ids=(1,)))
# A stale epoch against session "a" must not affect session "b" at all.
stale = engine.decode(DecodeRequest(session_id="a", route_epoch=4, position=1, idempotency_step=1, token_id=2))
assert stale.status.code is StatusCode.FAILED_PRECONDITION
still_fine = engine.decode(DecodeRequest(session_id="b", route_epoch=1, position=1, idempotency_step=1, token_id=2))
assert still_fine.status.code is StatusCode.OK
engine.cancel("a")
after_cancel_b = engine.decode(
DecodeRequest(session_id="b", route_epoch=1, position=2, idempotency_step=2, token_id=3)
)
assert after_cancel_b.status.code is StatusCode.OK, "cancelling session a must not cancel session b"
def test_step_delay_seconds_invokes_the_configured_sleep_hook():
calls: list[float] = []
engine = FakeShardEngine(FakeShardEngineConfig(step_delay_seconds=0.25, sleep=calls.append))
_load(engine)
engine.prefill(PrefillRequest(session_id="s", route_epoch=1, position=0, idempotency_step=0, token_ids=(1,)))
engine.decode(DecodeRequest(session_id="s", route_epoch=1, position=1, idempotency_step=1, token_id=2))
assert calls == [0.25, 0.25]
def test_memory_budget_bytes_trips_deterministic_resource_exhausted():
engine = FakeShardEngine(FakeShardEngineConfig(memory_budget_bytes=8))
_load(engine)
first = engine.prefill(
PrefillRequest(session_id="s", route_epoch=1, position=0, idempotency_step=0, token_ids=(1,))
)
assert first.status.code is StatusCode.OK # 8 bytes used, exactly at budget
second = engine.decode(DecodeRequest(session_id="s", route_epoch=1, position=1, idempotency_step=1, token_id=2))
assert second.status.code is StatusCode.RESOURCE_EXHAUSTED
assert second.status.retryable is True
assert second.output is None
def test_malformed_output_is_structurally_valid_but_semantically_wrong_for_tail():
engine = FakeShardEngine(FakeShardEngineConfig(malformed_output=True))
_load(engine, shard_start=0, shard_end=3, total_layers=4)
result = engine.prefill(
PrefillRequest(session_id="s", route_epoch=1, position=0, idempotency_step=0, token_ids=(1, 2, 3))
)
assert result.status.code is StatusCode.OK
assert isinstance(result.output, TokenOutput)
assert result.output.token_id >= MALFORMED_TOKEN_ID_FLOOR
def test_malformed_output_is_structurally_valid_but_semantically_wrong_for_boundary_bundle():
engine = FakeShardEngine(FakeShardEngineConfig(malformed_output=True))
_load(engine, shard_start=0, shard_end=1, total_layers=4)
result = engine.prefill(
PrefillRequest(session_id="s", route_epoch=1, position=0, idempotency_step=0, token_ids=(1, 2, 3))
)
assert result.status.code is StatusCode.OK
assert isinstance(result.output, BoundaryBundle)
assert result.output.architecture.startswith("malformed:")
assert len(result.output.tensors[0].data) == 1
def test_crash_after_calls_raises_instead_of_returning_a_structured_status():
engine = FakeShardEngine(FakeShardEngineConfig(crash_after_calls=2))
_load(engine)
ok = engine.prefill(PrefillRequest(session_id="s", route_epoch=1, position=0, idempotency_step=0, token_ids=(1,)))
assert ok.status.code is StatusCode.OK
with pytest.raises(RuntimeError):
engine.decode(DecodeRequest(session_id="s", route_epoch=1, position=1, idempotency_step=1, token_id=2))
def test_crash_exception_factory_is_configurable():
class _SimulatedSegfault(Exception):
pass
engine = FakeShardEngine(
FakeShardEngineConfig(crash_after_calls=1, crash_exception_factory=_SimulatedSegfault)
)
_load(engine)
with pytest.raises(_SimulatedSegfault):
engine.prefill(PrefillRequest(session_id="s", route_epoch=1, position=0, idempotency_step=0, token_ids=(1,)))
def test_config_rejects_invalid_knob_values():
with pytest.raises(ValueError):
FakeShardEngineConfig(step_delay_seconds=-1.0)
with pytest.raises(ValueError):
FakeShardEngineConfig(memory_budget_bytes=-1)
with pytest.raises(ValueError):
FakeShardEngineConfig(crash_after_calls=0)
def test_load_result_and_capabilities_report_recipe_architecture():
engine = FakeShardEngine()
load_result = _load(engine, recipe={"architecture": "deepseek-v4-flash"})
assert load_result.architecture == "deepseek-v4-flash"
caps = engine.capabilities()
assert caps.architecture == "deepseek-v4-flash"

View File

@@ -334,3 +334,157 @@ def test_ctest_lane_raises_an_actionable_error_for_a_failing_named_test(tmp_path
assert "meshnet-fixture-fail" in str(error)
else:
raise AssertionError("a failing named CTest lane must raise DependencyError")
def test_accelerator_presets_isolate_one_backend_without_touching_the_cpu_default() -> None:
dependency = _load_dependency_module()
lock = json.loads((LLAMA_DIR / "UPSTREAM_LOCK.json").read_text())
presets = lock["accelerator_presets"]
assert set(presets) == {"cuda", "rocm", "vulkan", "metal"}
base_flags = list(lock["build"]["configure_flags"])
base_values = dict(flag[len("-D"):].split("=", 1) for flag in base_flags)
for name, preset in presets.items():
flags = dependency.accelerator_configure_flags(lock, name)
# The CPU default's own flag list is never mutated by building a preset.
assert lock["build"]["configure_flags"] == base_flags
new_values = dict(flag[len("-D"):].split("=", 1) for flag in flags)
backend_flag = preset["backend_flag"]
assert base_values[backend_flag] == "OFF"
assert new_values[backend_flag] == "ON"
for other_flag, value in base_values.items():
if other_flag != backend_flag:
assert new_values[other_flag] == value, f"{name}: {other_flag} drifted from the CPU default"
def test_accelerator_configure_flags_rejects_an_unknown_lane() -> None:
dependency = _load_dependency_module()
lock = json.loads((LLAMA_DIR / "UPSTREAM_LOCK.json").read_text())
try:
dependency.accelerator_configure_flags(lock, "bogus")
except dependency.DependencyError as error:
assert "unknown accelerator lane" in str(error)
else:
raise AssertionError("an unknown accelerator lane must be refused")
def test_accelerator_status_reports_unavailable_sdks_without_raising(monkeypatch) -> None:
dependency = _load_dependency_module()
lock = json.loads((LLAMA_DIR / "UPSTREAM_LOCK.json").read_text())
monkeypatch.setattr(dependency.shutil, "which", lambda name: None)
for name in ("cuda", "rocm", "vulkan"):
env_var = lock["accelerator_presets"][name]["sdk_probe"]["env_var"]
monkeypatch.delenv(env_var, raising=False)
binary = lock["accelerator_presets"][name]["sdk_probe"]["binary"]
assert dependency.accelerator_status(name, lock) == {
"lane": name,
"available": False,
"reason": f"{binary} is unavailable on PATH",
}
monkeypatch.setattr(dependency.sys, "platform", "linux")
assert dependency.accelerator_status("metal", lock) == {
"lane": "metal",
"available": False,
"reason": "platform 'linux' is not 'darwin'",
}
def test_accelerator_status_honors_an_explicit_sdk_override(tmp_path, monkeypatch) -> None:
dependency = _load_dependency_module()
lock = json.loads((LLAMA_DIR / "UPSTREAM_LOCK.json").read_text())
fake_nvcc = tmp_path / "nvcc"
fake_nvcc.write_text("#!/bin/sh\nexit 0\n")
fake_nvcc.chmod(0o755)
monkeypatch.setenv("CUDACXX", str(fake_nvcc))
assert dependency.accelerator_status("cuda", lock) == {
"lane": "cuda",
"available": True,
"sdk_binary": str(fake_nvcc),
}
def test_accelerator_status_rejects_an_unknown_lane() -> None:
dependency = _load_dependency_module()
lock = json.loads((LLAMA_DIR / "UPSTREAM_LOCK.json").read_text())
try:
dependency.accelerator_status("bogus", lock)
except dependency.DependencyError as error:
assert "unknown accelerator lane" in str(error)
else:
raise AssertionError("an unknown accelerator lane must be refused")
def test_accelerator_build_refuses_to_compile_an_unavailable_lane(tmp_path, monkeypatch) -> None:
dependency = _load_dependency_module()
source = tmp_path / "source"
(source / "cmake").mkdir(parents=True)
(source / "cmake" / "meshnet-patch-stack.cmake").write_text("# marker\n")
monkeypatch.setattr(dependency, "_verify_source", lambda *a, **k: None)
monkeypatch.setattr(dependency, "_verify_patched_source", lambda *a, **k: None)
monkeypatch.setattr(dependency.shutil, "which", lambda name: None)
monkeypatch.delenv("CUDACXX", raising=False)
build_dir = tmp_path / "build-cuda"
try:
dependency.accelerator_build(source, "cuda", build_dir)
except dependency.DependencyError as error:
assert "SDK is unavailable" in str(error)
else:
raise AssertionError("accelerator_build must refuse to compile an unavailable lane")
assert not build_dir.exists()
@requires_cmake
def test_accelerator_build_compiles_the_available_lane_with_isolated_evidence(tmp_path, monkeypatch) -> None:
dependency = _load_dependency_module()
# A tiny synthetic project stands in for the patched llama.cpp checkout —
# it only needs the meshnet patch-stack marker and one target, proving
# accelerator_build's configure/build/evidence wiring without a multi-minute
# llama.cpp compile or a real GPU SDK.
source = tmp_path / "source"
(source / "cmake").mkdir(parents=True)
(source / "cmake" / "meshnet-patch-stack.cmake").write_text("# marker\n")
(source / "CMakeLists.txt").write_text(
"cmake_minimum_required(VERSION 3.14)\n"
"project(accelerator_lane_fixture NONE)\n"
"option(GGML_CUDA \"\" OFF)\n"
"if(GGML_CUDA)\n"
" file(WRITE ${CMAKE_BINARY_DIR}/lane-flag-on.txt \"on\")\n"
"endif()\n"
"add_custom_target(fixture-target ALL COMMAND ${CMAKE_COMMAND} -E true)\n"
)
base_lock = json.loads((LLAMA_DIR / "UPSTREAM_LOCK.json").read_text())
fake_lock = dict(base_lock)
fake_lock["build"] = {
**base_lock["build"],
"generator": "Unix Makefiles",
"configure_flags": ["-DGGML_CUDA=OFF"],
"native_targets": ["fixture-target"],
}
monkeypatch.setattr(dependency, "_load_lock", lambda: fake_lock)
monkeypatch.setattr(dependency, "_patches", lambda lock: [])
monkeypatch.setattr(dependency, "_verify_source", lambda *a, **k: None)
monkeypatch.setattr(dependency, "_verify_patched_source", lambda *a, **k: None)
monkeypatch.setenv("CUDACXX", str(dependency._cmake()))
build_dir = tmp_path / "build-cuda"
result = dependency.accelerator_build(source, "cuda", build_dir)
assert result == build_dir
assert (build_dir / "lane-flag-on.txt").is_file()
metadata = json.loads((build_dir / "meshnet-build-metadata.json").read_text())
assert metadata["lane"] == "cuda"
assert metadata["backend_flag"] == "GGML_CUDA"
assert metadata["configure_flags"] == ["-DGGML_CUDA=ON"]
assert metadata["hardware_execution"] is False
assert metadata["hardware_certified"] is False
assert metadata["semantic_certification"] is False
assert "registered-dark" in metadata["note"]

View File

@@ -0,0 +1,275 @@
"""DGR-034: end-to-end owned-range loads through the native report tool.
Gated on the built ``meshnet-range-report`` binary (the deterministic
CPU-only native lane builds it from the pinned, patched llama.cpp tree); in
an environment without that build these tests skip rather than fake a pass.
When the binary is present they run real loads of a tiny synthetic
dense-Llama GGUF — no model download, no GPU — and prove the loader
registers exactly the owned tensors, reports ownership derived from the
loaded state, and rejects invalid/out-of-model ranges and missing required
tensors. The JSON is consumed through ``meshnet_node.range_report`` so the
strict project-owned contract is exercised on real tool output.
"""
from __future__ import annotations
import json
import os
import struct
import subprocess
import sys
from pathlib import Path
import pytest
from meshnet_node.range_report import RangeReportError, parse_owned_range_report
REPO_ROOT = Path(__file__).resolve().parent.parent
DEFAULT_BINARY = REPO_ROOT / "build" / "llama.cpp" / "build" / "bin" / "meshnet-range-report"
BINARY = Path(os.environ.get("MESHNET_RANGE_REPORT_BIN", DEFAULT_BINARY))
requires_range_report_tool = pytest.mark.skipif(
not BINARY.is_file(),
reason=(
"meshnet-range-report is not built; run the deterministic native lane "
"(scripts/llama_cpp_dependency.py build) to enable these tests"
),
)
# --- Minimal GGUF v3 writer, mirroring the model-free native fixture --------
K_LAYERS = 4
K_EMBD = 8
K_FFN = 16
K_VOCAB = 16
ALIGNMENT = 32
_GGUF_UINT32 = 4
_GGUF_FLOAT32 = 6
_GGUF_STRING = 8
_GGML_TYPE_F32 = 0
def _gguf_string(value: str) -> bytes:
data = value.encode("utf-8")
return struct.pack("<Q", len(data)) + data
def _metadata_entries() -> list[tuple[str, int, object]]:
return [
("general.architecture", _GGUF_STRING, "llama"),
("general.alignment", _GGUF_UINT32, ALIGNMENT),
("llama.context_length", _GGUF_UINT32, 16),
("llama.embedding_length", _GGUF_UINT32, K_EMBD),
("llama.block_count", _GGUF_UINT32, K_LAYERS),
("llama.feed_forward_length", _GGUF_UINT32, K_FFN),
("llama.attention.head_count", _GGUF_UINT32, 2),
("llama.attention.head_count_kv", _GGUF_UINT32, 2),
("llama.rope.dimension_count", _GGUF_UINT32, 4),
("llama.attention.layer_norm_rms_epsilon", _GGUF_FLOAT32, 1.0e-5),
("tokenizer.ggml.model", _GGUF_STRING, "no_vocab"),
("llama.vocab_size", _GGUF_UINT32, K_VOCAB),
]
def _fixture_tensors() -> list[tuple[str, tuple[int, ...]]]:
tensors: list[tuple[str, tuple[int, ...]]] = [
("token_embd.weight", (K_EMBD, K_VOCAB)),
("output_norm.weight", (K_EMBD,)),
("output.weight", (K_EMBD, K_VOCAB)),
]
for layer in range(K_LAYERS):
prefix = f"blk.{layer}."
tensors += [
(prefix + "attn_norm.weight", (K_EMBD,)),
(prefix + "attn_q.weight", (K_EMBD, K_EMBD)),
(prefix + "attn_k.weight", (K_EMBD, K_EMBD)),
(prefix + "attn_v.weight", (K_EMBD, K_EMBD)),
(prefix + "attn_output.weight", (K_EMBD, K_EMBD)),
(prefix + "ffn_norm.weight", (K_EMBD,)),
(prefix + "ffn_gate.weight", (K_EMBD, K_FFN)),
(prefix + "ffn_down.weight", (K_FFN, K_EMBD)),
(prefix + "ffn_up.weight", (K_EMBD, K_FFN)),
]
return tensors
def write_dense_llama_gguf(path: Path, *, drop: frozenset[str] = frozenset()) -> Path:
"""Write a tiny dense-Llama GGUF; ``drop`` omits tensors (corruption cases)."""
kvs = _metadata_entries()
tensors = [(name, dims) for name, dims in _fixture_tensors() if name not in drop]
blob = bytearray()
blob += b"GGUF" + struct.pack("<IQQ", 3, len(tensors), len(kvs))
for key, vtype, value in kvs:
blob += _gguf_string(key)
blob += struct.pack("<I", vtype)
if vtype == _GGUF_STRING:
blob += _gguf_string(value) # type: ignore[arg-type]
elif vtype == _GGUF_UINT32:
blob += struct.pack("<I", value) # type: ignore[arg-type]
elif vtype == _GGUF_FLOAT32:
blob += struct.pack("<f", value) # type: ignore[arg-type]
else: # pragma: no cover - writer guard
raise AssertionError(f"unhandled kv type {vtype}")
offset = 0
infos = bytearray()
data = bytearray()
for name, dims in tensors:
infos += _gguf_string(name)
infos += struct.pack("<I", len(dims))
for dim in dims:
infos += struct.pack("<Q", dim)
infos += struct.pack("<IQ", _GGML_TYPE_F32, offset)
size = 4
for dim in dims:
size *= dim
assert size % ALIGNMENT == 0
data += bytes(size)
offset += size
blob += infos
blob += bytes(-len(blob) % ALIGNMENT) # pad header to the data section
blob += data
path.write_bytes(bytes(blob))
return path
# --- Tool driver -------------------------------------------------------------
LAYER_BYTES = 2624 # 9 registered F32 tensors per layer, see _fixture_tensors
EMBD_BYTES = 512
OUT_NORM_BYTES = 32
OUT_BYTES = 512
def run_tool(model: Path, start: int, end: int, *extra: str) -> tuple[int, dict]:
env = dict(os.environ)
env["LD_LIBRARY_PATH"] = f"{BINARY.parent}:{env.get('LD_LIBRARY_PATH', '')}"
completed = subprocess.run(
[
str(BINARY),
"--model", str(model),
"--start", str(start),
"--end", str(end),
*extra,
],
capture_output=True,
text=True,
env=env,
timeout=120,
)
try:
doc = json.loads(completed.stdout)
except json.JSONDecodeError as exc: # pragma: no cover - diagnostic path
raise AssertionError(
f"tool did not print a JSON report (exit {completed.returncode}): "
f"{completed.stdout!r} {completed.stderr!r}"
) from exc
return completed.returncode, doc
@pytest.fixture(scope="module")
def dense_llama_gguf(tmp_path_factory: pytest.TempPathFactory) -> Path:
return write_dense_llama_gguf(tmp_path_factory.mktemp("gguf") / "dense-llama.gguf")
@requires_range_report_tool
class TestOwnedRangeLoads:
def test_middle_range_registers_exactly_its_layers(self, dense_llama_gguf: Path) -> None:
code, doc = run_tool(dense_llama_gguf, 1, 3, "--no-extra-bufts")
assert code == 0
report = parse_owned_range_report(doc)
assert (report.start_layer, report.end_layer) == (1, 3)
assert report.registered_tensors == 18
assert report.registered_bytes == 2 * LAYER_BYTES
# The fixture layers are contiguous in the file, so the pure mmap span
# is exactly the owned tensor bytes — scaled down from the artifact.
assert report.mapped_bytes == 2 * LAYER_BYTES
assert report.mapped_bytes < report.file_bytes
def test_head_range_owns_embeddings(self, dense_llama_gguf: Path) -> None:
code, doc = run_tool(dense_llama_gguf, 0, 1)
assert code == 0
report = parse_owned_range_report(doc)
assert report.is_head and report.has_token_embeddings
assert not report.has_output_head
assert report.registered_tensors == 10
assert report.registered_bytes == EMBD_BYTES + LAYER_BYTES
def test_tail_range_owns_norm_and_output(self, dense_llama_gguf: Path) -> None:
code, doc = run_tool(dense_llama_gguf, 3, 4)
assert code == 0
report = parse_owned_range_report(doc)
assert report.is_tail and report.has_output_head
assert not report.has_token_embeddings
assert report.registered_tensors == 11
assert report.registered_bytes == LAYER_BYTES + OUT_NORM_BYTES + OUT_BYTES
def test_shards_partition_the_whole_model_bytes(self, dense_llama_gguf: Path) -> None:
shards = [(0, 1), (1, 3), (3, 4)]
registered = []
for start, end in shards:
code, doc = run_tool(dense_llama_gguf, start, end)
assert code == 0
registered.append(parse_owned_range_report(doc).registered_bytes)
code, doc = run_tool(dense_llama_gguf, 0, 4)
assert code == 0
whole = parse_owned_range_report(doc)
assert whole.registered_tensors == 3 + 9 * K_LAYERS
assert sum(registered) == whole.registered_bytes
def test_non_mmap_load_scales_resident_with_the_range(self, dense_llama_gguf: Path) -> None:
code, doc = run_tool(dense_llama_gguf, 1, 3, "--no-mmap")
assert code == 0
report = parse_owned_range_report(doc)
assert report.mapped_bytes == 0
assert report.registered_bytes == 2 * LAYER_BYTES
code, doc = run_tool(dense_llama_gguf, 0, 4, "--no-mmap")
assert code == 0
whole = parse_owned_range_report(doc)
assert report.resident_bytes < whole.resident_bytes
@requires_range_report_tool
class TestRangeRejection:
def test_out_of_model_range_is_refused(self, dense_llama_gguf: Path) -> None:
code, doc = run_tool(dense_llama_gguf, 3, 5)
assert code == 3 and doc["ok"] is False
with pytest.raises(RangeReportError):
parse_owned_range_report(doc)
def test_empty_range_is_refused(self, dense_llama_gguf: Path) -> None:
code, doc = run_tool(dense_llama_gguf, 2, 2)
assert code == 3 and doc["ok"] is False
def test_inverted_range_is_refused(self, dense_llama_gguf: Path) -> None:
code, doc = run_tool(dense_llama_gguf, 3, 1)
assert code == 3 and doc["ok"] is False
def test_missing_required_owned_tensor_is_refused(self, tmp_path: Path) -> None:
corrupted = write_dense_llama_gguf(
tmp_path / "missing-tensor.gguf", drop=frozenset({"blk.1.attn_q.weight"})
)
code, doc = run_tool(corrupted, 0, 2)
assert code == 3 and doc["ok"] is False
assert "blk.1.attn_q.weight" in doc["error"]
def test_whole_model_load_still_works_through_the_range_loader(
self, dense_llama_gguf: Path
) -> None:
code, doc = run_tool(dense_llama_gguf, 0, 4)
assert code == 0
report = parse_owned_range_report(doc)
assert report.is_head and report.is_tail
assert report.has_token_embeddings and report.has_output_head
def test_tool_binary_gate_points_at_the_locked_build() -> None:
# The gate must name the deterministic lane's output, never a downloaded binary.
assert DEFAULT_BINARY.name == "meshnet-range-report"
assert "llama.cpp" in DEFAULT_BINARY.parts
assert DEFAULT_BINARY.parent.name == "bin"
assert DEFAULT_BINARY.parent.parent.name == "build"

View File

@@ -0,0 +1,171 @@
"""Offline behavior tests for DGR-030's native CI/build matrix orchestration.
These tests never fetch or compile llama.cpp: `llama_cpp_dependency`'s fetch/
apply/reverse/build/smoke/ctest_lane/accelerator_status/accelerator_build are
stubbed so the matrix's own lane-reporting and cleanup contract is exercised
in isolation. The real compile path is covered separately by
`tests/test_llama_cpp_dependency.py`'s `accelerator_build`/CPU-lane tests and
by a live run recorded in the DGR-030 evidence README.
"""
from __future__ import annotations
import importlib.util
import json
import pathlib
import sys
ROOT = pathlib.Path(__file__).resolve().parents[1]
MATRIX_SCRIPT = ROOT / "scripts/native_accelerator_matrix.py"
DEP_SCRIPT = ROOT / "scripts/llama_cpp_dependency.py"
def _load_matrix_module(monkeypatch):
"""Load private copies of both modules with a controllable `dep`.
`native_accelerator_matrix.py` does `import llama_cpp_dependency as dep`
after inserting `scripts/` onto `sys.path`; pre-registering our own module
instance under that name in `sys.modules` (undone by monkeypatch at
teardown) makes the matrix module bind to the stub instead of importing a
fresh copy of the real dependency module.
"""
dep_spec = importlib.util.spec_from_file_location("llama_cpp_dependency_matrix_dep", DEP_SCRIPT)
dep = importlib.util.module_from_spec(dep_spec)
dep_spec.loader.exec_module(dep)
monkeypatch.setitem(sys.modules, "llama_cpp_dependency", dep)
matrix_spec = importlib.util.spec_from_file_location("native_accelerator_matrix", MATRIX_SCRIPT)
matrix = importlib.util.module_from_spec(matrix_spec)
matrix_spec.loader.exec_module(matrix)
return matrix, dep
def test_matrix_reports_unavailable_accelerator_sdks_as_skipped_not_false_success(tmp_path, monkeypatch) -> None:
matrix, dep = _load_matrix_module(monkeypatch)
workspace = tmp_path / "llama.cpp"
source = workspace / "source"
source.mkdir(parents=True)
calls: list = []
monkeypatch.setattr(dep, "fetch", lambda ws: source)
monkeypatch.setattr(dep, "apply", lambda src: calls.append(("apply", src)))
monkeypatch.setattr(dep, "reverse", lambda src: calls.append(("reverse", src)))
monkeypatch.setattr(
dep,
"_load_lock",
lambda: {"accelerator_presets": {"cuda": {}, "rocm": {}, "vulkan": {}, "metal": {}}},
)
def _cpu_build(src, build_dir):
build_dir.mkdir(parents=True)
(build_dir / "meshnet-build-metadata.json").write_text(json.dumps({"lane": "cpu"}))
return build_dir / "bin/llama-gguf-hash"
monkeypatch.setattr(dep, "build", _cpu_build)
monkeypatch.setattr(dep, "smoke", lambda binary: calls.append(("smoke", binary)))
monkeypatch.setattr(dep, "ctest_lane", lambda build_dir: calls.append(("ctest", build_dir)))
monkeypatch.setattr(
dep,
"accelerator_status",
lambda name, lock: {"lane": name, "available": False, "reason": f"{name} SDK is unavailable on PATH"},
)
report = matrix.run_matrix(workspace)
assert report["lanes"][0] == {
"lane": "cpu",
"status": "built",
"build_dir": str((workspace / "build").resolve()),
"metadata": {"lane": "cpu"},
}
accelerator_lanes = {lane["lane"]: lane for lane in report["lanes"][1:]}
assert set(accelerator_lanes) == {"cuda", "rocm", "vulkan", "metal"}
for name, lane in accelerator_lanes.items():
assert lane["status"] == "skipped"
assert "unavailable" in lane["reason"]
assert report["failed_lanes"] == []
assert report["hardware_certified"] is False
assert ("reverse", source) in calls # cleanup always runs
# Only the CPU lane is ever smoke-tested/ctested; skipped accelerator lanes are not.
smoke_calls = [call for call in calls if call[0] == "smoke"]
ctest_calls = [call for call in calls if call[0] == "ctest"]
assert smoke_calls == [("smoke", (workspace.resolve() / "build" / "bin/llama-gguf-hash"))]
assert ctest_calls == [("ctest", (workspace.resolve() / "build"))]
def test_matrix_compiles_an_available_accelerator_lane_without_smoke_or_ctest(tmp_path, monkeypatch) -> None:
matrix, dep = _load_matrix_module(monkeypatch)
workspace = tmp_path / "llama.cpp"
source = workspace / "source"
source.mkdir(parents=True)
calls: list = []
monkeypatch.setattr(dep, "fetch", lambda ws: source)
monkeypatch.setattr(dep, "apply", lambda src: None)
monkeypatch.setattr(dep, "reverse", lambda src: calls.append("reverse"))
monkeypatch.setattr(dep, "_load_lock", lambda: {"accelerator_presets": {"cuda": {}}})
monkeypatch.setattr(
matrix,
"_cpu_lane",
lambda src, ws: {"lane": "cpu", "status": "skipped", "reason": "pre-existing build dir"},
)
monkeypatch.setattr(
dep, "accelerator_status", lambda name, lock: {"lane": name, "available": True, "sdk_binary": "/fake/nvcc"}
)
def _accelerator_build(src, name, build_dir):
calls.append(("accelerator_build", name))
build_dir.mkdir(parents=True)
(build_dir / "meshnet-build-metadata.json").write_text(
json.dumps({"lane": name, "hardware_certified": False})
)
return build_dir
monkeypatch.setattr(dep, "accelerator_build", _accelerator_build)
monkeypatch.setattr(dep, "smoke", lambda binary: calls.append(("smoke", binary)))
monkeypatch.setattr(dep, "ctest_lane", lambda build_dir: calls.append(("ctest", build_dir)))
report = matrix.run_matrix(workspace)
assert report["lanes"][1]["lane"] == "cuda"
assert report["lanes"][1]["status"] == "built"
assert report["lanes"][1]["metadata"]["hardware_certified"] is False
assert ("accelerator_build", "cuda") in calls
assert not any(call[0] in ("smoke", "ctest") for call in calls if isinstance(call, tuple))
assert "reverse" in calls
def test_matrix_reports_a_lane_failure_without_aborting_the_others_or_skipping_reverse(tmp_path, monkeypatch) -> None:
matrix, dep = _load_matrix_module(monkeypatch)
workspace = tmp_path / "llama.cpp"
source = workspace / "source"
source.mkdir(parents=True)
calls: list = []
monkeypatch.setattr(dep, "fetch", lambda ws: source)
monkeypatch.setattr(dep, "apply", lambda src: None)
monkeypatch.setattr(dep, "reverse", lambda src: calls.append("reverse"))
monkeypatch.setattr(dep, "_load_lock", lambda: {"accelerator_presets": {"cuda": {}, "vulkan": {}}})
def _cpu_lane_raises(src, ws):
raise dep.DependencyError("simulated cpu compile failure")
monkeypatch.setattr(matrix, "_cpu_lane", _cpu_lane_raises)
monkeypatch.setattr(
dep,
"accelerator_status",
lambda name, lock: {"lane": name, "available": False, "reason": f"{name} SDK is unavailable on PATH"},
)
report = matrix.run_matrix(workspace)
assert report["lanes"][0] == {"lane": "cpu", "status": "failed", "reason": "simulated cpu compile failure"}
assert report["failed_lanes"] == ["cpu"]
accelerator_statuses = {lane["lane"]: lane["status"] for lane in report["lanes"][1:]}
assert accelerator_statuses == {"cuda": "skipped", "vulkan": "skipped"}
assert "reverse" in calls

View File

@@ -0,0 +1,636 @@
"""DGR-033 integration tests for the standalone native C++ Shard worker.
These tests spawn the *real* compiled ``shard_worker`` executable as a separate
OS process, connect to its real localhost socket with the committed generated
``ShardRuntimeStub`` stubs, and drive the complete lifecycle/stream contract.
There is no in-memory channel, no Python servicer, and no fake transport: the
server under test is the C++ binary DGR-033 builds.
The worker binary is located via ``MESHNET_SHARD_WORKER_BIN`` or the default
out-of-tree build path ``build/native/shard_worker``. When it has not been
built (a default developer/CI checkout without the pinned gRPC C++ toolchain),
every test here is skipped rather than failed — the same ``requires_cmake``
gating pattern DGR-029/DGR-030 use for native-build-dependent tests. The
session that implemented DGR-033 built the binary and ran these for real; see
``evidence/DGR-033/README.md`` for the exact commands and results.
"""
from __future__ import annotations
import os
import signal
import socket
import subprocess
import time
import zlib
import grpc
import pytest
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
_PYTHONPATH = os.pathsep.join(
[os.path.join(REPO_ROOT, "packages", "node"), os.path.join(REPO_ROOT, "packages", "tracker")]
)
from meshnet_node.native_protocol.generated import ( # noqa: E402
shard_runtime_pb2 as pb,
shard_runtime_pb2_grpc as pb_grpc,
)
def _worker_binary() -> str | None:
explicit = os.environ.get("MESHNET_SHARD_WORKER_BIN")
if explicit and os.path.exists(explicit):
return explicit
default = os.path.join(REPO_ROOT, "build", "native", "shard_worker")
if os.path.exists(default):
return default
return None
_WORKER_BIN = _worker_binary()
pytestmark = pytest.mark.skipif(
_WORKER_BIN is None,
reason=(
"native shard_worker binary not built; build packages/node/native with the "
"pinned gRPC C++ toolchain or set MESHNET_SHARD_WORKER_BIN"
),
)
def _free_port() -> int:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("127.0.0.1", 0))
port = s.getsockname()[1]
s.close()
return port
def _start_worker(listen_addr: str, extra_env: dict[str, str] | None = None) -> subprocess.Popen:
env = dict(os.environ)
env["PYTHONPATH"] = _PYTHONPATH
if extra_env:
env.update(extra_env)
proc = subprocess.Popen(
[_WORKER_BIN, listen_addr],
cwd=REPO_ROOT,
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
deadline = time.time() + 30.0
while time.time() < deadline:
line = proc.stdout.readline()
if not line:
if proc.poll() is not None:
out, _ = proc.communicate()
raise RuntimeError(f"worker exited early:\n{out}")
continue
if "listening on" in line:
return proc
raise RuntimeError("worker did not start listening in time")
class _Worker:
"""A spawned worker plus a ready channel; also captures stdout on close."""
def __init__(self, extra_env: dict[str, str] | None = None) -> None:
self.port = _free_port()
self.addr = f"127.0.0.1:{self.port}"
self.proc = _start_worker(self.addr, extra_env)
self.channel = grpc.insecure_channel(self.addr)
grpc.channel_ready_future(self.channel).result(timeout=15.0)
def stub(self) -> pb_grpc.ShardRuntimeStub:
return pb_grpc.ShardRuntimeStub(self.channel)
def session(self, requests):
call = self.channel.stream_stream(
"/meshnet.shard.v1.ShardRuntime/Session",
request_serializer=lambda m: m.SerializeToString(),
response_deserializer=pb.SessionResponse.FromString,
)
return list(call(iter(requests)))
def close(self, *, sig: int = signal.SIGTERM) -> str:
self.channel.close()
self.proc.send_signal(sig)
try:
out, _ = self.proc.communicate(timeout=10)
except subprocess.TimeoutExpired:
self.proc.kill()
out, _ = self.proc.communicate()
return out or ""
@pytest.fixture()
def worker():
w = _Worker()
try:
yield w
finally:
if w.proc.poll() is None:
w.close()
def _crc32c(payload: bytes) -> bytes:
return zlib.crc32(payload).to_bytes(4, "big")
_WORKER_FINGERPRINT = dict(
model_artifact_digest="sha256:native-test-artifact",
runtime_recipe_digest="sha256:native-test-recipe",
recipe_id="native-test",
recipe_version="1",
catalogue_version="1",
)
def _open(
*,
route_session_id="rs-1",
route_epoch=7,
credits_granted=16,
max_inflight_chunks=16,
max_chunk_bytes=4 * 1024 * 1024,
schema_version=pb.SCHEMA_VERSION_1,
fingerprint=None,
shard_range=None,
) -> pb.SessionRequest:
fp = pb.Fingerprint(**_WORKER_FINGERPRINT) if fingerprint is None else fingerprint
sr = (
pb.ShardRange(start_layer=0, end_layer=32, effective_start_layer=0)
if shard_range is None
else shard_range
)
return pb.SessionRequest(
open=pb.SessionOpen(
schema_version=schema_version,
route_session_id=route_session_id,
route_epoch=route_epoch,
fingerprint=fp,
shard_range=sr,
proposed_flow_control=pb.FlowControl(
credits_granted=credits_granted,
max_inflight_chunks=max_inflight_chunks,
max_chunk_bytes=max_chunk_bytes,
max_prefill_chunk_tokens=512,
),
accepted_compression=[pb.COMPRESSION_NONE],
)
)
def _chunk(
work_id,
payload: bytes,
step,
*,
route_session_id="rs-1",
route_epoch=7,
deadline_unix_nanos=0,
fragments=1,
total_bytes=None,
) -> pb.SessionRequest:
total = len(payload) if total_bytes is None else total_bytes
frags = []
if fragments == 1:
frags = [pb.TensorFragment(fragment_index=0, fragment_count=1, byte_offset=0, payload=payload)]
else:
# Split into ``fragments`` tiling pieces.
size = max(1, len(payload) // fragments)
offset = 0
idx = 0
while offset < len(payload):
piece = payload[offset : offset + size] if idx < fragments - 1 else payload[offset:]
frags.append(
pb.TensorFragment(
fragment_index=idx, fragment_count=fragments, byte_offset=offset, payload=piece
)
)
offset += len(piece)
idx += 1
tensor = pb.NamedTensor(
name="hidden_states",
shape=[1, 1, 4096],
dtype=pb.DTYPE_BFLOAT16,
byte_order=pb.BYTE_ORDER_LITTLE_ENDIAN,
total_bytes=total,
compression=pb.COMPRESSION_NONE,
checksum=pb.Checksum(algorithm=pb.CHECKSUM_ALGORITHM_CRC32C, value=_crc32c(payload)),
fragments=frags,
)
bundle = pb.TensorBundle(
bundle_version=1,
tensors=[tensor],
architecture=pb.ARCHITECTURE_TYPE_DENSE,
boundary_point="pre_tail_residual",
)
envelope = pb.Envelope(
schema_version=pb.SCHEMA_VERSION_1,
work_id=work_id,
route_session_id=route_session_id,
route_epoch=route_epoch,
idempotency_step=step,
phase=pb.PHASE_PREFILL,
position=pb.PositionSpan(first_position=0, token_count=1),
deadline_unix_nanos=deadline_unix_nanos,
)
return pb.SessionRequest(chunk=pb.ActivationChunk(envelope=envelope, bundle=bundle))
def _decode(work_id, payload: bytes, step, position) -> pb.SessionRequest:
tensor = pb.NamedTensor(
name="hidden_states",
shape=[1, 1, 4096],
dtype=pb.DTYPE_BFLOAT16,
byte_order=pb.BYTE_ORDER_LITTLE_ENDIAN,
total_bytes=len(payload),
compression=pb.COMPRESSION_NONE,
checksum=pb.Checksum(algorithm=pb.CHECKSUM_ALGORITHM_CRC32C, value=_crc32c(payload)),
fragments=[pb.TensorFragment(fragment_index=0, fragment_count=1, byte_offset=0, payload=payload)],
)
return pb.SessionRequest(
decode=pb.DecodeStep(
idempotency_step=step,
position=position,
expected_past_len=position,
work_id=work_id,
bundle=pb.TensorBundle(bundle_version=1, tensors=[tensor], architecture=pb.ARCHITECTURE_TYPE_DENSE),
)
)
def _release() -> pb.SessionRequest:
return pb.SessionRequest(
release=pb.ReleaseSignal(route_session_id="rs-1", route_epoch=7, work_id="work-final")
)
def _cancel(*, route_session_id="rs-1", work_id="", reason="test cancel") -> pb.SessionRequest:
return pb.SessionRequest(
cancel=pb.CancelSignal(route_session_id=route_session_id, route_epoch=7, work_id=work_id, reason=reason)
)
# --- startup / health / capability ----------------------------------------
def test_worker_startup_and_health(worker):
health = worker.stub().Health(pb.HealthRequest(schema_version=pb.SCHEMA_VERSION_1))
assert health.state == pb.SERVING_STATE_SERVING
assert health.schema_version == pb.SCHEMA_VERSION_1
def test_worker_capability(worker):
cap = worker.stub().GetCapability(pb.CapabilityRequest(schema_version=pb.SCHEMA_VERSION_1))
assert cap.validated is True
assert cap.schema_version == pb.SCHEMA_VERSION_1
assert cap.shard_range.end_layer == 32
assert pb.SCHEMA_VERSION_1 in cap.supported_schema_versions
# --- fragmented prefill / decode / release ---------------------------------
def test_fragmented_prefill_echoes_reassembled_payload(worker):
payload = b"REAL_ACTIVATION_BYTES_prefill_across_three_fragments_1234567890"
responses = worker.session([_open(), _chunk("w1", payload, step=1, fragments=3), _release()])
assert responses[0].WhichOneof("kind") == "accepted"
echoed = responses[1]
assert echoed.WhichOneof("kind") == "chunk"
got = b"".join(f.payload for f in echoed.chunk.bundle.tensors[0].fragments)
assert got == payload
assert echoed.chunk.bundle.tensors[0].checksum.value == _crc32c(payload)
assert responses[2].status.terminal is True
def test_decode_step_is_served(worker):
payload = b"REAL_ACTIVATION_BYTES_decode_step"
responses = worker.session([_open(), _decode("w2", payload, step=1, position=1)])
echoed = responses[1]
assert echoed.WhichOneof("kind") == "chunk"
assert echoed.chunk.envelope.phase == pb.PHASE_DECODE
assert echoed.chunk.bundle.tensors[0].fragments[0].payload == payload
def test_release_is_terminal(worker):
responses = worker.session([_open(), _release()])
assert responses[0].WhichOneof("kind") == "accepted"
assert responses[1].status.terminal is True
# --- deadlines / flow control / bounded messages ---------------------------
def test_expired_deadline_is_rejected(worker):
responses = worker.session([_open(), _chunk("w-late", b"payload", step=1, deadline_unix_nanos=1)])
assert responses[1].status.error.code == pb.ERROR_CODE_DEADLINE_EXCEEDED
def test_flow_control_violation_and_topup(worker):
responses = worker.session(
[
_open(credits_granted=1),
_chunk("w-a", b"payload-a", step=1),
_chunk("w-b", b"payload-b", step=2),
pb.SessionRequest(flow_control=pb.FlowControl(credits_granted=5)),
_chunk("w-c", b"payload-c", step=3),
]
)
assert responses[1].WhichOneof("kind") == "chunk"
assert responses[2].status.error.code == pb.ERROR_CODE_FLOW_CONTROL_VIOLATION
assert responses[2].status.error.retryable is True
assert responses[3].WhichOneof("kind") == "flow_control"
assert responses[3].flow_control.credits_granted >= 5
assert responses[4].WhichOneof("kind") == "chunk"
def test_bounded_message_is_rejected():
"""A tensor whose declared payload exceeds the negotiated ceiling is refused."""
w = _Worker(extra_env={"MESHNET_MAX_CHUNK_BYTES": "64"})
try:
big = b"x" * 128
responses = w.session([_open(), _chunk("w-big", big, step=1, total_bytes=128)])
status = responses[1].status
assert status.error.code == pb.ERROR_CODE_RESOURCE_EXHAUSTED
assert "max_chunk_bytes" in status.error.detail
finally:
if w.proc.poll() is None:
w.close()
def test_malformed_fragment_tiling_is_rejected(worker):
# A fragment at a non-zero offset with no predecessor cannot tile.
tensor = pb.NamedTensor(
name="hidden_states",
shape=[1, 1, 4096],
dtype=pb.DTYPE_BFLOAT16,
byte_order=pb.BYTE_ORDER_LITTLE_ENDIAN,
total_bytes=7,
compression=pb.COMPRESSION_NONE,
checksum=pb.Checksum(algorithm=pb.CHECKSUM_ALGORITHM_CRC32C, value=_crc32c(b"payload")),
fragments=[pb.TensorFragment(fragment_index=0, fragment_count=1, byte_offset=5, payload=b"payload")],
)
bad = pb.SessionRequest(
chunk=pb.ActivationChunk(
envelope=pb.Envelope(
schema_version=pb.SCHEMA_VERSION_1,
work_id="w-gap",
route_session_id="rs-1",
route_epoch=7,
idempotency_step=1,
),
bundle=pb.TensorBundle(bundle_version=1, tensors=[tensor]),
)
)
responses = worker.session([_open(), bad])
assert responses[1].status.error.code == pb.ERROR_CODE_PAYLOAD_CORRUPT
assert "tile" in responses[1].status.error.detail
def test_stale_route_epoch_is_rejected(worker):
responses = worker.session([_open(route_epoch=7), _chunk("w-stale", b"payload", step=1, route_epoch=5)])
assert responses[1].status.error.code == pb.ERROR_CODE_EPOCH_STALE
def test_duplicate_idempotency_step_is_acked(worker):
chunk = _chunk("w-dup", b"payload", step=1)
responses = worker.session([_open(), chunk, chunk])
assert responses[1].WhichOneof("kind") == "chunk"
assert responses[2].WhichOneof("kind") == "ack"
assert responses[2].ack.duplicate is True
# --- cancellation ----------------------------------------------------------
def test_in_band_cancel_of_single_work_item_does_not_end_stream(worker):
responses = worker.session(
[
_open(),
_cancel(work_id="work-x"),
_chunk("work-x", b"payload", step=1),
_chunk("work-y", b"payload", step=2),
_release(),
]
)
assert responses[1].status.error.code == pb.ERROR_CODE_CANCELLED
assert responses[1].status.terminal is False
assert responses[2].status.error.code == pb.ERROR_CODE_CANCELLED
assert responses[3].WhichOneof("kind") == "chunk"
assert responses[4].status.terminal is True
def test_in_band_cancel_of_whole_session_is_terminal(worker):
responses = worker.session([_open(), _cancel(work_id="")])
assert responses[1].status.error.code == pb.ERROR_CODE_CANCELLED
assert responses[1].status.terminal is True
def test_out_of_band_cancel_rpc_races_ahead_of_open(worker):
stub = worker.stub()
resp = stub.Cancel(
pb.CancelRequest(
schema_version=pb.SCHEMA_VERSION_1,
route_session_id="rs-precancel",
route_epoch=1,
work_id="work-precancelled",
reason="operator abort",
)
)
assert resp.cancelled_work_items == 1
responses = worker.session(
[
_open(route_session_id="rs-precancel"),
_chunk("work-precancelled", b"payload", step=1, route_session_id="rs-precancel"),
]
)
assert responses[1].status.error.code == pb.ERROR_CODE_CANCELLED
def test_release_rpc_is_idempotent(worker):
stub = worker.stub()
# Open a session WITHOUT an in-stream release so state persists on the
# servicer, then drop it out of band twice.
worker.session([_open(route_session_id="rs-rel")])
first = stub.Release(pb.ReleaseRequest(schema_version=pb.SCHEMA_VERSION_1, route_session_id="rs-rel", route_epoch=7))
second = stub.Release(pb.ReleaseRequest(schema_version=pb.SCHEMA_VERSION_1, route_session_id="rs-rel", route_epoch=7))
assert first.released is True
assert second.released is False # idempotent: nothing left to drop
def test_independent_session_cancellation(worker):
# Cancel the whole of session A; session B must remain fully serviceable.
a = worker.session([_open(route_session_id="sess-A"), _cancel(route_session_id="sess-A", work_id="")])
assert a[1].status.terminal is True
b = worker.session(
[
_open(route_session_id="sess-B"),
_chunk("work-b", b"payload-b", step=1, route_session_id="sess-B"),
_release(),
]
)
assert b[1].WhichOneof("kind") == "chunk", "cancelling session A must not affect session B"
# --- graceful shutdown -----------------------------------------------------
def test_graceful_shutdown_on_sigterm():
w = _Worker()
# Confirm it is serving, then send SIGTERM and require a clean drain/exit.
assert w.stub().Health(pb.HealthRequest(schema_version=pb.SCHEMA_VERSION_1)).state == pb.SERVING_STATE_SERVING
out = w.close(sig=signal.SIGTERM)
assert w.proc.returncode == 0, f"worker did not exit cleanly on SIGTERM:\n{out}"
assert "shut down cleanly" in out
# --- direct vs opaque relay byte identity ----------------------------------
def test_direct_and_opaque_relay_yield_identical_responses(worker):
"""A direct hop and an opaque relay of the exact captured request bytes must
produce byte-identical server responses (relays carry frames verbatim)."""
payload = b"RELAY_ACTIVATION_BYTES"
requests = [_open(), _chunk("w1", payload, step=1), _release()]
direct_call = worker.channel.stream_stream(
"/meshnet.shard.v1.ShardRuntime/Session",
request_serializer=lambda m: m.SerializeToString(),
response_deserializer=lambda b: b,
)
direct_resp = list(direct_call(iter(requests)))
captured = [m.SerializeToString() for m in requests]
relay_call = worker.channel.stream_stream(
"/meshnet.shard.v1.ShardRuntime/Session",
request_serializer=lambda b: b, # raw captured bytes, no reinterpretation
response_deserializer=lambda b: b,
)
relay_resp = list(relay_call(iter(captured)))
assert len(direct_resp) == len(relay_resp) == 3
for i, (d, r) in enumerate(zip(direct_resp, relay_resp)):
assert d == r, f"response #{i} differs between direct and opaque relay"
# --- fail-closed before SessionOpen ----------------------------------------
def test_chunk_before_open_is_rejected(worker):
# An activation with no preceding SessionOpen must fail closed and end the
# stream: no work may bypass the lifecycle handshake.
responses = worker.session([_chunk("w-noopen", b"payload", step=1)])
assert len(responses) == 1
assert responses[0].WhichOneof("kind") == "status"
assert responses[0].status.error.code == pb.ERROR_CODE_INTERNAL
assert responses[0].status.terminal is True
assert "SessionOpen" in responses[0].status.error.detail
def test_decode_before_open_is_rejected(worker):
responses = worker.session([_decode("w-noopen", b"payload", step=1, position=0)])
assert len(responses) == 1
assert responses[0].WhichOneof("kind") == "status"
assert responses[0].status.error.code == pb.ERROR_CODE_INTERNAL
assert responses[0].status.terminal is True
# --- flow-control negotiation with strict worker bounds --------------------
def test_flow_control_proposal_is_clamped_to_worker_bounds(worker):
# A peer proposing a window far above the worker limits must be clamped to
# the worker own ceilings, never granted the inflated proposal.
responses = worker.session(
[_open(credits_granted=9999, max_inflight_chunks=9999, max_chunk_bytes=1073741824)]
)
fc = responses[0].accepted.flow_control
assert fc.max_inflight_chunks == 16
assert fc.credits_granted == 16
assert fc.max_chunk_bytes == 4 * 1024 * 1024
def test_negotiated_max_chunk_bytes_caps_peer_proposal():
# Worker ceiling is 64 bytes; the peer proposes 4 MiB. The negotiated per
# session ceiling is the stricter 64, so a 128-byte tensor is refused even
# though the peer allowed it — the worker never adopts the peer proposal.
w = _Worker(extra_env={"MESHNET_MAX_CHUNK_BYTES": "64"})
try:
big = b"x" * 128
responses = w.session(
[_open(max_chunk_bytes=4 * 1024 * 1024), _chunk("w-big", big, step=1, total_bytes=128)]
)
assert responses[0].accepted.flow_control.max_chunk_bytes == 64
assert responses[1].status.error.code == pb.ERROR_CODE_RESOURCE_EXHAUSTED
assert "max_chunk_bytes" in responses[1].status.error.detail
finally:
if w.proc.poll() is None:
w.close()
# --- in-stream release erases session state --------------------------------
def test_in_stream_release_erases_session_state(worker):
stub = worker.stub()
resp = worker.session(
[
_open(route_session_id="rs-erase"),
pb.SessionRequest(
release=pb.ReleaseSignal(route_session_id="rs-erase", route_epoch=7, work_id="w-final")
),
]
)
assert resp[-1].status.terminal is True
# The state is already gone: an out-of-band Release finds nothing to drop.
after = stub.Release(
pb.ReleaseRequest(schema_version=pb.SCHEMA_VERSION_1, route_session_id="rs-erase", route_epoch=7)
)
assert after.released is False
# --- SessionOpen identity validation ---------------------------------------
def test_incompatible_schema_is_rejected_at_open(worker):
responses = worker.session([_open(schema_version=pb.SCHEMA_VERSION_UNSPECIFIED)])
assert responses[0].WhichOneof("kind") == "status"
assert responses[0].status.error.code == pb.ERROR_CODE_SCHEMA_UNSUPPORTED
assert responses[0].status.terminal is True
def test_incompatible_fingerprint_is_rejected_at_open(worker):
bad_fp = pb.Fingerprint(
model_artifact_digest="sha256:some-other-model",
runtime_recipe_digest="sha256:native-test-recipe",
recipe_id="native-test",
recipe_version="1",
catalogue_version="1",
)
responses = worker.session([_open(fingerprint=bad_fp)])
assert responses[0].WhichOneof("kind") == "status"
assert responses[0].status.error.code == pb.ERROR_CODE_FINGERPRINT_MISMATCH
assert responses[0].status.terminal is True
def test_shard_range_mismatch_is_rejected_at_open(worker):
responses = worker.session(
[_open(shard_range=pb.ShardRange(start_layer=0, end_layer=64, effective_start_layer=0))]
)
assert responses[0].WhichOneof("kind") == "status"
assert responses[0].status.error.code == pb.ERROR_CODE_SHARD_RANGE_MISMATCH
assert responses[0].status.terminal is True
def test_session_accepted_reports_worker_fingerprint_not_caller(worker):
# The caller asserts no fingerprint; SessionAccepted must carry the worker
# OWN served identity, not a copy of the caller (empty) fingerprint.
responses = worker.session([_open(fingerprint=pb.Fingerprint())])
assert responses[0].WhichOneof("kind") == "accepted"
accepted = responses[0].accepted
assert accepted.fingerprint.model_artifact_digest == "sha256:native-test-artifact"
assert accepted.fingerprint.runtime_recipe_digest == "sha256:native-test-recipe"

273
tests/test_range_report.py Normal file
View File

@@ -0,0 +1,273 @@
"""DGR-034: strict consumption of owned-range reports from loaded engine state.
The ``meshnet-range-report`` native tool loads one dense-Llama GGUF through
the Meshnet owned-range loader and prints a JSON document derived from the
loaded model state. ``meshnet_node.range_report`` is the strict consumer:
it must accept exactly the documents that encode the dense-Llama ownership
contract and fail closed on everything else — invalid, empty, or
out-of-model ranges, endpoint registrations that disagree with the loaded
state, gapped or unexpected tensor registrations, and inconsistent byte
counts.
"""
from __future__ import annotations
from typing import Any
import pytest
from meshnet_node.range_report import (
OwnedRangeReport,
RangeReportError,
parse_owned_range_report,
)
N_LAYER = 40
LAYER_BYTES = 300 * 2**20
EMBD_BYTES = 360 * 2**20
OUT_BYTES = 525 * 2**20
FILE_BYTES = 13669 * 2**20
def _doc(**overrides: Any) -> dict[str, Any]:
"""A valid middle-range [10, 20) mmap report the consumer must accept."""
doc: dict[str, Any] = {
"ok": True,
"model": "/models/dense.gguf",
"architecture": "llama",
"n_layer": N_LAYER,
"file_bytes": FILE_BYTES,
"requested_range": [10, 20],
"reported_range": [10, 20],
"mmap": True,
"touched": False,
"use_extra_bufts": True,
"has_token_embeddings": False,
"has_output_head": False,
"tied_output_head": False,
"mapped_bytes": 10 * LAYER_BYTES,
"resident_bytes": 10 * LAYER_BYTES,
"registered_tensors": 90,
"registered_bytes": 10 * LAYER_BYTES,
"unexpected_registered_tensors": [],
"missing_owned_layers": [],
"vm_size_bytes": FILE_BYTES + 2**28,
"vm_rss_bytes": 2**28,
"vm_hwm_bytes": 2**28,
}
doc.update(overrides)
return doc
def _head_doc(**overrides: Any) -> dict[str, Any]:
base = _doc(
requested_range=[0, 10],
reported_range=[0, 10],
has_token_embeddings=True,
mapped_bytes=10 * LAYER_BYTES + EMBD_BYTES,
resident_bytes=10 * LAYER_BYTES + EMBD_BYTES,
registered_tensors=91,
registered_bytes=10 * LAYER_BYTES + EMBD_BYTES,
)
base.update(overrides)
return base
def _tail_doc(**overrides: Any) -> dict[str, Any]:
base = _doc(
requested_range=[30, 40],
reported_range=[30, 40],
has_output_head=True,
mapped_bytes=10 * LAYER_BYTES + OUT_BYTES,
resident_bytes=10 * LAYER_BYTES + OUT_BYTES,
registered_tensors=92,
registered_bytes=10 * LAYER_BYTES + OUT_BYTES,
)
base.update(overrides)
return base
class TestAcceptance:
def test_middle_range_registers_only_per_layer_tensors(self) -> None:
report = parse_owned_range_report(_doc())
assert (report.start_layer, report.end_layer) == (10, 20)
assert not report.is_head and not report.is_tail
assert not report.has_token_embeddings and not report.has_output_head
def test_head_range_owns_embeddings_only_at_the_head(self) -> None:
report = parse_owned_range_report(_head_doc())
assert report.is_head and not report.is_tail
assert report.has_token_embeddings and not report.has_output_head
def test_tail_range_owns_norm_and_output_only_at_the_tail(self) -> None:
report = parse_owned_range_report(_tail_doc())
assert report.is_tail and not report.is_head
assert report.has_output_head and not report.has_token_embeddings
def test_whole_model_range_owns_both_endpoints(self) -> None:
report = parse_owned_range_report(
_head_doc(
requested_range=[0, 40],
reported_range=[0, 40],
has_output_head=True,
mapped_bytes=FILE_BYTES,
resident_bytes=FILE_BYTES,
registered_tensors=363,
registered_bytes=N_LAYER * LAYER_BYTES + EMBD_BYTES + OUT_BYTES,
)
)
assert report.is_head and report.is_tail
assert report.has_token_embeddings and report.has_output_head
def test_tied_output_tail_registers_the_embedding_as_its_output_head(self) -> None:
report = parse_owned_range_report(
_tail_doc(
has_token_embeddings=True,
tied_output_head=True,
registered_tensors=91,
registered_bytes=10 * LAYER_BYTES + EMBD_BYTES,
mapped_bytes=10 * LAYER_BYTES + EMBD_BYTES,
resident_bytes=10 * LAYER_BYTES + EMBD_BYTES,
)
)
assert report.tied_output_head and report.has_output_head
def test_non_mmap_load_reports_resident_allocation_only(self) -> None:
report = parse_owned_range_report(
_doc(mmap=False, mapped_bytes=0, resident_bytes=10 * LAYER_BYTES)
)
assert report.mapped_bytes == 0
assert report.resident_bytes == 10 * LAYER_BYTES
def test_process_counters_may_be_absent_off_linux(self) -> None:
report = parse_owned_range_report(
_doc(vm_size_bytes=None, vm_rss_bytes=None, vm_hwm_bytes=None)
)
assert report.vm_hwm_bytes is None
class TestRangeRejection:
def test_rejected_load_fails_closed_with_the_tool_error(self) -> None:
with pytest.raises(RangeReportError, match="dense Llama only"):
parse_owned_range_report(
{"ok": False, "error": "owned-range load rejected the artifact or range: dense Llama only"}
)
def test_reported_range_must_match_the_requested_range(self) -> None:
with pytest.raises(RangeReportError, match="loaded engine state"):
parse_owned_range_report(_doc(reported_range=[10, 21]))
def test_out_of_model_range_is_rejected(self) -> None:
with pytest.raises(RangeReportError, match="outside the model"):
parse_owned_range_report(
_doc(requested_range=[30, 41], reported_range=[30, 41], has_output_head=True)
)
def test_empty_range_is_rejected(self) -> None:
with pytest.raises(RangeReportError, match="empty or"):
parse_owned_range_report(_doc(requested_range=[10, 10], reported_range=[10, 10]))
def test_inverted_range_is_rejected(self) -> None:
with pytest.raises(RangeReportError, match="empty or"):
parse_owned_range_report(_doc(requested_range=[20, 10], reported_range=[20, 10]))
def test_boolean_range_bounds_are_rejected(self) -> None:
with pytest.raises(RangeReportError, match="integer pair"):
parse_owned_range_report(_doc(reported_range=[True, 20]))
class TestEndpointRejection:
def test_embeddings_registered_below_the_head_are_rejected(self) -> None:
with pytest.raises(RangeReportError, match="embeddings belong to the head"):
parse_owned_range_report(_doc(has_token_embeddings=True))
def test_output_head_registered_above_the_tail_is_rejected(self) -> None:
with pytest.raises(RangeReportError, match="output head belong to the tail"):
parse_owned_range_report(_tail_doc(requested_range=[20, 30], reported_range=[20, 30]))
def test_tail_without_an_output_head_is_rejected(self) -> None:
with pytest.raises(RangeReportError, match="output head belong to the tail"):
parse_owned_range_report(_tail_doc(has_output_head=False))
def test_tied_output_below_the_tail_is_rejected(self) -> None:
with pytest.raises(RangeReportError, match="only belong to the tail"):
parse_owned_range_report(_doc(tied_output_head=True))
def test_unexpected_registered_tensors_are_rejected(self) -> None:
with pytest.raises(RangeReportError, match="unexpected_registered_tensors"):
parse_owned_range_report(
_doc(unexpected_registered_tensors=["blk.10.attn_q.weight.extra"])
)
def test_missing_owned_layers_are_rejected_as_gaps(self) -> None:
with pytest.raises(RangeReportError, match="missing_owned_layers"):
parse_owned_range_report(_doc(missing_owned_layers=[12]))
class TestByteCountRejection:
def test_mapped_span_must_cover_the_registered_tensors(self) -> None:
with pytest.raises(RangeReportError, match="undercounts"):
parse_owned_range_report(_doc(mapped_bytes=LAYER_BYTES))
def test_mapped_span_must_not_exceed_the_artifact(self) -> None:
with pytest.raises(RangeReportError, match="exceeds the artifact"):
parse_owned_range_report(
_tail_doc(mapped_bytes=FILE_BYTES + 1, resident_bytes=FILE_BYTES + 1)
)
def test_non_mmap_load_must_not_claim_a_mapped_span(self) -> None:
with pytest.raises(RangeReportError, match="must not claim"):
parse_owned_range_report(_doc(mmap=False, mapped_bytes=LAYER_BYTES))
def test_resident_allocation_must_cover_the_registered_tensors(self) -> None:
with pytest.raises(RangeReportError, match="undercounts"):
parse_owned_range_report(
_doc(mmap=False, mapped_bytes=0, resident_bytes=LAYER_BYTES)
)
def test_an_empty_registration_is_rejected(self) -> None:
with pytest.raises(RangeReportError, match="no tensors"):
parse_owned_range_report(_doc(registered_tensors=0, registered_bytes=0))
class TestSchemaRejection:
def test_wrong_architecture_is_rejected(self) -> None:
with pytest.raises(RangeReportError, match="dense Llama only"):
parse_owned_range_report(_doc(architecture="qwen2"))
def test_missing_field_is_rejected(self) -> None:
doc = _doc()
del doc["mapped_bytes"]
with pytest.raises(RangeReportError, match="missing field"):
parse_owned_range_report(doc)
def test_boolean_bytes_are_rejected(self) -> None:
with pytest.raises(RangeReportError, match="non-negative integer"):
parse_owned_range_report(_doc(mapped_bytes=True))
def test_non_mapping_document_is_rejected(self) -> None:
with pytest.raises(RangeReportError, match="JSON object"):
parse_owned_range_report(["not", "a", "report"]) # type: ignore[arg-type]
def test_owned_range_report_rejects_direct_construction_outside_the_contract() -> None:
with pytest.raises(RangeReportError, match="dense Llama only"):
OwnedRangeReport(
architecture="qwen2",
n_layer=N_LAYER,
start_layer=10,
end_layer=20,
has_token_embeddings=False,
has_output_head=False,
tied_output_head=False,
mapped_bytes=10 * LAYER_BYTES,
resident_bytes=10 * LAYER_BYTES,
registered_tensors=90,
registered_bytes=10 * LAYER_BYTES,
file_bytes=FILE_BYTES,
mmap=True,
touched=False,
vm_size_bytes=None,
vm_rss_bytes=None,
vm_hwm_bytes=None,
)

241
tests/test_shard_engine.py Normal file
View File

@@ -0,0 +1,241 @@
"""DGR-031 ``ShardEngine`` contract tests.
``_ReferenceEngine`` below is a minimal, in-memory ``ShardEngine`` that exists
only to prove :func:`assert_shard_engine_contract` is non-vacuous and to pin
the abstract contract's own validation rules. It is deliberately not the
DGR-032 deterministic fixture (delay/memory-pressure/malformed/crash
injection, full session/epoch modeling for the fake worker) — that is a
separate, larger story. DGR-032 and DGR-037 are expected to import
``assert_shard_engine_contract`` from ``tests/shard_engine_contract.py``
against their own engines.
"""
from __future__ import annotations
import hashlib
import pytest
from meshnet_node.shard_engine import (
ArchitectureAuxStateHook,
BoundaryBundle,
DecodeRequest,
EngineCapabilities,
EngineTensor,
HealthResult,
LoadRequest,
LoadResult,
MetricsResult,
MtpHook,
PrefillRequest,
ShardEngine,
StepResult,
TokenOutput,
)
from meshnet_node.shard_lifecycle import CacheResult, StatusCode, StructuredStatus
from shard_engine_contract import assert_shard_engine_contract
class _ReferenceEngine(ShardEngine):
"""Minimal in-memory engine used only to exercise the shared contract."""
def __init__(self) -> None:
self._loaded: LoadRequest | None = None
self._sessions: dict[str, dict] = {}
self._cancelled_total = 0
def load(self, request: LoadRequest) -> LoadResult:
self._loaded = request
return LoadResult(
status=StructuredStatus(StatusCode.OK, "loaded"),
effective_start=request.shard_start,
architecture="dense",
)
def capabilities(self) -> EngineCapabilities:
if self._loaded is None:
return EngineCapabilities(status=StructuredStatus(StatusCode.FAILED_PRECONDITION, "not loaded"))
request = self._loaded
return EngineCapabilities(
status=StructuredStatus(StatusCode.OK, "ready"),
shard_start=request.shard_start,
shard_end=request.shard_end,
effective_start=request.shard_start,
total_layers=request.total_layers,
architecture="dense",
max_concurrent_sessions=8,
max_context_tokens=131072,
supports_mtp=False,
)
def prefill(self, request: PrefillRequest) -> StepResult:
if self._loaded is None:
return StepResult(status=StructuredStatus(StatusCode.FAILED_PRECONDITION, "engine not loaded"))
self._sessions[request.session_id] = {"epoch": request.route_epoch, "cancelled": False}
output = self._transform(self._seed_bytes(request.token_ids, request.input), request.idempotency_step)
return StepResult(status=StructuredStatus(StatusCode.OK, "prefilled"), cache_result=CacheResult.STORED, output=output)
def decode(self, request: DecodeRequest) -> StepResult:
session = self._sessions.get(request.session_id)
if session is None:
return StepResult(
status=StructuredStatus(StatusCode.NOT_FOUND, "no cached session state"),
cache_result=CacheResult.MISS,
)
if session["cancelled"]:
return StepResult(status=StructuredStatus(StatusCode.CANCELLED, "session cancelled"))
if request.route_epoch < session["epoch"]:
return StepResult(status=StructuredStatus(StatusCode.FAILED_PRECONDITION, "stale route epoch"))
session["epoch"] = request.route_epoch
token_ids = (request.token_id,) if request.token_id is not None else None
output = self._transform(self._seed_bytes(token_ids, request.input), request.idempotency_step)
return StepResult(status=StructuredStatus(StatusCode.OK, "decoded"), cache_result=CacheResult.HIT, output=output)
def cancel(self, session_id: str, *, work_id: str = "", reason: str = "") -> StructuredStatus:
session = self._sessions.setdefault(session_id, {"epoch": 0, "cancelled": False})
if not session["cancelled"]:
self._cancelled_total += 1
session["cancelled"] = True
return StructuredStatus(StatusCode.CANCELLED, reason or "cancelled")
def release(self, session_id: str) -> StructuredStatus:
self._sessions.pop(session_id, None)
return StructuredStatus(StatusCode.OK, "released")
def health(self) -> HealthResult:
return HealthResult(
status=StructuredStatus(StatusCode.OK, "ok"),
serving=self._loaded is not None,
state="SERVING" if self._loaded is not None else "NOT_LOADED",
active_sessions=len(self._sessions),
)
def metrics(self) -> MetricsResult:
return MetricsResult(
status=StructuredStatus(StatusCode.OK, "ok"),
active_sessions=len(self._sessions),
cancelled_sessions=self._cancelled_total,
)
@staticmethod
def _seed_bytes(token_ids, bundle: BoundaryBundle | None) -> bytes:
if token_ids:
return b"".join(int(t).to_bytes(4, "big") for t in token_ids)
if bundle is not None:
return b"".join(tensor.data for tensor in bundle.tensors)
return b""
def _transform(self, seed: bytes, idempotency_step: int) -> BoundaryBundle | TokenOutput:
digest = hashlib.sha256(seed + idempotency_step.to_bytes(4, "big")).digest()
assert self._loaded is not None
if self._loaded.shard_end >= self._loaded.total_layers - 1:
token_id = int.from_bytes(digest[:4], "big") % 50_000
return TokenOutput(token_id=token_id)
tensor = EngineTensor(name="hidden_states", shape=(1, max(len(seed) // 4, 1)), dtype="bfloat16", data=digest)
return BoundaryBundle(tensors=(tensor,), architecture="dense", boundary_point="pre_tail_residual")
def test_reference_engine_obeys_the_shared_shard_engine_contract():
assert_shard_engine_contract(_ReferenceEngine)
def test_shard_engine_is_abstract_and_cannot_be_instantiated_directly():
with pytest.raises(TypeError):
ShardEngine() # type: ignore[abstract]
def test_engine_tensor_rejects_empty_name_shape_or_dtype():
with pytest.raises(ValueError):
EngineTensor(name="", shape=(1,), dtype="bfloat16", data=b"x")
with pytest.raises(ValueError):
EngineTensor(name="t", shape=(), dtype="bfloat16", data=b"x")
with pytest.raises(ValueError):
EngineTensor(name="t", shape=(0,), dtype="bfloat16", data=b"x")
with pytest.raises(ValueError):
EngineTensor(name="t", shape=(1,), dtype="", data=b"x")
def test_boundary_bundle_requires_at_least_one_tensor():
with pytest.raises(ValueError):
BoundaryBundle(tensors=(), architecture="dense", boundary_point="pre_tail_residual")
def test_boundary_bundle_tensor_lookup_by_name():
tensor = EngineTensor(name="hidden_states", shape=(1, 1), dtype="bfloat16", data=b"\x00\x00")
bundle = BoundaryBundle(tensors=(tensor,), architecture="dense", boundary_point="pre_tail_residual")
assert bundle.tensor("hidden_states") is tensor
with pytest.raises(KeyError):
bundle.tensor("router_logits")
def test_token_output_rejects_negative_token_id():
with pytest.raises(ValueError):
TokenOutput(token_id=-1)
def test_mtp_hook_is_reserved_and_refuses_to_enable():
MtpHook() # disabled is fine
with pytest.raises(ValueError):
MtpHook(enabled=True)
with pytest.raises(ValueError):
MtpHook(draft_token_count=-1)
def test_architecture_aux_state_hook_carries_opaque_shard_local_state():
hook = ArchitectureAuxStateHook(kind="csa", state={"window": 128})
assert hook.kind == "csa"
assert hook.state == {"window": 128}
def test_prefill_and_decode_requests_require_exactly_one_input_kind():
with pytest.raises(ValueError):
PrefillRequest(session_id="s", route_epoch=0, position=0, idempotency_step=0)
with pytest.raises(ValueError):
PrefillRequest(
session_id="s",
route_epoch=0,
position=0,
idempotency_step=0,
token_ids=(1,),
input=BoundaryBundle(
tensors=(EngineTensor(name="hidden_states", shape=(1,), dtype="bfloat16", data=b"x"),),
architecture="dense",
boundary_point="pre_tail_residual",
),
)
with pytest.raises(ValueError):
DecodeRequest(session_id="s", route_epoch=0, position=0, idempotency_step=0)
def test_load_request_validates_shard_range_against_total_layers():
LoadRequest(artifact_path="a", shard_start=0, shard_end=3, total_layers=4)
with pytest.raises(ValueError):
LoadRequest(artifact_path="a", shard_start=0, shard_end=4, total_layers=4)
with pytest.raises(ValueError):
LoadRequest(artifact_path="", shard_start=0, shard_end=0, total_layers=1)
with pytest.raises(ValueError):
LoadRequest(artifact_path="a", shard_start=3, shard_end=1, total_layers=4)
def test_step_result_requires_an_output_when_status_is_ok():
with pytest.raises(ValueError):
StepResult(status=StructuredStatus(StatusCode.OK, "ok"), output=None)
# A non-OK status is allowed to carry no output.
StepResult(status=StructuredStatus(StatusCode.NOT_FOUND, "missing"), output=None)
def test_shard_engine_module_imports_no_native_or_grpc_or_wire_abi_types():
import meshnet_node.shard_engine as shard_engine_module
# The boundary module must not *import* anything that would let a
# ggml_tensor, llama context/scheduler handle, ctypes native handle, or a
# generated-protobuf (ABI) message leak into a project-owned dataclass
# field. Checking bound globals (not docstring prose) proves this
# structurally rather than by convention.
forbidden_modules = {"ctypes", "grpc", "meshnet_node.native_protocol"}
for name, value in vars(shard_engine_module).items():
module_name = getattr(value, "__name__", None)
assert module_name not in forbidden_modules, (
f"shard_engine.{name} binds forbidden module {module_name!r}"
)