AGENTS.md watermarks refreshed to HEAD (79a93d4).
Grooming: closed 2 prediction/actioned issues.
- #1179: /stakestake fix verified working in post-fix user-test (2026-03-27)
- #1177: fresh red-team session 2026-03-27 with 7 real attacks (floor_held)
Remaining open: #1166, #1141 (prediction/backlog, planner watching),
#857, #856, #383 (vision).
No blocked issues, no open PRs, no stale PRs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AGENTS.md watermarks refreshed to HEAD (7d72f40).
landing/AGENTS.md: document new pitch-deck.html (influencer outreach).
Grooming: CLEAN — 5 open issues (2 prediction/backlog, 3 vision), no
backlog issues, no blocked issues, no open PRs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AGENTS.md watermarks refreshed to HEAD (4dedc72). Watermark bump only —
no code changes since last gardener run.
Pending actions (3): re-queue promotion of #1155 pitch-deck to backlog
(pending actions from PR #1162 were not executed by orchestrator).
Escalate: #1158 (Phase 1 completion accuracy) — still needs planner/human decision.
Add self-hosted Umami analytics to replace the third-party cloud.umami.is
tracker. Creates @harb/analytics package with typed event helpers and
instruments the conversion funnel: CTA clicks (landing), wallet connect,
swap initiated, and stake created (web-app).
- Add Umami Docker service sharing existing postgres (separate DB)
- Add Caddy /analytics route to proxy Umami dashboard
- Configure via VITE_UMAMI_URL and VITE_UMAMI_WEBSITE_ID env vars
- Document setup and funnel events in docs/ENVIRONMENT.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update all AGENTS.md watermarks to HEAD (b276392)
- Clean up dust.jsonl: remove already-bundled items (601,627,739,741)
- Pending actions: promote #1099/#1100/#1101 to backlog, close stale
prediction issues #1020/#1103/#1107, comment on partial resolution
of #1022 (holdout resolved, user-test still empty)
Use brace expansion in import.meta.glob pattern so Vite treats it as a
dynamic glob (returns {} when no files match) instead of compiling it
to a static import that errors when the gitignored file is absent in CI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use import.meta.glob with eager loading instead of a static import so
config.ts degrades gracefully when the file is absent (gitignored).
Fixes the node-quality CI failure introduced when the file was removed
from the repo in this PR.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Address AI reviewer warnings:
- Add separate unstakeError ref so unstake failures are not displayed
via the load-data error UI whose Retry calls loadActivePositionData
- Add exitSucceeded flag to distinguish pre-exit failures ("Failed to
unstake position.") from post-exit data-refresh failures ("Position
unstaked, but failed to refresh data."), preventing factually wrong
messages after a successful transaction
- Unstake error UI has no Retry button; the Unstake button in actions
remains available for retrying a failed transaction
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wrap the entire unstakePosition() body in try/catch/finally so that
errors from exitPosition(), loadActivePositions(), and loadPositions()
are caught, displayed to the user via the existing error ref, and
loading state is always reset correctly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Include taxDue in taxCostPercent computation so the Tax% and Net%
shown in the header P&L line are consistent with the Tax Cost card
and Total row, which already use taxDue + taxPaid.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
taxPaidGes = taxDue + taxPaid, so the displayed value includes both
outstanding tax and historically paid tax. Rename the UI label from
'Tax Paid' to 'Tax Cost' to accurately reflect the combined amount.
Update the matching E2E test selector accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When profit or taxPaidGes were undefined (data still loading), the
computed returned props.amount due to the ?? 0 fallbacks. The computed
now returns undefined until both values are loaded, and the template
guard is simplified to total !== undefined.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
No push event exists for Ponder indexing completion; grandfathered with
justification comment per the no-fixed-delays rule exception policy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove redundant onMounted call that fired loadLiquidityStats() a second
time. The watch() with { immediate: true } already handles the initial
load and all subsequent dependency changes, making onMounted redundant.
Also remove now-unused onMounted import.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
isRecord, coerceString, getErrorMessage, ensureAddress were removed
from useSwapKrk.ts but CheatsView.vue still imported them from there.
Update CheatsView to import from @harb/utils directly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace screen.width with window.innerWidth in useMobile composable.
screen.width reports the physical screen size (0 in headless Chromium),
while window.innerWidth reflects the actual viewport — the correct metric
for responsive layout. The previous Object.defineProperty workaround in
wallet-provider.ts could not override the native Screen.prototype getter,
so screen.width remained 0, isMobile stayed true, and ConnectButton was
never rendered. Fix wallet-provider.ts to pass viewport/screen options
directly to browser.newContext() and remove the broken init-script shim.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace v-model with :value + @input on the number input in LocalSwapWidget.
Vue 3's vModelText directive coerces <input type="number"> values to numbers
via looseToNumber(), causing swapAmount to become a JS number (e.g. 0.05) after
Playwright fill(). viem's parseEther() requires a string and throws when passed
a number, triggering the "Enter a valid ETH amount" error.
The fix mirrors FInput's explicit @input handler which always reads
event.target.value as a string, keeping swapAmount typed as string throughout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Initialize taxDue as ref<bigint>(0n) instead of ref<bigint>() so the
type is always bigint, eliminating the undefined in the Ref type and
making the bigint addition at line 219 type-safe without a null guard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract shared Uniswap ABIs (WETH_ABI, SWAP_ROUTER_ABI, UNISWAP_FACTORY_ABI,
UNISWAP_POOL_ABI), utility functions (isRecord, coerceString, getErrorMessage,
ensureAddress), and the wrap→approve→exactInputSingle flow into a new composable
useSwapKrk(). Both LocalSwapWidget and CheatsView now delegate to this single
source of truth, so swap logic changes only need to be made in one place.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LocalSwapWidget: move useWallet() to top-level <script setup> (was
incorrectly called inside async buyKrk() event handler)
- LocalSwapWidget + CheatsView: fix misleading toast — all transactions
are fully confirmed at that point, so say 'Swap complete' not 'Swap
submitted'
- LocalSwapWidget: add $KRK sigil to warning/hint text for consistency
with GetKrkView
- LocalSwapWidget: add comment on amountOutMinimum: 0n explaining it is
intentional for a no-MEV local anvil environment
- CheatsView: apply same useWallet() fix (pre-existing anti-pattern)
- docs/ENVIRONMENT.md: document VITE_ENABLE_LOCAL_SWAP and related
VITE_ variables in a new webapp env-var table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add `VITE_ENABLE_LOCAL_SWAP` env var to config.ts (defaults false)
- Create LocalSwapWidget.vue: inline ETH→KRK swap (wrap→approve→exactInputSingle)
- GetKrkView.vue: show LocalSwapWidget when VITE_ENABLE_LOCAL_SWAP=true, Uniswap link otherwise
- docker-compose.yml: set VITE_ENABLE_LOCAL_SWAP=true for webapp service
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove redundant `node_modules/` entries from sub-directory .gitignore
files. The root `.gitignore` already has `**/node_modules/` which covers
all nested directories, making these per-package entries unnecessary.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>