Phases 2 and 3 of the Hungarian tender-qualification feature are built on master (NOT yet pushed). Phase 2 adds an editable company_profile singleton + Settings form; Phase 3 adds on-demand LLM eligibility assessment (tender-qualify edge fn + queue/verdict UI), cloning the CRM /disclosure on-demand-LLM pattern. Continues the 2026-06-09 refocus; for source/schema context see implementation-guide and the TOPICS index.
State: on master, NOT pushed
Both phases are committed to local master and code-reviewed clean (web tests + build green), but not pushed. Two external/operator blockers (below) keep the feature from being live end-to-end.
For Agents
Phase 3 mirrors the /disclosure analyzer: <feature>_request queue table + one <feature> verdict per entity (upsert), an edge fn that claims-races the newest pending request, calls Claude with structured output, and writes the verdict back. Frontend = request hook + polling-status hook + verdict panel. Same ANTHROPIC_API_KEY secret as the disclosure analyzer (_shared/llm.ts).
Phase 2 — Company Profile (DONE, master, not pushed)
Editable singleton holding Levandor’s capabilities/references/financials — the baseline the qualification assessment compares each tender’s eligibility criteria against.
Table company_profile: id integer pk default 1 check (id = 1) — enforces exactly one row. Migration 20260615093853_company_profileapplied live to project mkofmdtdldxgmmolxxhc.
Settings UI: CompanyProfileForm in the Settings page.
The form initialized its local state from query data on every render, so a TanStack Query focus-refetch wiped the user’s in-progress edits. Fixed with a hydrate-once guard:
VERDICT_SCHEMA must avoidminimum / maximum / minLength / pattern and any recursion. Including those keywords makes the raw fetch to the Anthropic API return 400. Keep the JSON schema to plain types/enums/required.
(adaptive thinking)
With thinking: { type: 'adaptive' }, the API puts the thinking block first in content[]. Reading content[0] yields the thinking block, not the answer. Find the block where type === 'text' and parse that:
const text = resp.content.find((b) => b.type === 'text')?.text;
useRequestTenderQualification — enqueue a request.
useTenderQualificationRequest — polls every 8s while the request is pending/running, with a 15-minute bound; invalidates the verdict query key on done.
Components: TenderQualifyButton + QualificationVerdictPanel + an eligibility-prose section on TenderOverviewTab.
i18n: en/hu; labels rendered via t() so Hungarian resolves correctly.
Quality: code-reviewed clean; web tests + build green.
External blockers (operator action required)
Blocker 1 — ANTHROPIC_API_KEY not set in Supabase secrets
Without it, every real-tender LLM assessment lands status = 'error' with "ANTHROPIC_API_KEY not set". Same secret backs _shared/llm.ts (the disclosure analyzer).
supabase secrets set ANTHROPIC_API_KEY=sk-ant-... --project-ref mkofmdtdldxgmmolxxhc
Blocker 2 — web SPA deploy parked
Phase 1+2+3 frontend is not live — the Cloudflare Pages deploy is parked on wrangler login / CLOUDFLARE_API_TOKEN.
Self-verification gaps
Edge fn not self-verifiable end-to-end: verifyAuth needs a real user bearer or TENDER_QUALIFY_SECRET; live invoke is browser-driven.
Empty-criteria short-circuit untestable against real data: all 101 live tenders have eligibility_criteria (0 null-criteria), so the unknown short-circuit path can’t be exercised against production data right now.