HTTP integration test: hybrid route cache tier isolation with seeded traders rows (#283) #306

Closed
opened 2026-06-05 04:08:15 +00:00 by PlasticDigits · 11 comments
PlasticDigits commented 2026-06-05 04:08:15 +00:00 (Migrated from gitlab.com)

Summary

Add an HTTP-level integration test that seeds two traders rows with different tier_id values and proves GET /api/v1/route/solve (global best execution with amount_in) does not serve a wrong-discount cached quote across discount tiers. This closes the regression gap left after GitLab #283 / MR !751: unit tests cover hybrid_cache_key format only; no test exercises resolve_discount_tier → in-memory cache → HTTP response end-to-end.

Current codebase

Cache + tier resolution (shipped #283)

  • indexer/src/api/route_solver.rs:
    • resolve_discount_tier(state, quote_trader) — async DB lookup: discount subject = trader.as_deref().or(sender.as_deref()); reads traders.tier_id via get_trader; unknown → 0.
    • execute_hybrid_route_solve — calls resolve_discount_tier, builds hybrid_cache_key(..., discount_tier), checks cache_get / cache_put.
    • In-process cache: ROUTE_CACHE_TTL 12s, ROUTE_CACHE_MAX_ENTRIES 512, keyed by solver_version|token_in|token_out|amount_bucket|max_maker_fills|trader_key|t{tier}.
  • Unit tests only: hybrid_cache_key_tests::{hybrid_cache_key_includes_trader_or_none, hybrid_cache_key_distinguishes_discount_tier} assert key string equality/inequality; they never hit Postgres or Ax6.

Existing route-solve integration tests

  • indexer/tests/api_route_solve.rs — 19 tests; uses wiremock LCD via indexer/tests/common/lcd_mock.rs.
  • route_solve_get_with_trader_returns_higher_estimate — proves optional trader query param changes LCD mock output (8888888 vs 9777776) but does not seed traders.tier_id and does not assert cache behavior.
  • LCD mock bumps output when trader is present on sim queries; it does not vary output by sender or by resolved DB tier.

Traders table + test seeds

Precedent for cache assertions via wiremock call counts

Why this is needed

  1. Regression safety for #283. A future refactor could break resolve_discount_tier wiring while unit tests on the pure hybrid_cache_key fn still pass — HTTP test catches the real failure mode (wrong estimated_amount_out served from cache).
  2. Quote-accuracy class bug. Two senders on different tiers sharing a cache entry yields misleading pre-trade quotes (chain still enforces real fee at execute, but integrators/dapp preflight would be wrong).
  3. Agent/CI guardrail. Docs and skills (skills/AGENTS_HYBRID_QUOTING.md, docs/indexer-invariants.md) now document tier-based cache keys; CI should enforce the behavior, not just the key format.

Constraints and guardrails

  • Scope: sender-only path (trader unset). #283 keys on resolved tier for subject trader if set else sender. Trusted-router path (sender≠trader, dual-tier key) is out of scope — tracked in #279 cache phase.
  • Use existing test stack: Postgres + wiremock LCD + TestServer; #[serial] + shared DB lock pattern from indexer/tests/common/mod.rs. No LocalTerra deploy required.
  • Do not weaken #283 unit tests — add integration test alongside them.
  • Minimal LCD mock extension: either tier-aware mock responder or distinct fixed outputs keyed on sender in sim queries; avoid coupling to production tier discount math (tiers table lives on-chain; indexer only stores synced tier_id).
  • Tier sync staleness (~600s) is accepted-class per #283; test uses direct SQL seed of traders.tier_id, not trader_tracker LCD sync.
  • POST /route/solve is out of scope unless trivial — POST path does not use the hybrid GET result cache today; focus on GET global best execution.
  • Keep test deterministic and fast — prefer 2-hop seed (seed_route_solve_2hop) + single-path mock over multi-path global solver unless needed.

Relevant files

File Role
indexer/src/api/route_solver.rs resolve_discount_tier, cache get/put, GET handler
indexer/tests/api_route_solve.rs Add new test(s) here
indexer/tests/common/mod.rs Optional helper: seed_traders_with_tiers(pool, &[(addr, tier_id), …])
indexer/tests/common/lcd_mock.rs Extend or add start_tier_aware_route_optimizer_mock()
indexer/src/db/queries/traders.rs get_trader / TraderRow.tier_id
docs/indexer-invariants.md Route GET cache tier invariant (link test)
skills/AGENTS_HYBRID_QUOTING.md Add test command to "Tests to run after changes"
  1. Seed helper (in common/mod.rs or inline in test): insert two terra1… addresses into traders with tier_id = 0 and tier_id = 5 (or 9), registered = true.
  2. Tier-aware LCD mock (extend lcd_mock.rs):
    • Return distinct router simulate_swap_operations amounts for tier-0 vs tier-5 subjects (e.g. base 8888888 vs discounted 9777776), keyed on sender when trader is absent in the sim payload (matching how maybe_simulate forwards sender).
    • Alternatively: return tier-specific amounts based on whichever of trader/sender appears in the wiremock request body.
  3. Test A — cache isolation (primary):
    • GET …/route/solve?token_in=…&token_out=…&amount_in=1000000&sender={tier0_addr} → assert estimated_amount_out == tier0_amount.
    • GET …&sender={tier5_addr} (same tokens/amount) → assert estimated_amount_out == tier5_amount (must differ from tier0 even though first response is cached under a different key).
  4. Test B — same-tier cache hit (secondary):
    • Two different sender addresses, both seeded tier_id = 5.
    • First GET warms cache; second GET with other sender → mock.received_requests() count unchanged (same pattern as api_orderbook_lcd_mock cache test).
  5. Document in skills/invariants: cargo test route_solve_get_cache_tier (or chosen name).

Acceptance criteria

  • New integration test(s) in api_route_solve.rs seed ≥2 traders rows with different tier_id and pass against Postgres + wiremock.
  • Different tiers: sequential GETs with sender=A (tier 0) then sender=B (tier 5), same route params → different estimated_amount_out; second request does not return first sender's cached body.
  • Same tier: two distinct sender addresses both at tier 5 → second GET reuses cache (wiremock call count stable OR response identical with zero new LCD sim calls).
  • Unknown sender (no traders row) resolves tier 0 and does not collide with tier-5 cache entry.
  • Test runs in CI job that already executes cargo test --test api_route_solve (no new infra).
  • Skills/docs cross-link the test name (minimal one-line addition to AGENTS_HYBRID_QUOTING or indexer-invariants evidence column).

Test plan — functional paths

Path Setup Request Expected
Tier 0 sender, cold cache Seed trader A tier_id=0 GET /route/solve?…&amount_in=1M&sender=A 200; tier-0 mock amount
Tier 5 sender after tier-0 cached Same seed + warm cache from row above GET …&sender=B (tier 5) 200; tier-5 amount ≠ tier-0
Same tier cache reuse Seed A,B both tier_id=5 Two GETs differing only by sender 2nd does not increase LCD sim count
Unregistered sender No row for C GET …&sender=C tier-0 amount; distinct key from tier-5
Invalid sender — sender=not-a-wallet 400 (existing validation; no regression)
trader set (smoke) Seed trader row for T GET …&trader=T Uses trader as discount subject (tier from T's row); optional if timeboxed — not the primary #283 gap

Run commands:

cd indexer && cargo test --test api_route_solve route_solve.*cache.*tier -j 1 -- --test-threads=1
cd indexer && cargo test --test api_route_solve -j 1 -- --test-threads=1   # full suite, no regressions
cd indexer && cargo test hybrid_cache_key --lib   # #283 unit tests still pass

Test plan — attack, hack, and abuse vectors

Vector Scenario Expected behavior Test assertion
Cache poisoning / wrong-discount quote Attacker or victim A (tier 0) warms cache; victim B (tier 5) requests same route B must not receive A's cached quote Different estimated_amount_out for B
Tier impersonation via address churn Attacker tries many sender values to probe cached quotes Each distinct tier gets isolated cache slot; unknown → tier 0 No tier-5 quote served to tier-0 sender
Cache bypass amplification Not primary scope (#279) but regression: varying sender alone should not force full fanout within same tier Same-tier senders share cache LCD call count stable on same-tier repeat
SQL injection via sender param sender='; DROP TABLE traders; -- 400 invalid address Reuse pattern from indexer/tests/security.rs if adding abuse case; at minimum no 500
Stale tier_id exploitation Row says tier 5 but on-chain registry says tier 0 Accepted staleness class (#283); chain enforces at execute Document as manual/accepted — do not assert on-chain parity in this test
Trusted-router confusion trader=T_tier5&sender=S_tier0 Known gap (#279): key uses trader tier only today SKIP or explicit #[ignore] documenting #279 — do not false-pass

Verification criteria

Agent/human sign-off when all pass:

  1. cd indexer && cargo test --test api_route_solve -j 1 -- --test-threads=1 — all tests green including new tier-cache test(s).
  2. cd indexer && cargo test hybrid_cache_key --lib — unit tests still green.
  3. Manual code review: test fails if discount_tier segment removed from hybrid_cache_key call in execute_hybrid_route_solve (optional: confirm by temporary local revert).
  4. Docs/skills list the new test command (grep route_solve.*tier in skills/AGENTS_HYBRID_QUOTING.md or docs/indexer-invariants.md).
  • #283 — hybrid cache key on discount tier (code merged MR !751)
  • #245 — off-chain trader/sender forwarding
  • #279 — broader cache hardening (trusted router dual-tier, concurrency)

Labels (suggested)

indexer, correctness, testing, hybrid

## Summary Add an **HTTP-level integration test** that seeds two `traders` rows with different `tier_id` values and proves `GET /api/v1/route/solve` (global best execution with `amount_in`) does **not** serve a wrong-discount cached quote across discount tiers. This closes the regression gap left after [GitLab #283](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/283) / MR !751: unit tests cover `hybrid_cache_key` format only; no test exercises `resolve_discount_tier` → in-memory cache → HTTP response end-to-end. ## Current codebase ### Cache + tier resolution (shipped #283) - [`indexer/src/api/route_solver.rs`](../indexer/src/api/route_solver.rs): - `resolve_discount_tier(state, quote_trader)` — async DB lookup: discount subject = `trader.as_deref().or(sender.as_deref())`; reads `traders.tier_id` via `get_trader`; unknown → **0**. - `execute_hybrid_route_solve` — calls `resolve_discount_tier`, builds `hybrid_cache_key(..., discount_tier)`, checks `cache_get` / `cache_put`. - In-process cache: `ROUTE_CACHE_TTL` **12s**, `ROUTE_CACHE_MAX_ENTRIES` **512**, keyed by `solver_version|token_in|token_out|amount_bucket|max_maker_fills|trader_key|t{tier}`. - **Unit tests only:** `hybrid_cache_key_tests::{hybrid_cache_key_includes_trader_or_none, hybrid_cache_key_distinguishes_discount_tier}` assert key string equality/inequality; they never hit Postgres or Ax6. ### Existing route-solve integration tests - [`indexer/tests/api_route_solve.rs`](../indexer/tests/api_route_solve.rs) — 19 tests; uses wiremock LCD via [`indexer/tests/common/lcd_mock.rs`](../indexer/tests/common/lcd_mock.rs). - `route_solve_get_with_trader_returns_higher_estimate` — proves optional `trader` query param changes LCD mock output (`8888888` vs `9777776`) but **does not seed `traders.tier_id`** and does not assert cache behavior. - LCD mock bumps output when `trader` is present on sim queries; it does **not** vary output by `sender` or by resolved DB tier. ### Traders table + test seeds - Schema: [`indexer/migrations/20260310000001_initial_schema.sql`](../indexer/migrations/20260310000001_initial_schema.sql) — `traders.tier_id SMALLINT NOT NULL DEFAULT 0`. - [`indexer/tests/common/mod.rs`](../indexer/tests/common/mod.rs) — `seed_db` inserts one trader with default tier; `seed_route_solve*` helpers insert pairs/assets but **no tier-specific trader rows**. - [`indexer/src/db/queries/traders.rs`](../indexer/src/db/queries/traders.rs) — `get_trader` used by route solver tier resolution. ### Precedent for cache assertions via wiremock call counts - [`indexer/tests/api_orderbook_lcd_mock.rs`](../indexer/tests/api_orderbook_lcd_mock.rs) — uses `mock.received_requests()` to prove repeat GET hits cache (no extra LCD calls). ## Why this is needed 1. **Regression safety for #283.** A future refactor could break `resolve_discount_tier` wiring while unit tests on the pure `hybrid_cache_key` fn still pass — HTTP test catches the real failure mode (wrong `estimated_amount_out` served from cache). 2. **Quote-accuracy class bug.** Two senders on different tiers sharing a cache entry yields misleading pre-trade quotes (chain still enforces real fee at execute, but integrators/dapp preflight would be wrong). 3. **Agent/CI guardrail.** Docs and skills ([`skills/AGENTS_HYBRID_QUOTING.md`](../skills/AGENTS_HYBRID_QUOTING.md), [`docs/indexer-invariants.md`](../docs/indexer-invariants.md)) now document tier-based cache keys; CI should enforce the behavior, not just the key format. ## Constraints and guardrails - **Scope: sender-only path (`trader` unset).** #283 keys on resolved tier for subject `trader if set else sender`. Trusted-router path (`sender≠trader`, dual-tier key) is **out of scope** — tracked in [#279](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/279) cache phase. - **Use existing test stack:** Postgres + wiremock LCD + `TestServer`; `#[serial]` + shared DB lock pattern from [`indexer/tests/common/mod.rs`](../indexer/tests/common/mod.rs). No LocalTerra deploy required. - **Do not weaken #283 unit tests** — add integration test alongside them. - **Minimal LCD mock extension:** either tier-aware mock responder **or** distinct fixed outputs keyed on `sender` in sim queries; avoid coupling to production tier discount math (tiers table lives on-chain; indexer only stores synced `tier_id`). - **Tier sync staleness (~600s) is accepted-class** per #283; test uses direct SQL seed of `traders.tier_id`, not `trader_tracker` LCD sync. - **POST `/route/solve` is out of scope** unless trivial — POST path does not use the hybrid GET result cache today; focus on GET global best execution. - Keep test **deterministic and fast** — prefer 2-hop seed (`seed_route_solve_2hop`) + single-path mock over multi-path global solver unless needed. ## Relevant files | File | Role | |------|------| | [`indexer/src/api/route_solver.rs`](../indexer/src/api/route_solver.rs) | `resolve_discount_tier`, cache get/put, GET handler | | [`indexer/tests/api_route_solve.rs`](../indexer/tests/api_route_solve.rs) | **Add new test(s) here** | | [`indexer/tests/common/mod.rs`](../indexer/tests/common/mod.rs) | Optional helper: `seed_traders_with_tiers(pool, &[(addr, tier_id), …])` | | [`indexer/tests/common/lcd_mock.rs`](../indexer/tests/common/lcd_mock.rs) | Extend or add `start_tier_aware_route_optimizer_mock()` | | [`indexer/src/db/queries/traders.rs`](../indexer/src/db/queries/traders.rs) | `get_trader` / `TraderRow.tier_id` | | [`docs/indexer-invariants.md`](../docs/indexer-invariants.md) | Route GET cache tier invariant (link test) | | [`skills/AGENTS_HYBRID_QUOTING.md`](../skills/AGENTS_HYBRID_QUOTING.md) | Add test command to "Tests to run after changes" | ## Recommended direction 1. **Seed helper** (in `common/mod.rs` or inline in test): insert two `terra1…` addresses into `traders` with `tier_id = 0` and `tier_id = 5` (or 9), `registered = true`. 2. **Tier-aware LCD mock** (extend `lcd_mock.rs`): - Return distinct router `simulate_swap_operations` amounts for tier-0 vs tier-5 subjects (e.g. base `8888888` vs discounted `9777776`), keyed on `sender` when `trader` is absent in the sim payload (matching how `maybe_simulate` forwards `sender`). - Alternatively: return tier-specific amounts based on whichever of `trader`/`sender` appears in the wiremock request body. 3. **Test A — cache isolation (primary):** - `GET …/route/solve?token_in=…&token_out=…&amount_in=1000000&sender={tier0_addr}` → assert `estimated_amount_out == tier0_amount`. - `GET …&sender={tier5_addr}` (same tokens/amount) → assert `estimated_amount_out == tier5_amount` (**must differ** from tier0 even though first response is cached under a different key). 4. **Test B — same-tier cache hit (secondary):** - Two different sender addresses, both seeded `tier_id = 5`. - First GET warms cache; second GET with other sender → `mock.received_requests()` count **unchanged** (same pattern as `api_orderbook_lcd_mock` cache test). 5. **Document** in skills/invariants: `cargo test route_solve_get_cache_tier` (or chosen name). ## Acceptance criteria - [ ] New integration test(s) in `api_route_solve.rs` seed **≥2** `traders` rows with **different** `tier_id` and pass against Postgres + wiremock. - [ ] **Different tiers:** sequential GETs with `sender=A` (tier 0) then `sender=B` (tier 5), same route params → **different** `estimated_amount_out`; second request does **not** return first sender's cached body. - [ ] **Same tier:** two distinct sender addresses both at tier 5 → second GET reuses cache (wiremock call count stable OR response identical with zero new LCD sim calls). - [ ] **Unknown sender** (no `traders` row) resolves tier **0** and does not collide with tier-5 cache entry. - [ ] Test runs in CI job that already executes `cargo test --test api_route_solve` (no new infra). - [ ] Skills/docs cross-link the test name (minimal one-line addition to AGENTS_HYBRID_QUOTING or indexer-invariants evidence column). ## Test plan — functional paths | Path | Setup | Request | Expected | |------|-------|---------|----------| | Tier 0 sender, cold cache | Seed trader A `tier_id=0` | `GET /route/solve?…&amount_in=1M&sender=A` | 200; tier-0 mock amount | | Tier 5 sender after tier-0 cached | Same seed + warm cache from row above | `GET …&sender=B` (tier 5) | 200; tier-5 amount ≠ tier-0 | | Same tier cache reuse | Seed A,B both `tier_id=5` | Two GETs differing only by sender | 2nd does not increase LCD sim count | | Unregistered sender | No row for C | `GET …&sender=C` | tier-0 amount; distinct key from tier-5 | | Invalid sender | — | `sender=not-a-wallet` | **400** (existing validation; no regression) | | `trader` set (smoke) | Seed trader row for T | `GET …&trader=T` | Uses `trader` as discount subject (tier from T's row); **optional** if timeboxed — not the primary #283 gap | **Run commands:** ```bash cd indexer && cargo test --test api_route_solve route_solve.*cache.*tier -j 1 -- --test-threads=1 cd indexer && cargo test --test api_route_solve -j 1 -- --test-threads=1 # full suite, no regressions cd indexer && cargo test hybrid_cache_key --lib # #283 unit tests still pass ``` ## Test plan — attack, hack, and abuse vectors | Vector | Scenario | Expected behavior | Test assertion | |--------|----------|-------------------|----------------| | **Cache poisoning / wrong-discount quote** | Attacker or victim A (tier 0) warms cache; victim B (tier 5) requests same route | B must not receive A's cached quote | Different `estimated_amount_out` for B | | **Tier impersonation via address churn** | Attacker tries many `sender` values to probe cached quotes | Each distinct tier gets isolated cache slot; unknown → tier 0 | No tier-5 quote served to tier-0 sender | | **Cache bypass amplification** | Not primary scope (#279) but regression: varying `sender` alone should not force full fanout **within same tier** | Same-tier senders share cache | LCD call count stable on same-tier repeat | | **SQL injection via sender param** | `sender='; DROP TABLE traders; --` | **400** invalid address | Reuse pattern from [`indexer/tests/security.rs`](../indexer/tests/security.rs) if adding abuse case; at minimum no 500 | | **Stale tier_id exploitation** | Row says tier 5 but on-chain registry says tier 0 | Accepted staleness class (#283); chain enforces at execute | Document as **manual/accepted** — do not assert on-chain parity in this test | | **Trusted-router confusion** | `trader=T_tier5&sender=S_tier0` | Known gap (#279): key uses trader tier only today | **SKIP** or explicit `#[ignore]` documenting #279 — do not false-pass | ## Verification criteria Agent/human sign-off when **all** pass: 1. `cd indexer && cargo test --test api_route_solve -j 1 -- --test-threads=1` — all tests green including new tier-cache test(s). 2. `cd indexer && cargo test hybrid_cache_key --lib` — unit tests still green. 3. Manual code review: test fails if `discount_tier` segment removed from `hybrid_cache_key` call in `execute_hybrid_route_solve` (optional: confirm by temporary local revert). 4. Docs/skills list the new test command (grep `route_solve.*tier` in `skills/AGENTS_HYBRID_QUOTING.md` or `docs/indexer-invariants.md`). ## Related issues - [#283](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/283) — hybrid cache key on discount tier (code merged MR !751) - [#245](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/245) — off-chain `trader`/`sender` forwarding - [#279](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/279) — broader cache hardening (trusted router dual-tier, concurrency) ## Labels (suggested) `indexer`, `correctness`, `testing`, `hybrid`
Brouie commented 2026-06-05 06:14:42 +00:00 (Migrated from gitlab.com)

mentioned in issue #283

mentioned in issue #283
Brouie commented 2026-06-05 08:23:01 +00:00 (Migrated from gitlab.com)

mentioned in issue #322

mentioned in issue #322
Brouie commented 2026-06-05 08:23:04 +00:00 (Migrated from gitlab.com)

mentioned in issue #324

mentioned in issue #324
ghost1 commented 2026-06-05 11:11:49 +00:00 (Migrated from gitlab.com)

mentioned in commit 662cab6523

mentioned in commit 662cab65231b9c8f767f2e2a273180ab2db9c3f3
PlasticDigits commented 2026-06-05 11:12:10 +00:00 (Migrated from gitlab.com)

mentioned in merge request !797

mentioned in merge request !797
PlasticDigits commented 2026-06-05 11:12:21 +00:00 (Migrated from gitlab.com)

Implementation complete — MR !797

https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/79

Added HTTP integration tests for hybrid GET route cache tier isolation (#283 regression guard):

  • route_solve_get_cache_tier_isolation — tier-0 vs tier-5 senders get distinct estimated_amount_out; unknown sender resolves tier 0
  • route_solve_get_cache_same_tier_reuses_lcd — same-tier senders share cache (wiremock LCD call count stable)
  • route_solve_invalid_sender_returns_400 — sender validation smoke test

All acceptance criteria PASS. Verification:

cd indexer && cargo test route_solve_get_cache_tier -j 1 -- --test-threads=1
cd indexer && cargo test --test api_route_solve -j 1 -- --test-threads=1   # 22 passed
cd indexer && cargo test hybrid_cache_key --lib   # 2 passed
Implementation complete — MR !797 https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/merge_requests/79 Added HTTP integration tests for hybrid GET route cache tier isolation (#283 regression guard): - `route_solve_get_cache_tier_isolation` — tier-0 vs tier-5 senders get distinct `estimated_amount_out`; unknown sender resolves tier 0 - `route_solve_get_cache_same_tier_reuses_lcd` — same-tier senders share cache (wiremock LCD call count stable) - `route_solve_invalid_sender_returns_400` — sender validation smoke test All acceptance criteria PASS. Verification: ```bash cd indexer && cargo test route_solve_get_cache_tier -j 1 -- --test-threads=1 cd indexer && cargo test --test api_route_solve -j 1 -- --test-threads=1 # 22 passed cd indexer && cargo test hybrid_cache_key --lib # 2 passed ```
ghost1 commented 2026-06-05 12:43:27 +00:00 (Migrated from gitlab.com)

mentioned in commit 0583223a6a

mentioned in commit 0583223a6af69ed46903d25ed5336eb9644b98dd
PlasticDigits commented 2026-06-05 12:45:25 +00:00 (Migrated from gitlab.com)

mentioned in commit 1a2801a287

mentioned in commit 1a2801a287d77ba3f5a348a43a9213e53b37fb9b
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-05 12:45:25 +00:00
ghost1 commented 2026-06-05 13:13:17 +00:00 (Migrated from gitlab.com)

mentioned in commit 0dd92913f8

mentioned in commit 0dd92913f8652dcf627acb52f9d70e15d74d15f2
PlasticDigits commented 2026-06-05 13:13:57 +00:00 (Migrated from gitlab.com)

mentioned in merge request !798

mentioned in merge request !798
PlasticDigits commented 2026-06-08 08:43:13 +00:00 (Migrated from gitlab.com)

mentioned in commit abe16fc6a7

mentioned in commit abe16fc6a746780e666343d5bf2e84de2d02db48
Sign in to join this conversation.
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
code/cl8y-dex-terraclassic#306
No description provided.