1-card target: ThinkingCap-27B on ONE P150a (TP=1) - context ladder, accuracy, bf4 coverage, batching #69

Open
opened 2026-09-25 10:06:18 +02:00 by Grok · 3 comments
Owner

Target (owner, 2026-09-25): 1 card and 2 cards. This issue tracks the 1-card target (TP=1, one P150a). It carries forward what PR #15 (goal 4) left open. The PR is closed because its content is merged and its base branch is dead.

Measured so far (09-05 tree, QUASAR NVFP4, goal4-single-card-nvfp4-2026-09-05.jsonl)

  • G4.1 MET: the 27B serves from one card, coherent output.
  • G4.2 MET: device weights are 19.76 GiB, leaving 11.1 GiB free of 30.87 GiB usable.
  • B=1 decode is 144.1 ms/token, with max_num_seqs=1, bf16 KV and an 8k context.

Open, carried from #15

  • G4.3 context ladder. Never measured. The budget arithmetic (an estimate) says ~163k tokens at bf16 KV and ~307k at bf8 KV once the TP=1 bf8 typecast is fixed.
  • G4.4 accuracy. Never measured. Gate it against the native FP8 teacher (the bfp-quant P1 harness, bfp-quant:bench/runs/bfp-quant-p1-20260925T0645Z.jsonl).
  • Step D, QUASAR bf4 coverage. attn/GDN at bf4 is now measured non-inferior at TP=2 (GPTQ G2, #62). The natural 1-card path is G2 weights on one card.

Reopened by the 2026-09-25 rule (a closure needs an on-hardware record at its scope)

Each of these rests on a code read or a single observed failure, so each is engineering work, not a limit.

  • bf8 KV on TP=1: the single-device attention lacks the prefill typecast and dies in paged_fill_cache.
  • Batch >1 on one card: our own asserts in model.py block it. The 09-05 run used max_num_seqs=1 by construction.
  • TPAttention / CCL asserts at num_devices == 1: this needs a degenerate-TP bypass.
  • TP=1 MTP (#63b): prefill_for_spec, verify_forward and verify_traced assert num_devices > 1.
  • Grid tuning and the gather mode at TP=1: the TP=2 closures do not cover TP=1.

Enablers already measured

  • Two single-card processes coexist, one per card (mtp-thinkingcap:bench/runs/two-card-coexistence-20260925T0625Z.jsonl). Paired A/Bs can run one arm per card. Two full 27B loads at once are not yet shown to be safe for host RAM.
  • Card 0 is PCIe x4 and card 1 is x16; see docs/HARDWARE.md.
  • Ornith-1.5-35B-A3B (PR #16, closed) is a candidate single-P150 model. Its 32.5 tok/s is third-party.

The NVFP4 loader patches 0009/0010 were deleted as dead. Recover them with git show 682dd38:patches/tt-metal/0009-qwen36-nvfp4-compressed-tensors-loader.patch and …/0010-qwen36-single-device-lazy-state-dict.patch.

The scoreboard row is docs/STATUS.md, TP=1.

Target (owner, 2026-09-25): **1 card and 2 cards**. This issue tracks the 1-card target (TP=1, one P150a). It carries forward what PR #15 (goal 4) left open. The PR is closed because its content is merged and its base branch is dead. ## Measured so far (09-05 tree, QUASAR NVFP4, `goal4-single-card-nvfp4-2026-09-05.jsonl`) - G4.1 MET: the 27B serves from one card, coherent output. - G4.2 MET: device weights are **19.76 GiB**, leaving 11.1 GiB free of 30.87 GiB usable. - B=1 decode is **144.1 ms/token**, with max_num_seqs=1, bf16 KV and an 8k context. ## Open, carried from #15 - **G4.3 context ladder.** Never measured. The budget arithmetic (an **estimate**) says ~163k tokens at bf16 KV and ~307k at bf8 KV once the TP=1 bf8 typecast is fixed. - **G4.4 accuracy.** Never measured. Gate it against the native FP8 teacher (the bfp-quant P1 harness, `bfp-quant:bench/runs/bfp-quant-p1-20260925T0645Z.jsonl`). - **Step D, QUASAR bf4 coverage.** attn/GDN at bf4 is now measured non-inferior at TP=2 (GPTQ G2, #62). The natural 1-card path is G2 weights on one card. ## Reopened by the 2026-09-25 rule (a closure needs an on-hardware record at its scope) Each of these rests on a code read or a single observed failure, so each is engineering work, not a limit. - **bf8 KV on TP=1:** the single-device attention lacks the prefill typecast and dies in `paged_fill_cache`. - **Batch >1 on one card:** our own asserts in `model.py` block it. The 09-05 run used max_num_seqs=1 by construction. - **TPAttention / CCL asserts at `num_devices == 1`:** this needs a degenerate-TP bypass. - **TP=1 MTP (#63b):** `prefill_for_spec`, `verify_forward` and `verify_traced` assert `num_devices > 1`. - **Grid tuning and the gather mode at TP=1:** the TP=2 closures do not cover TP=1. ## Enablers already measured - Two single-card processes coexist, one per card (`mtp-thinkingcap:bench/runs/two-card-coexistence-20260925T0625Z.jsonl`). Paired A/Bs can run one arm per card. Two full 27B loads at once are not yet shown to be safe for host RAM. - Card 0 is PCIe x4 and card 1 is x16; see docs/HARDWARE.md. - Ornith-1.5-35B-A3B (PR #16, closed) is a candidate single-P150 model. Its 32.5 tok/s is **third-party**. The NVFP4 loader patches 0009/0010 were deleted as dead. Recover them with `git show 682dd38:patches/tt-metal/0009-qwen36-nvfp4-compressed-tensors-loader.patch` and `…/0010-qwen36-single-device-lazy-state-dict.patch`. The scoreboard row is docs/STATUS.md, TP=1.
Author
Owner

TP=1 MTP: interim status (2026-09-25 10:10Z). No new speed number in this update.

"Decode on one card is broken": there is no decode failure on record yet. No TP=1 run has reached decode.

  • The ttx g2-tp1-probe runs (cfx-llm2:/var/lib/models/ttx-runs/ttx-g2-tp1-probe-smoke-20260925T083748Z and the ttx-ab-...-20260925T090800Z-r{0,1}-{A,B} runs) are load-only. Their phases are imports, open, ttnn_matmul, model_load, hold and done, and every one ends ok: true. They show that G2 loads on one card: 20.96 GB of 33.1 GB DRAM, VmHWM 5.6–5.8 GB, and 32–34 MB RSS after load. They never prefill or decode.
  • My own TP=1 attempts died in the first prefill chunk, before decode:
    • attempt 1: a GDN qkvzab 2D matmul needed 3.32 MB of static CBs;
    • attempt 2: a GDN nlp_concat_heads over 48 heads needed 1.68 MB.
    • Both are fixed and committed (TP1_L1_FIT: blocked prefill outputs and grouped concat-heads), and neither affects TP=2. See bench/runs/mtp-tp1-attempts-20260925.jsonl.
  • The next smoke (warmups W0–W3 plus one K3 spec arm) is the first run that can reach decode. It is queued on card 1 under a ttx per-card slot (bench/mtp/tp1-card.sh). It is waiting for MemAvailable to reach 4 GB: tt-quant's GPTQ on card 0 holds 2 GB RSS, and a TP=1 load peaks at ~5.8 GB. If decode fails there, I will report the op, file and line.

TP=2 byte-identity with the TP=1 code: whole-chunk prompts are unchanged; the tail-chunk difference is pre-existing (bench/runs/mtp-tp1-tp2identity-20260925.jsonl).

  • Every 4096-token prompt is token-identical in all six runs, and code logits are bit-identical.
  • agentic_edit (2048 + a 47-token tail chunk) repeats exactly when code and harness are identical, but changes with any change to the process:
Overlay Harness Output
old old 8dac
new new or old cb11
old new a61a
  • The harness change adds no device compute op. So the TP=1 code does not cause the difference: the eager partial-chunk prefill is sensitive to perturbation. The suspect is that the last-tile pad rows 47–63 reach the GDN state unmasked. It is not localised yet.
  • Consequence: the TP=1 vs TP=2 identity gate uses the near-tie rule on every prompt, and bit-exactness is expected only on whole-chunk prompts.

Docs moved to the new layout: LEDGER R-2026-09-25-mtp-g2 and D-2026-09-25-mtp-tp1, MTP.md and STATUS.md.

**TP=1 MTP: interim status (2026-09-25 10:10Z).** No new speed number in this update. **"Decode on one card is broken": there is no decode failure on record yet.** No TP=1 run has reached decode. - The ttx `g2-tp1-probe` runs (cfx-llm2:/var/lib/models/ttx-runs/ttx-g2-tp1-probe-smoke-20260925T083748Z and the `ttx-ab-...-20260925T090800Z-r{0,1}-{A,B}` runs) are **load-only**. Their phases are imports, open, ttnn_matmul, model_load, hold and done, and every one ends `ok: true`. They show that G2 loads on one card: 20.96 GB of 33.1 GB DRAM, VmHWM 5.6–5.8 GB, and 32–34 MB RSS after load. They never prefill or decode. - My own TP=1 attempts died in the **first prefill chunk**, before decode: - attempt 1: a GDN qkvzab 2D matmul needed 3.32 MB of static CBs; - attempt 2: a GDN `nlp_concat_heads` over 48 heads needed 1.68 MB. - Both are fixed and committed (`TP1_L1_FIT`: blocked prefill outputs and grouped concat-heads), and neither affects TP=2. See `bench/runs/mtp-tp1-attempts-20260925.jsonl`. - The next smoke (warmups W0–W3 plus one K3 spec arm) is the first run that can reach decode. It is queued on **card 1 under a ttx per-card slot** (`bench/mtp/tp1-card.sh`). It is waiting for MemAvailable to reach 4 GB: tt-quant's GPTQ on card 0 holds 2 GB RSS, and a TP=1 load peaks at ~5.8 GB. If decode fails there, I will report the op, file and line. **TP=2 byte-identity with the TP=1 code: whole-chunk prompts are unchanged; the tail-chunk difference is pre-existing** (`bench/runs/mtp-tp1-tp2identity-20260925.jsonl`). - Every 4096-token prompt is token-identical in all six runs, and code logits are bit-identical. - agentic_edit (2048 + a 47-token tail chunk) repeats exactly when code and harness are identical, but changes with any change to the process: | Overlay | Harness | Output | |---|---|---| | old | old | 8dac | | new | new or old | cb11 | | old | new | a61a | - The harness change adds no device compute op. So the TP=1 code does not cause the difference: the eager partial-chunk prefill is sensitive to perturbation. The suspect is that the last-tile pad rows 47–63 reach the GDN state unmasked. It is not localised yet. - Consequence: the TP=1 vs TP=2 identity gate uses the near-tie rule on every prompt, and bit-exactness is expected only on whole-chunk prompts. Docs moved to the new layout: LEDGER `R-2026-09-25-mtp-g2` and `D-2026-09-25-mtp-tp1`, MTP.md and STATUS.md.
Author
Owner

TP=1 decode on one card now runs: G2 plain and MTP K3/K7 on a single P150a (card 1, ttx per-card slot). Label: TP=1 iteration, not production. Record: bench/runs/mtp-tp1-smoke-20260925T1310Z.jsonl.

Why decode looked broken. No TP=1 run had ever reached decode:

  • The ttx g2-tp1-probe runs are load-only.
  • Every earlier attempt died in the first prefill chunk, before decode.

The root cause of the last failure (attempt 3, 13:08Z):

  • Op: ttnn.experimental.nlp_concat_heads.
  • Call path: tp_common.py:74 concat_heads_fit, called from gdn/tp.py:761 forward_prefill (fused out: rms_norm, then concat heads), called from model.py:1798 _forward_prefill_chunk_tp, during the warmup prefill trace capture.
  • Error: TT_THROW dataflow_buffer.cpp:2617 "static dataflow buffers ... clash with L1 buffers ... L1 buffer at 860160, CB region ends at 898048".
  • Why it happens at TP=1: the head-major GDN norm output (48 heads at TP=1, 24 at TP=2) was held in L1. Every prefill activation the TP code keeps in L1 was tuned at half the TP=1 width.

Fix (commit on mtp-thinkingcap). tp_common.pf_l1() returns DRAM under TP1_L1_FIT (set only at one device) and L1 otherwise. It is applied at every short-lived prefill L1 site:

  • attention: qkv3, the q/kv slices, create-heads, the q/k norm, the gate sigmoid, the wo prefill output;
  • GDN: the FIR conv output, the fused-out norm and concat.

At TP=2 every call returns what it did before. With prefill fixed, decode ran first time, with no decode-side change.

Smoke numbers (B=1, 4096-token prompts, G2, 64 engaged / 1 exempt banners):

Arm ms/token TTFT s Committed per iteration
plain (warmup, 20 tokens) ~80.9 2.9 –
K3 spec, code (timed, 256 tokens) 38.4 4.8 3.92
TP=2 G2, same K3 code arm 27.1 3.7 3.98
  • The K3 iteration breaks down as draft 34.6 + verify 102.7 + reseed 13.1 = 151 ms.
  • DRAM peaks at 24.4 of 33.1 GB.
  • Warm load takes 165 s. Host VmHWM reaches 9.45 GB during load and falls to 48 MB after, so two TP=1 loads have to be staggered.
  • No timed TP=1 plain TG or PP number yet. The 80.9 ms is a 20-token warmup.

Next: gate (c), two TP=1 containers at once (card 0 on the s2a arms, card 1 on the s2b arms, staggered, with watchdog and mem.tsv). That run also supplies the 20-prompt data for gate (a), identity plus logit PCC against TP=2, and gate (b), K3/K7 acceptance. After that comes the solo timed s1 run for TP=1 PP and TG. The pair is queued behind tt-quant's mesh run.

**TP=1 decode on one card now runs: G2 plain and MTP K3/K7 on a single P150a** (card 1, ttx per-card slot). Label: **TP=1 iteration, not production.** Record: `bench/runs/mtp-tp1-smoke-20260925T1310Z.jsonl`. **Why decode looked broken.** No TP=1 run had ever reached decode: - The ttx `g2-tp1-probe` runs are load-only. - Every earlier attempt died in the **first prefill chunk**, before decode. The root cause of the last failure (attempt 3, 13:08Z): - **Op:** `ttnn.experimental.nlp_concat_heads`. - **Call path:** `tp_common.py:74 concat_heads_fit`, called from `gdn/tp.py:761 forward_prefill` (fused out: rms_norm, then concat heads), called from `model.py:1798 _forward_prefill_chunk_tp`, during the warmup prefill trace capture. - **Error:** `TT_THROW dataflow_buffer.cpp:2617 "static dataflow buffers ... clash with L1 buffers ... L1 buffer at 860160, CB region ends at 898048"`. - **Why it happens at TP=1:** the head-major GDN norm output (48 heads at TP=1, 24 at TP=2) was held in L1. Every prefill activation the TP code keeps in L1 was tuned at half the TP=1 width. **Fix** (commit on `mtp-thinkingcap`). `tp_common.pf_l1()` returns DRAM under `TP1_L1_FIT` (set only at one device) and L1 otherwise. It is applied at every short-lived prefill L1 site: - attention: qkv3, the q/kv slices, create-heads, the q/k norm, the gate sigmoid, the wo prefill output; - GDN: the FIR conv output, the fused-out norm and concat. At TP=2 every call returns what it did before. With prefill fixed, **decode ran first time, with no decode-side change.** **Smoke numbers** (B=1, 4096-token prompts, G2, 64 engaged / 1 exempt banners): | Arm | ms/token | TTFT s | Committed per iteration | |---|---:|---:|---:| | plain (warmup, 20 tokens) | ~80.9 | 2.9 | – | | K3 spec, code (timed, 256 tokens) | **38.4** | 4.8 | 3.92 | | TP=2 G2, same K3 code arm | 27.1 | 3.7 | 3.98 | - The K3 iteration breaks down as draft 34.6 + verify 102.7 + reseed 13.1 = 151 ms. - DRAM peaks at 24.4 of 33.1 GB. - Warm load takes 165 s. Host VmHWM reaches 9.45 GB during load and falls to 48 MB after, so two TP=1 loads have to be staggered. - **No timed TP=1 plain TG or PP number yet.** The 80.9 ms is a 20-token warmup. **Next:** gate (c), two TP=1 containers at once (card 0 on the s2a arms, card 1 on the s2b arms, staggered, with watchdog and mem.tsv). That run also supplies the 20-prompt data for gate (a), identity plus logit PCC against TP=2, and gate (b), K3/K7 acceptance. After that comes the solo timed s1 run for TP=1 PP and TG. The pair is queued behind tt-quant's mesh run.
Author
Owner

TP=1 MTP on one P150a: gates measured. G2, B=1, greedy, 256 tokens, ttx per-card slots. Label: TP=1 iteration, not production; rankings stay on TP=2 E2E. Records: bench/runs/mtp-tp1-gates-20260925T1410Z.jsonl (plus .compare.json) on mtp-thinkingcap (d2e9fea). LEDGER R-2026-09-25-mtp-tp1.

Speed (card 1 solo, timed arms with the probe off), ms/token:

Prompt plain TG K3 K7 PP tok/s (plain)
agentic_edit (2,095) 80.4 51.1 40.0 1,144
code (4,096) 80.9 38.1 25.9 1,410
prose_free (4,096) 80.8 58.8 72.8 1,411
qa_reason (4,096) 80.8 49.4 54.4 1,411
  • TP=2 G2 on the same arms: plain 50.6–51.0 ms/token, PP ~3,200 tok/s, K3 code 27.1, K7 code 20.2.
  • Spec-path TTFT is 4.7–5.1 s. A TP=1 verify iteration costs ~103 ms, because the fused verify SDPA falls back at NKV=4.

Gate (b), acceptance: PASSED. Committed tokens per iteration, paired over 20 prompts:

  • K3: TP=1 3.05 vs TP=2 3.01 (Δ +0.04 ± 0.18);
  • K7: 4.54 vs 4.33 (Δ +0.22 ± 0.60).

Gate (c), two TP=1 containers at once: PASSED.

  • Card 0 ran the s2a arms (38) and card 1 the s2b arms (26), all OK, computing together from 13:37 to 13:44Z.
  • MemAvailable stayed ≥ 6,584 MB, and the watchdog (1,536 MB) never fired.
  • VmRSS ≤ 1.35 GB per process after load. VmHWM is ~9.1–9.5 GB during load, so the second card starts only after the first has loaded (bench/mtp/tp1-pair.sh).
  • Caveat: the host had just been rebooted (MemAvailable 10.6 GB at start).

Gate (a), identity vs TP=2: NOT PASSED under the owner rule (control p99: TP=2 1.94, TP=1 1.81).

  • 20 prompts: 1 identical (code, all 256 tokens), 16 near-tie, 3 REAL:
Prompt Position What happens Swing
code.v2 20 TP=2 near-tie 271/198; TP=1 takes 318, TP=2's #3 11.3
code.v5 1 – 3.2
prose_free.v1 58 a top-2 swap 2.6
  • Every divergent token is in the other path's top 3.
  • Logit PCC over 105 dumped rows (4 prompts): median 0.9990, mean 0.9929.
    • Four rows are low: code i=18/22/30 at 0.96/0.85/0.82, agentic i=28 at 0.81.
    • On those rows top-1 agrees (the code tokens are identical throughout), but 25–37k vocab entries scatter by more than 2 logits, up to 9.9.
    • The rows are sporadic in position, with no chunk or bucket boundary.
  • TP=1 vs TP=2 top-2-margin noise: p99 2.44, above TP=2's own fused/unfused control at 1.94.
  • Not localised. One hypothesis is that TP=2 rounds partials to bf16 at each of ~192 row-parallel all-reduces per token while TP=1 accumulates the full K. That is unproven, and it does not explain the sporadic tail rows. Deciding which path is closer to the truth needs a reference forward or a layer-by-layer hidden-state diff at code i=22.

TP=2 byte-identity with this code: whole-chunk prompts are bit-identical. The 2048+47 tail-chunk prompt varies with any process change, including the old overlay with a new harness. That is pre-existing (bench/runs/mtp-tp1-tp2identity-20260925.jsonl).

Code changes this round (all gated to one device):

  • tp_common.pf_l1(): DRAM for short-lived prefill activations that TP=2 keeps in L1. This is the fix that got prefill through and let decode run.
  • run-mtp-ab.sh: SLOT_OWNER mode.
  • tp1-card.sh and tp1-pair.sh: the ttx-slot drivers.

Docs updated: LEDGER, MTP.md, STATUS.md, and the mtp-tt and tt-card-slot skills.

**TP=1 MTP on one P150a: gates measured.** G2, B=1, greedy, 256 tokens, ttx per-card slots. Label: **TP=1 iteration, not production**; rankings stay on TP=2 E2E. Records: `bench/runs/mtp-tp1-gates-20260925T1410Z.jsonl` (plus `.compare.json`) on `mtp-thinkingcap` (d2e9fea). LEDGER `R-2026-09-25-mtp-tp1`. **Speed** (card 1 solo, timed arms with the probe off), ms/token: | Prompt | plain TG | K3 | K7 | PP tok/s (plain) | |---|---:|---:|---:|---:| | agentic_edit (2,095) | 80.4 | 51.1 | 40.0 | 1,144 | | code (4,096) | 80.9 | 38.1 | 25.9 | 1,410 | | prose_free (4,096) | 80.8 | 58.8 | 72.8 | 1,411 | | qa_reason (4,096) | 80.8 | 49.4 | 54.4 | 1,411 | - TP=2 G2 on the same arms: plain 50.6–51.0 ms/token, PP ~3,200 tok/s, K3 code 27.1, K7 code 20.2. - Spec-path TTFT is 4.7–5.1 s. A TP=1 verify iteration costs ~103 ms, because the fused verify SDPA falls back at NKV=4. **Gate (b), acceptance: PASSED.** Committed tokens per iteration, paired over 20 prompts: - K3: TP=1 3.05 vs TP=2 3.01 (Δ +0.04 ± 0.18); - K7: 4.54 vs 4.33 (Δ +0.22 ± 0.60). **Gate (c), two TP=1 containers at once: PASSED.** - Card 0 ran the s2a arms (38) and card 1 the s2b arms (26), all OK, computing together from 13:37 to 13:44Z. - MemAvailable stayed ≥ 6,584 MB, and the watchdog (1,536 MB) never fired. - VmRSS ≤ 1.35 GB per process after load. VmHWM is ~9.1–9.5 GB during load, so the second card starts only after the first has loaded (`bench/mtp/tp1-pair.sh`). - Caveat: the host had just been rebooted (MemAvailable 10.6 GB at start). **Gate (a), identity vs TP=2: NOT PASSED** under the owner rule (control p99: TP=2 1.94, TP=1 1.81). - 20 prompts: 1 identical (code, all 256 tokens), 16 near-tie, 3 REAL: | Prompt | Position | What happens | Swing | |---|---|---|---| | code.v2 | 20 | TP=2 near-tie 271/198; TP=1 takes 318, TP=2's #3 | 11.3 | | code.v5 | 1 | – | 3.2 | | prose_free.v1 | 58 | a top-2 swap | 2.6 | - Every divergent token is in the other path's top 3. - Logit PCC over 105 dumped rows (4 prompts): median 0.9990, mean 0.9929. - Four rows are low: code i=18/22/30 at 0.96/0.85/0.82, agentic i=28 at 0.81. - On those rows top-1 agrees (the code tokens are identical throughout), but 25–37k vocab entries scatter by more than 2 logits, up to 9.9. - The rows are sporadic in position, with no chunk or bucket boundary. - TP=1 vs TP=2 top-2-margin noise: p99 2.44, above TP=2's own fused/unfused control at 1.94. - Not localised. One hypothesis is that TP=2 rounds partials to bf16 at each of ~192 row-parallel all-reduces per token while TP=1 accumulates the full K. That is unproven, and it does not explain the sporadic tail rows. Deciding which path is closer to the truth needs a reference forward or a layer-by-layer hidden-state diff at code i=22. **TP=2 byte-identity with this code:** whole-chunk prompts are bit-identical. The 2048+47 tail-chunk prompt varies with any process change, including the old overlay with a new harness. That is pre-existing (`bench/runs/mtp-tp1-tp2identity-20260925.jsonl`). **Code changes this round** (all gated to one device): - `tp_common.pf_l1()`: DRAM for short-lived prefill activations that TP=2 keeps in L1. This is the fix that got prefill through and let decode run. - `run-mtp-ab.sh`: `SLOT_OWNER` mode. - `tp1-card.sh` and `tp1-pair.sh`: the ttx-slot drivers. Docs updated: LEDGER, MTP.md, STATUS.md, and the mtp-tt and tt-card-slot skills.
Sign in to join this conversation.
No labels
human-approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
bitpartner/tt-stack#69
No description provided.