Smaller fixes from the 2nd code-review pass on plan 21.
Backend / CLI / DB:
- memoryTools.ts: error-path fallback no longer hardcodes
retrievalStrategy:"exact"; uses DEFAULT_MEMORY_SETTINGS via
toMemoryRetrievalConfig (D16 / Bug #7).
- memory.mjs: applyLegacyTypeMap also runs on search / list / clear
(was only on add); legacy user/feedback/project/reference remap to
canonical types with a stderr warning (D17 / Bug #4).
- migrationRunner.ts: case "073" guards via
hasColumn(memories, needs_reindex) so an unmarked re-run of
073_memory_vec.sql is skipped cleanly (D27).
UI:
- MemoryEngineStatus: optional onConfigure callback; "Configurar →"
CTAs on the Embedding / Qdrant / Rerank rows when those components
are off or missing (matches §4.3 wireframe).
- EngineTab: scroll IDs on config cards + handleConfigure wired to
the status panel. Providers fetch moved from render body
(setState-during-render anti-pattern) into useEffect.
- RerankConfigCard: toggle is disabled when no provider has a key —
blocks turning rerank ON without a provider, still allows turning
it OFF (D13).
- MemoriesTab: Import validates each entry against the canonical
type enum before POST so invalid types are caught locally with a
clear skipped count.
Tooling:
- package.json: test:all includes test:vitest:ui so the UI suite
is no longer orphaned in CI.
Tests:
- cli-memory-commands: asserts updated for the new legacy->canonical
remap on search/clear.
- memory-embedding-resolve: drop always-true `|| reason.length > 0`
clauses that neutralized two assertions.
- memory-embedding-static-potion: model_load_failed test forces a
real load failure via MEMORY_STATIC_CACHE_DIR=/dev/null/<subdir>
and asserts EmbeddingError shape + reason + sanitized message
(replaces the previous `assert.ok(true)`).
- rerank-config-card.test.tsx: happy-path now uses a provider with
hasKey; new test covers the disabled-toggle guard.
Full memory suite green: 331/331 unit tests, 46/46 UI tests.
typecheck:core, typecheck:noimplicit:core, check:cycles clean.
Round-3 F-I18N covered ConversationTab, StatsTab, StatsCharts, and labels in
TimingWaterfall, but missed:
- TimingTab.tsx — 5 user-visible labels (Timestamp, Method, Status, Request
size, Response size) were hardcoded English.
- TimingWaterfall.tsx — empty state ("No timing data available.") and Total
latency label were also hardcoded.
- common.understand — RiskNoticeModal calls
`useTranslations("common")("understand")` but the key did not exist;
only the `|| "I understand"` fallback rescued it, leaving pt-BR users
seeing English.
Adds the missing 7 trafficInspector.timing* keys and common.understand to both
en.json and pt-BR.json, wires `useTranslations` in both components, and adds a
source-grep test asserting no English literals remain in JSX and that all 8
keys exist in both locales.
`Button.tsx` exposes only a default export, but `RiskNoticeModal.tsx` was
importing `{ Button }` (named) — so `Button` resolved to `undefined` and every
render of the modal crashed with React's "Element type is invalid".
The modal opens on first DNS activation for every agent, so this bug
effectively broke DNS interception for every agent in production. It went
undetected through 3 rounds of code review because two test artifacts masked
the failure:
1. `tests/unit/ui/agent-card.test.tsx > calls onDnsToggle when DNS button
clicked` failed since round 3 with the exact error "Element type is
invalid... Check the render method of RiskNoticeModal", but was repeatedly
dismissed as "pre-existing / flaky".
2. `tests/unit/ui/agent-card-risk-modal.test.tsx` (rodada 3) mocked Button
as a named export — which made the test green even though the production
import was broken. Classic "test alignment to broken code" anti-pattern.
This commit:
- switches RiskNoticeModal to `import Button from "@/shared/components/Button"`
- adds a regression-guard test that source-greps for the default-import shape
and asserts Button.tsx remains default-only
- updates the named mock in agent-card-risk-modal.test.tsx to `default:` so
tests now reflect the real module shape (no more masking)
- updates the agent-card.test.tsx DNS-click test to seed the per-agent
risk-accepted localStorage flag, isolating the DNS-toggle path from the
risk-modal path (the modal flow is covered by the risk-modal spec)
After: agent-card.test.tsx 4/4 green; agent-card-risk-modal.test.tsx 5/5 green;
new regression guard prevents recurrence of either pattern.
- mitm-proxy-moved-page.test.tsx: 4 tests — banner renders pageMoved.title/message, goNow triggers router.replace, setTimeout auto-redirect fires at 2500ms
- agent-bridge-server-card-a11y.test.tsx: source-inspection tests for all 6 aria-label attributes in AgentBridgeServerCard + 2 for SessionRecorderBar
- conversation-tab-separators.test.tsx: +1 test that conversationNotAvailable key resolves when body is null
Closes Gap 2 from code review #2: ExportCodeModal, BuildTab, PresetPicker,
StudioTopBar, SearchToolsTopBar, SearchConceptCard and ProviderCatalog now
use useTranslations() from next-intl for all user-facing strings.
The F9 i18n PR (566ebb953) added the keys but several legacy hardcoded
strings still slipped through. This change wires them up and fixes
pt-BR translations that were leaking English ("Cancel" → "Cancelar",
"Save" → "Salvar", "Copy" → "Copiar", "Clear All" → "Limpar tudo", etc.).
New keys added in both pt-BR.json and en.json under the `playground`
namespace:
- close, closeExportModal, exportShort
- exportRealKeyWarning, placeholderHintPrefix, placeholderHintSuffix
- copyLangCode (with {language} placeholder)
- loadingPresets, loadPresetPlaceholder
Test updates:
- 7 UI test files: mocks updated for next-intl (useTranslations) and
assertions updated to match the i18n key-as-text returned by the mock.
- PlaygroundStudio test: 2 tests previously checked for a "F7 implementation
pending" placeholder which no longer exists (F7+F9 fully implemented those
tabs) — assertions now verify the tab becomes active instead.
- PlaygroundConfigPane test: endpoint select count bumped from 10 to 13
(D4-rev2) with a more robust selector that finds the endpoint select
regardless of PresetPicker's load-preset select position.
Validation:
- npm run typecheck:core: clean
- npm run lint: 0 errors (warnings pre-existing)
- npx vitest run tests/unit/ui: 245 tests pass (26 files)
- node --test tests/unit/playground-*.test.ts tests/unit/search-tools-*.test.ts
tests/unit/db-playground-presets.test.ts: 122 tests pass
AgentCard.handleRiskAccept was calling markRiskAccepted() before opening the
RiskNoticeModal, which itself writes the same key via dontShowAgainKey on accept.
Remove the redundant markRiskAccepted call and delete the now-unused helper so
RiskNoticeModal (D16) is the sole canonical persistence owner. Add a spy-based
test asserting the key is written exactly once per accept.
11 assertions covering: dynamic import with ssr:false, absence of static
recharts import in StatsTab, absence of the discarded _rechartsPreload
pattern, and presence of recharts exports in StatsCharts.
Components were created by F4 per master-plan §3.7-§3.9 but never integrated:
the `*ToolCard.tsx` files use the legacy `ManualConfigModal` from
`@/shared/components` (barrel-export root), not the F4 versions in
`@/shared/components/cli`. The 3 files were sitting as dead code.
Removes:
- src/shared/components/cli/BaseUrlSelect.tsx
- src/shared/components/cli/ApiKeySelect.tsx
- src/shared/components/cli/ManualConfigModal.tsx
- tests/unit/ui/BaseUrlSelect.test.tsx
- tests/unit/ui/ApiKeySelect.test.tsx
- tests/unit/ui/ManualConfigModal.test.tsx
Updates `src/shared/components/cli/index.ts` to drop the dead exports.
Kept (actively used by page clients):
- CliToolCard, CliConceptCard, CliComparisonCard
Verified:
- typecheck:core + noimplicit:core clean
- npx eslint src/shared/components/cli/: 0 issues
- check:cycles: clean (212 files, -3 from removed components)
- 50/50 UI tests pass across the 3 kept components + 3 page clients
- 0 residual imports of the 3 removed symbols anywhere in src/ or tests/
Closes code review v3 gap #1 (dead code).
New test file stacked-allocation-bar.test.tsx covers:
- empty allocations → null render
- 3 segments with correct widths (50%/30%/20%)
- 1 segment at 100%
- labels without usedSuffix when usage=null
- usedSuffix labels when usage provided (consumed/fairShare%)
- fallback to apiKeyId when keyLabel missing
pool-card.test.tsx updated: mock StackedAllocationBar, assert it renders
when pool.allocations is non-empty.
- tests/unit/custom-cli-config.test.ts: fix ERR_MODULE_NOT_FOUND — stale import path cli-tools → cli-code (regression from F8 git mv, missed by F10 audit because it only ran curated test subset).
- tests/unit/ui/CliAgentsPage.test.tsx: update vi.mock path to current cli-code location (was no-op mock pointing to deleted path).
- tests/unit/ui/CliToolCard.test.tsx: update URL strings /dashboard/cli-tools/claude → /dashboard/cli-code/claude (cosmetic alignment with new routes).
- src/app/(dashboard)/dashboard/cli-code/components/ToolDetailClient.tsx: remove dead case "cliproxyapi" + unused import (no entry in CLI_TOOLS catalog).
- src/app/(dashboard)/dashboard/cli-agents/CliAgentsPageClient.tsx: replace inline div skeleton with shared <CardSkeleton /> for visual consistency with CliCodePageClient.
- src/app/api/cli-tools/{forge,jcode,deepseek-tui,smelt,pi}-settings/route.ts: replace catch (err: any) with catch (err) + (err as NodeJS.ErrnoException).code narrowing (8 instances, eliminates 8 of 11 implicit-any introductions).
Validated: custom-cli-config.test.ts now 3/3 PASS (was 0/1 FAIL with ERR_MODULE_NOT_FOUND); F1/F3 tests 147/147 PASS; UI tests 25/25 PASS; typecheck:core + noimplicit clean.
Converts the monolithic memory page into a 3-tab Studio layout
(Memories | Playground | Engine) with URL-driven tab state, 8 new
React components, 2 SWR hooks, 50+ i18n keys, and 8 Vitest unit tests
covering all new components (45/45 passing).
Add eslint-disable-next-line comment to the outer hookRef assignment inside
mountHook<T> in all 5 vitest UI test files — the assignment is intentional
(test harness captures hook return via React ref) and safe.
- compliance-tab-actor-filter.test.tsx: 4 tests verifying actor input
presence, initial fetch has no actor param, re-fetch with actor=X
after typing, and clearFilters does not throw.
- compression-log-namespace.test.tsx: 4 tests verifying that the logs
namespace is used (not settings), no _MISSING_ sentinels, and all 4
required keys are covered.