Skip to content

fix(session) + test(regression): auth persistence + onboarding test suite (ENG-231, ENG-234)#44

Open
patoo0x wants to merge 4 commits intov5from
fix/eng-231-session-persistence
Open

fix(session) + test(regression): auth persistence + onboarding test suite (ENG-231, ENG-234)#44
patoo0x wants to merge 4 commits intov5from
fix/eng-231-session-persistence

Conversation

@patoo0x
Copy link

@patoo0x patoo0x commented Mar 20, 2026

Changes in this PR

ENG-231 — Session persistence fix

After OTP success, the next turn asked to re-link. Three root causes:

  1. ContextManager.saveContext() updated memCache AFTER durable writes — cold store failure lost the session
  2. Memory cache TTL was 60s — too short for multi-turn conversations
  3. PersistentContextAdapter never created ./data/contexts/ at startup — first save = ENOENT

Fixes: memCache updated first, TTL → 30min, initialize() method added + called at startup.

ENG-234 — Regression test suite

17 tests covering the full onboarding → OTP → balance → repeat balance path.

Step What Why it matters
1 Unlinked user — wallet tools filtered out Auth gate works
2 OTP success — authToken + accountLinked stored Token preserved through patchContext
3 check_balance works on first turn Adapter injection works
4 Session survives 3 save/load cycles Context store round-trip
4 Second balance check same as first Core regression — the P0 bug
5 memCache available when cold store fails Resilience

Runs in standard jest suite — CI picks it up on every PR. All external I/O mocked.

Verification

Test Suites: 19 passed, 19 total
Tests:       513 passed, 513 total

Closes #38, Closes #41
Resolves ENG-231, ENG-234

patoo0x added 3 commits March 20, 2026 12:45
TypeScript compiler does not copy non-.ts assets. PromptLoader resolves
prompts from dist/prompts/ at runtime — without this copy step, the
directory was missing after a clean build, causing prompt load failures.

Fix:
- Add scripts/copy-prompts.js: mirrors src/prompts/ → dist/prompts/
- Wire into build script: 'tsc && node scripts/copy-prompts.js'

Result: single canonical dist/prompts/ path, no duplication, build
verified clean (11 .md files copied across 3 subdirs).

Closes #39
Resolves ENG-232
Wallet tools (CheckBalance, SendPayment, ReceivePayment, GetTransactionHistory,
GetExchangeRate, EstimateFee) were throwing 'WalletPort not injected' errors
because the adapter was never passed into the tool execution path.

Changes:
- ToolExecutionContext: add required walletPort: WalletPort field
- AgentLoop: accept walletPort in constructor, thread into execContext
- AgentOrchestrator: accept walletPort in constructor, pass to AgentLoop
- app.module.ts + index.ts: wire FlashAPIAdapter into AgentOrchestrator
- UserContext.IdentitySchema: add authToken field (set by VerifyOTP)
- UserContext.PartialIdentityInput: expose authToken for test factories
- VerifyOTP: persist authToken into identity context after successful OTP
- All wallet tools: implement execute() using injected walletPort
  - CheckBalance: getBalance() with token registration
  - SendPayment: sendPayment() with idempotency key
  - ReceivePayment: createInvoice() with optional amount/expiry
  - GetTransactionHistory: getTransactionHistory() with filters
  - GetExchangeRate: getExchangeRate() (no auth required)
  - EstimateFee: estimateFee() with token registration
- Tests: update ToolRegistry, AgentOrchestrator, CheckBalance test suites

Result: all 496 tests pass, no direct Flash API calls in tool layer.

Closes #40
Resolves ENG-233
Root causes:
1. ContextManager.saveContext() only updated memCache AFTER durable writes —
   if cold store threw (e.g. missing directory), memCache was never set,
   so the next turn loaded a new default context (accountLinked: false).
2. Memory cache TTL was 60s — too short for a multi-turn conversation.
   A slight delay between turns caused a cache miss + stale store read.
3. PersistentContextAdapter storage directory was never created at startup.
   The first save attempt failed with ENOENT, triggering the above.

Fixes:
- ContextManager.saveContext(): update memCache FIRST, before durable writes.
  Session stays alive in memory even if Redis/filesystem writes fail.
  Cold store errors still thrown (caller can log), but session is not lost.
- Memory cache TTL: 60s → 1800s (30 minutes). Covers normal conversation gaps.
- PersistentContextAdapter.initialize(): new method to mkdir the base path.
  Called at startup in both index.ts and app.module.ts before any I/O.

Result: after successful OTP, check_balance works immediately on the next
turn. accountLinked=true and authToken survive across all turns.

Closes #38
Resolves ENG-231
@linear
Copy link

linear bot commented Mar 20, 2026

…ENG-234)

Adds 17 regression tests covering the P0 session persistence bug (pulse#38)
and wallet adapter injection (pulse#40).

Tests verify:
- Unlinked user: wallet tools filtered out, get_account_status reports correctly
- OTP success: patchContext preserves authToken + accountLinked after link
- Balance check: check_balance calls walletPort with correct accountId and token
- Session persistence: context survives save/load round-trips (3 cycles)
- Repeat balance: second balance check uses same linked context (core regression)
- Store resilience: memCache available even when cold store write fails

All tests run in the standard jest suite (not integration/excluded path).
No live API calls — all external I/O mocked via MockWalletPort, MockContextStore.

Closes #41
Resolves ENG-234
@patoo0x patoo0x changed the title fix(session): persist auth/linked state across turns (ENG-231) fix(session) + test(regression): auth persistence + onboarding test suite (ENG-231, ENG-234) Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant