Frontend: fee-discount registry outage warning for traders #374

Closed
opened 2026-06-13 02:49:45 +00:00 by PlasticDigits · 17 comments
PlasticDigits commented 2026-06-13 02:49:45 +00:00 (Migrated from gitlab.com)

Parent

Follow-up from GitLab #365 — approved verification follow-up 2: non-blocking frontend banner when a registered trader may be charged full fee because the registry LCD is unreachable.

Parent gap: GitLab #361 register item M2.

Current codebase

  • Swap page shows “Hold CL8Y…” only when !registrationQuery.data?.registered (frontend-dapp/src/pages/SwapPage.tsx) — conflates “not registered” with “registered but discount unreadable”.
  • Fee-discount LCD helpers live in frontend-dapp/src/services/terraclassic/feeDiscount.ts (getRegistration, getDiscount).
  • Indexer client exposes getFeeDiscountHealth() → GET /api/v1/health/fee-discount (frontend-dapp/src/services/indexer/client.ts).
  • Partial implementation on main: frontend-dapp/src/utils/feeDiscountRegistryWarning.ts (resolveFeeDiscountRegistryStatus, shouldShowFeeDiscountRegistryWarning, FEE_DISCOUNT_REGISTRY_WARNING_TEXT). Swap page wires registration/discount query errors + indexer health into an amber non-blocking banner. Unit tests: frontend-dapp/src/utils/__tests__/feeDiscountRegistryWarning.test.ts. SwapPage mocks getFeeDiscountHealth in tests.
  • Trade market-order panel (TradeMarketOrderPanel.tsx, TradePage.tsx) does not yet surface the registry-outage warning — scope here is Swap (primary retail fee-discount UX); extend to Trade only if market quotes use the same registration/discount path.

Why needed

Registered tier holders can be charged full pair fee when the registry contract is paused or LCD queries fail, with no UI signal today. Traders may assume their discount still applies because registration UI previously showed them as registered (indexer tier sync or stale LCD success).

Constraints / guardrails

  • Non-blocking warning only — must not disable swap submit (on-chain fail-closed to full fee is accepted behavior).
  • Distinguish unregistered vs registry_unreachable vs registered where data allows; do not show the outage banner for genuinely unregistered wallets.
  • Do not change on-chain behavior or simulation math in this issue.
  • Reuse indexer health when available; fall back to LCD query error flags when indexer health fetch fails silently.
  • Match existing a11y patterns (role=status or alert as appropriate; amber/warning styling consistent with swap indexer-outage banners).

Relevant files

  • frontend-dapp/src/utils/feeDiscountRegistryWarning.ts
  • frontend-dapp/src/pages/SwapPage.tsx
  • frontend-dapp/src/services/terraclassic/feeDiscount.ts
  • frontend-dapp/src/services/indexer/client.ts (getFeeDiscountHealth)
  • frontend-dapp/src/utils/__tests__/feeDiscountRegistryWarning.test.ts
  • frontend-dapp/src/pages/SwapPage.test.tsx (add regression for banner visibility)
  • skills/AGENTS_FEE_DISCOUNT_TIERS.md § Registry outage observability
  1. On Swap (and optionally Trade if fee-discount queries run there), compute showFeeDiscountRegistryWarning via shouldShowFeeDiscountRegistryWarning:
    • registry_unreachable when registrationQuery.isError || discountQuery.isError, or indexer health configured && fee_discount_registry_ok === false.
    • Do not warn when !registration?.registered and LCD queries succeeded (show existing “Hold CL8Y…” path instead).
  2. Render amber banner with FEE_DISCOUNT_REGISTRY_WARNING_TEXT (“Fee discount unavailable; full pair fee may apply until the registry is reachable again.”).
  3. Poll indexer health on a modest staleTime (e.g. 30s) when FEE_DISCOUNT_CONTRACT_ADDRESS is configured.
  4. Add SwapPage unit test: registered + LCD error → warning visible; unregistered + healthy LCD → “Hold CL8Y” path, no outage banner.

Acceptance criteria

  • Registered trader with failing get_registration or get_discount LCD query sees non-blocking outage warning on Swap.
  • Registered trader with indexer fee_discount_registry_ok: false sees the same warning even if local LCD queries have not yet errored.
  • Unregistered trader with healthy registry does not see the outage warning (existing registration CTA unchanged).
  • Swap submit remains enabled when warning is shown.
  • Unit tests cover resolveFeeDiscountRegistryStatus matrix and SwapPage banner visibility.

Test plan

Path Command / action
Unit make test-frontend — feeDiscountRegistryWarning.test.ts, SwapPage.test.tsx registry-outage cases
Manual LocalTerra Register wallet → pause registry or block LCD → Swap shows amber warning; swap executes at full fee
Manual Indexer health down (fee_discount_registry_ok: false) with successful LCD registration read → warning still shown

Attack / abuse test plan

  • Warning text must not include raw LCD error messages, contract addresses of other users, or stack traces.
  • Do not add a public API that accepts arbitrary wallet addresses to probe registry health from the frontend.

Verification criteria

  • Registered + registrationQuery.isError → banner visible; swap button enabled.
  • fee_discount_registry_ok: false from indexer → banner visible without per-trader error details in UI.
  • Parent acceptance criterion “distinguish no registration vs registry unreachable” satisfied on Swap.
## Parent Follow-up from [GitLab #365](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/365) — approved verification follow-up **2**: non-blocking frontend banner when a registered trader may be charged full fee because the registry LCD is unreachable. Parent gap: [GitLab #361](https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/work_items/361) register item **M2**. ## Current codebase - Swap page shows “Hold CL8Y…” only when `!registrationQuery.data?.registered` (`frontend-dapp/src/pages/SwapPage.tsx`) — conflates “not registered” with “registered but discount unreadable”. - Fee-discount LCD helpers live in `frontend-dapp/src/services/terraclassic/feeDiscount.ts` (`getRegistration`, `getDiscount`). - Indexer client exposes `getFeeDiscountHealth()` → `GET /api/v1/health/fee-discount` (`frontend-dapp/src/services/indexer/client.ts`). - **Partial implementation on `main`:** `frontend-dapp/src/utils/feeDiscountRegistryWarning.ts` (`resolveFeeDiscountRegistryStatus`, `shouldShowFeeDiscountRegistryWarning`, `FEE_DISCOUNT_REGISTRY_WARNING_TEXT`). Swap page wires registration/discount query errors + indexer health into an amber non-blocking banner. Unit tests: `frontend-dapp/src/utils/__tests__/feeDiscountRegistryWarning.test.ts`. SwapPage mocks `getFeeDiscountHealth` in tests. - Trade market-order panel (`TradeMarketOrderPanel.tsx`, `TradePage.tsx`) does **not** yet surface the registry-outage warning — scope here is Swap (primary retail fee-discount UX); extend to Trade only if market quotes use the same registration/discount path. ## Why needed Registered tier holders can be charged **full pair fee** when the registry contract is paused or LCD queries fail, with no UI signal today. Traders may assume their discount still applies because registration UI previously showed them as registered (indexer tier sync or stale LCD success). ## Constraints / guardrails - **Non-blocking** warning only — must not disable swap submit (on-chain fail-closed to full fee is accepted behavior). - Distinguish `unregistered` vs `registry_unreachable` vs `registered` where data allows; do not show the outage banner for genuinely unregistered wallets. - Do not change on-chain behavior or simulation math in this issue. - Reuse indexer health when available; fall back to LCD query error flags when indexer health fetch fails silently. - Match existing a11y patterns (role=`status` or `alert` as appropriate; amber/warning styling consistent with swap indexer-outage banners). ## Relevant files - `frontend-dapp/src/utils/feeDiscountRegistryWarning.ts` - `frontend-dapp/src/pages/SwapPage.tsx` - `frontend-dapp/src/services/terraclassic/feeDiscount.ts` - `frontend-dapp/src/services/indexer/client.ts` (`getFeeDiscountHealth`) - `frontend-dapp/src/utils/__tests__/feeDiscountRegistryWarning.test.ts` - `frontend-dapp/src/pages/SwapPage.test.tsx` (add regression for banner visibility) - `skills/AGENTS_FEE_DISCOUNT_TIERS.md` § Registry outage observability ## Recommended direction 1. On Swap (and optionally Trade if fee-discount queries run there), compute `showFeeDiscountRegistryWarning` via `shouldShowFeeDiscountRegistryWarning`: - `registry_unreachable` when `registrationQuery.isError || discountQuery.isError`, **or** indexer health `configured && fee_discount_registry_ok === false`. - Do **not** warn when `!registration?.registered` and LCD queries succeeded (show existing “Hold CL8Y…” path instead). 2. Render amber banner with `FEE_DISCOUNT_REGISTRY_WARNING_TEXT` (“Fee discount unavailable; full pair fee may apply until the registry is reachable again.”). 3. Poll indexer health on a modest `staleTime` (e.g. 30s) when `FEE_DISCOUNT_CONTRACT_ADDRESS` is configured. 4. Add SwapPage unit test: registered + LCD error → warning visible; unregistered + healthy LCD → “Hold CL8Y” path, no outage banner. ## Acceptance criteria - [ ] Registered trader with failing `get_registration` or `get_discount` LCD query sees non-blocking outage warning on Swap. - [ ] Registered trader with indexer `fee_discount_registry_ok: false` sees the same warning even if local LCD queries have not yet errored. - [ ] Unregistered trader with healthy registry does **not** see the outage warning (existing registration CTA unchanged). - [ ] Swap submit remains enabled when warning is shown. - [ ] Unit tests cover `resolveFeeDiscountRegistryStatus` matrix and SwapPage banner visibility. ## Test plan | Path | Command / action | |------|------------------| | Unit | `make test-frontend` — `feeDiscountRegistryWarning.test.ts`, `SwapPage.test.tsx` registry-outage cases | | Manual LocalTerra | Register wallet → pause registry or block LCD → Swap shows amber warning; swap executes at full fee | | Manual | Indexer health down (`fee_discount_registry_ok: false`) with successful LCD registration read → warning still shown | ## Attack / abuse test plan - Warning text must not include raw LCD error messages, contract addresses of other users, or stack traces. - Do not add a public API that accepts arbitrary wallet addresses to probe registry health from the frontend. ## Verification criteria - Registered + `registrationQuery.isError` → banner visible; swap button enabled. - `fee_discount_registry_ok: false` from indexer → banner visible without per-trader error details in UI. - Parent acceptance criterion “distinguish no registration vs registry unreachable” satisfied on Swap.
PlasticDigits commented 2026-06-13 02:49:57 +00:00 (Migrated from gitlab.com)

mentioned in issue #365

mentioned in issue #365
PlasticDigits commented 2026-06-13 03:02:02 +00:00 (Migrated from gitlab.com)

mentioned in merge request !892

mentioned in merge request !892
PlasticDigits commented 2026-06-13 03:02:29 +00:00 (Migrated from gitlab.com)

mentioned in commit 16f31c3d35

mentioned in commit 16f31c3d35045892550e9a1afce8f3fc98d4f8c2
PlasticDigits commented 2026-06-13 03:02:56 +00:00 (Migrated from gitlab.com)

mentioned in merge request !893

mentioned in merge request !893
ghost1 commented 2026-06-13 03:05:08 +00:00 (Migrated from gitlab.com)

mentioned in commit 8160805ff8

mentioned in commit 8160805ff88feba48e95580ba9094bc8b3989fe3
Brouie commented 2026-06-13 03:19:50 +00:00 (Migrated from gitlab.com)

Checked the source + unit layer on my side on main edbe288.

The logic and wiring look right:

  • feeDiscountRegistryWarning.ts covers the matrix — resolveFeeDiscountRegistryStatus returns unconfigured / unregistered / registry_unreachable (on registration LCD error) / registry_unreachable (on indexer fee_discount_registry_ok:false) / registered, and shouldShowFeeDiscountRegistryWarning + the warning copy. Unit tests 6/6 green.
  • SwapPage.tsx wires it end to end: polls getFeeDiscountHealth, computes the flag via shouldShowFeeDiscountRegistryWarning, renders the non-blocking amber banner with FEE_DISCOUNT_REGISTRY_WARNING_TEXT, and keeps the existing "Hold CL8Y" path when there's no warning. Full SwapPage suite is 13/13.

What's not covered yet, so I'm not closing from my side:

  • The AC asks for a SwapPage-level banner-visibility test (registered + LCD error → banner shown; unregistered + healthy → no banner; submit stays enabled). The current SwapPage tests mock getFeeDiscountHealth to ok:true, so the banner never actually renders in any test — the matrix is exercised at the util layer but not at the page. Worth a SwapPage.test.tsx case that drives registration/discount error or health:false and asserts the banner shows and submit stays enabled.
  • The actual amber banner rendering on Swap (a11y role, non-blocking, the unregistered case staying on the CTA) is a browser walkthrough — that part I'll pick up on the frontend pass, not here.

So: implementation and util tests good; needs the page-level banner test plus the browser confirm before I'd sign it off. @PlasticDigits

Checked the source + unit layer on my side on main edbe288. The logic and wiring look right: - feeDiscountRegistryWarning.ts covers the matrix — resolveFeeDiscountRegistryStatus returns unconfigured / unregistered / registry_unreachable (on registration LCD error) / registry_unreachable (on indexer fee_discount_registry_ok:false) / registered, and shouldShowFeeDiscountRegistryWarning + the warning copy. Unit tests 6/6 green. - SwapPage.tsx wires it end to end: polls getFeeDiscountHealth, computes the flag via shouldShowFeeDiscountRegistryWarning, renders the non-blocking amber banner with FEE_DISCOUNT_REGISTRY_WARNING_TEXT, and keeps the existing "Hold CL8Y" path when there's no warning. Full SwapPage suite is 13/13. What's not covered yet, so I'm not closing from my side: - The AC asks for a SwapPage-level banner-visibility test (registered + LCD error → banner shown; unregistered + healthy → no banner; submit stays enabled). The current SwapPage tests mock getFeeDiscountHealth to ok:true, so the banner never actually renders in any test — the matrix is exercised at the util layer but not at the page. Worth a SwapPage.test.tsx case that drives registration/discount error or health:false and asserts the banner shows and submit stays enabled. - The actual amber banner rendering on Swap (a11y role, non-blocking, the unregistered case staying on the CTA) is a browser walkthrough — that part I'll pick up on the frontend pass, not here. So: implementation and util tests good; needs the page-level banner test plus the browser confirm before I'd sign it off. @PlasticDigits
Brouie commented 2026-06-13 03:39:53 +00:00 (Migrated from gitlab.com)

mentioned in commit d7dcfe81e7

mentioned in commit d7dcfe81e75d75e0037a89020b37cfacdcd85aa8
Brouie commented 2026-06-13 03:40:05 +00:00 (Migrated from gitlab.com)

mentioned in merge request !896

mentioned in merge request !896
Brouie commented 2026-06-13 03:40:16 +00:00 (Migrated from gitlab.com)

Filled the gap I flagged — the missing SwapPage banner-visibility test (AC5).

Branch qa/374-swappage-registry-banner-test (commit d7dcfe8), MR !896. New file SwapPage.feeDiscountRegistryBanner.test.tsx, 4 page-level cases:

  • registered + indexer fee_discount_registry_ok:false → banner renders, Swap submit stays enabled (non-blocking).
  • registered + get_registration LCD query error → banner renders.
  • registered + healthy registry → no banner.
  • unregistered + healthy registry → no banner.

The fee-discount queries gate on FEE_DISCOUNT_CONTRACT_ADDRESS, which is empty in the test env, so the file mocks @/utils/constants truthy — scoped to this file, the existing SwapPage.test.tsx 13 tests are unaffected (verified green). Test-only, tsc -b clean.

Needs your review/merge. After that the only thing left on #374 is the browser walkthrough (banner actually rendering on Swap, a11y role, unregistered staying on the CTA), which I'll pick up on the frontend pass. @PlasticDigits

Filled the gap I flagged — the missing SwapPage banner-visibility test (AC5). Branch qa/374-swappage-registry-banner-test (commit d7dcfe8), MR !896. New file SwapPage.feeDiscountRegistryBanner.test.tsx, 4 page-level cases: - registered + indexer fee_discount_registry_ok:false → banner renders, Swap submit stays enabled (non-blocking). - registered + get_registration LCD query error → banner renders. - registered + healthy registry → no banner. - unregistered + healthy registry → no banner. The fee-discount queries gate on FEE_DISCOUNT_CONTRACT_ADDRESS, which is empty in the test env, so the file mocks @/utils/constants truthy — scoped to this file, the existing SwapPage.test.tsx 13 tests are unaffected (verified green). Test-only, tsc -b clean. Needs your review/merge. After that the only thing left on #374 is the browser walkthrough (banner actually rendering on Swap, a11y role, unregistered staying on the CTA), which I'll pick up on the frontend pass. @PlasticDigits
PlasticDigits commented 2026-06-13 03:41:33 +00:00 (Migrated from gitlab.com)

mentioned in commit 44d73ee47c

mentioned in commit 44d73ee47ceefe87dd8a52fc469d5482d3f37332
PlasticDigits commented 2026-06-13 03:42:13 +00:00 (Migrated from gitlab.com)

mentioned in commit d013b611ab

mentioned in commit d013b611ab3b5407b7ce677f4b2ace4824f677ea
PlasticDigits commented 2026-06-13 03:47:52 +00:00 (Migrated from gitlab.com)

mentioned in commit a17df69fcb

mentioned in commit a17df69fcbf1dbf04ae271f00572c2fc6858ded4
PlasticDigits commented 2026-06-13 04:20:42 +00:00 (Migrated from gitlab.com)

mentioned in commit 17453e7a49

mentioned in commit 17453e7a49135a24b5a5b1527b98d99eddbd3961
PlasticDigits commented 2026-06-13 04:21:01 +00:00 (Migrated from gitlab.com)

mentioned in merge request !899

mentioned in merge request !899
PlasticDigits commented 2026-06-13 04:23:14 +00:00 (Migrated from gitlab.com)

mentioned in commit 3ece932132

mentioned in commit 3ece932132aa865b88987d8cc4078b76897ec40a
PlasticDigits commented 2026-06-13 08:13:45 +00:00 (Migrated from gitlab.com)

Verification — GitLab #374 (PASS)

Verified on main @ 78e3222 with LocalTerra (make setup-cloud-localterra --fresh --skip-build), indexer tmux indexer-dev, and make dev (http://127.0.0.1:5173).

Acceptance criteria

Criterion Result How verified
Registered trader + failing get_registration / get_discount LCD → non-blocking amber warning on Swap PASS feeDiscountRegistryWarning.test.ts; SwapPage.feeDiscountRegistryBanner.test.tsx; SwapPage.test.tsx § registry outage; browser (Playwright/Chromium): block fee-discount LCD smart queries → [data-testid="swap-fee-discount-registry-warning"] visible
Registered trader + indexer fee_discount_registry_ok: false → same warning PASS Unit/page tests above; browser with mocked health endpoint; live test: pointed indexer FEE_DISCOUNT_ADDRESS at invalid contract → GET /api/v1/health/fee-discount returned ok:false while LCD get_registration still succeeded → banner rendered without mock
Unregistered + healthy registry → no outage banner; existing Hold CL8Y CTA PASS SwapPage.test.tsx + SwapPage.feeDiscountRegistryBanner.test.tsx; browser after on-chain deregister + quoted swap (Hold CL8Y… visible, no outage banner)
Swap submit remains enabled when warning shown PASS SwapPage.feeDiscountRegistryBanner.test.tsx (indexer ok:false case asserts Swap enabled); SwapPage.test.tsx registry-outage cases
Unit tests: resolveFeeDiscountRegistryStatus matrix + SwapPage banner visibility PASS 10 util + 4 dedicated page + 3 inline SwapPage.test.tsx cases (30/30 targeted vitest green)

Automated commands (all green)

make test-frontend                                    # 983 passed
vitest run feeDiscountRegistryWarning.test.ts \
  SwapPage.feeDiscountRegistryBanner.test.tsx \
  SwapPage.test.tsx                                 # 30 passed
make verify-issue-365                               # P5 contract + indexer health + util (parent #365)

Manual / browser (LocalTerra)

  • Simulated Wallet (VITE_DEV_MNEMONIC), risk-ack dismissed, Swap page.
  • Healthy registry: no amber outage banner.
  • Indexer down (fee_discount_registry_ok: false): amber banner, role="status", alert-warning, stable copy — no raw LCD errors or addresses in UI.
  • LCD registration queries blocked: same banner.
  • Unregistered wallet: after quote, Hold CL8Y… CTA; no outage banner.
  • Live indexer outage: temporarily set bad FEE_DISCOUNT_ADDRESS in indexer/.env, restarted indexer — banner appeared with real health endpoint (restored env after).

Attack / abuse checks

  • Warning copy is generic (FEE_DISCOUNT_REGISTRY_WARNING_TEXT); util test + browser assert no terra1… / error / stack traces in banner text. PASS

Notes

  • Trade market-order panel intentionally out of scope per issue body.
  • Full on-chain “swap at full fee while registry unreachable” is covered by parent #365 contract invariant P5 (make verify-issue-365); this issue’s frontend observability scope is satisfied.

Closing as verified.

## Verification — GitLab #374 (PASS) Verified on `main` @ `78e3222` with LocalTerra (`make setup-cloud-localterra --fresh --skip-build`), indexer tmux `indexer-dev`, and `make dev` (`http://127.0.0.1:5173`). ### Acceptance criteria | Criterion | Result | How verified | |-----------|--------|----------------| | Registered trader + failing `get_registration` / `get_discount` LCD → non-blocking amber warning on Swap | **PASS** | `feeDiscountRegistryWarning.test.ts`; `SwapPage.feeDiscountRegistryBanner.test.tsx`; `SwapPage.test.tsx` § registry outage; browser (Playwright/Chromium): block fee-discount LCD smart queries → `[data-testid="swap-fee-discount-registry-warning"]` visible | | Registered trader + indexer `fee_discount_registry_ok: false` → same warning | **PASS** | Unit/page tests above; browser with mocked health endpoint; **live** test: pointed indexer `FEE_DISCOUNT_ADDRESS` at invalid contract → `GET /api/v1/health/fee-discount` returned `ok:false` while LCD `get_registration` still succeeded → banner rendered without mock | | Unregistered + healthy registry → no outage banner; existing Hold CL8Y CTA | **PASS** | `SwapPage.test.tsx` + `SwapPage.feeDiscountRegistryBanner.test.tsx`; browser after on-chain `deregister` + quoted swap (`Hold CL8Y…` visible, no outage banner) | | Swap submit remains enabled when warning shown | **PASS** | `SwapPage.feeDiscountRegistryBanner.test.tsx` (indexer `ok:false` case asserts Swap enabled); `SwapPage.test.tsx` registry-outage cases | | Unit tests: `resolveFeeDiscountRegistryStatus` matrix + SwapPage banner visibility | **PASS** | 10 util + 4 dedicated page + 3 inline `SwapPage.test.tsx` cases (30/30 targeted vitest green) | ### Automated commands (all green) ``` make test-frontend # 983 passed vitest run feeDiscountRegistryWarning.test.ts \ SwapPage.feeDiscountRegistryBanner.test.tsx \ SwapPage.test.tsx # 30 passed make verify-issue-365 # P5 contract + indexer health + util (parent #365) ``` ### Manual / browser (LocalTerra) - Simulated Wallet (`VITE_DEV_MNEMONIC`), risk-ack dismissed, Swap page. - **Healthy registry:** no amber outage banner. - **Indexer down (`fee_discount_registry_ok: false`):** amber banner, `role="status"`, `alert-warning`, stable copy — no raw LCD errors or addresses in UI. - **LCD registration queries blocked:** same banner. - **Unregistered wallet:** after quote, **Hold CL8Y…** CTA; no outage banner. - **Live indexer outage:** temporarily set bad `FEE_DISCOUNT_ADDRESS` in `indexer/.env`, restarted indexer — banner appeared with real health endpoint (restored env after). ### Attack / abuse checks - Warning copy is generic (`FEE_DISCOUNT_REGISTRY_WARNING_TEXT`); util test + browser assert no `terra1…` / `error` / stack traces in banner text. **PASS** ### Notes - Trade market-order panel intentionally out of scope per issue body. - Full on-chain “swap at full fee while registry unreachable” is covered by parent **#365** contract invariant P5 (`make verify-issue-365`); this issue’s frontend observability scope is satisfied. Closing as verified.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-13 08:13:46 +00:00
PlasticDigits commented 2026-07-12 07:14:19 +00:00 (Migrated from gitlab.com)

mentioned in issue #476

mentioned in issue #476
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#374
No description provided.