* fix(call): close abandoned idempotency claims with a stored 410 instead of 409 for 24h
A served call whose done-marking failed (a DB pool timeout) left its claim pending, and every
retry of that key answered 409 idempotency_in_progress until the 24h window expired.
- A claim is a lease owned by its call: it carries call_ref from the claim, the owner renews it
every 5 minutes while running, and store, release and renewal are fenced on call_ref.
- A lease older than 15 minutes with no open hold and no pending async task under call_ref (and
its call_ref: children) is closed by compare-and-swap on owner and lease timestamp with a
stored terminal 410: idempotency_response_lost with the charge when the owner's ledger shows
one (GET /calls/{id}/result may still have the answer), else idempotency_outcome_unknown.
The key is never run again: a lapsed lease does not prove the owner stopped, and a second run
under the same key is the double charge idempotency exists to prevent. Legacy rows without a
call_ref keep answering 409 until they expire.
- Storing, releasing and renewing a claim retry once on a pool timeout, on a fresh session.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf(db): index the idempotency sweep and slow the arena refresh
Both run on the 1 vCPU primary whose CPU pinned during the pool-saturation windows that
stranded idempotency claims and settlements:
- _claim_idempotent sweeps expired labels on every keyed call, but idempotentcall had only
membership_id, so the DELETE read every label the caller holds (tens of thousands for a
batch caller). (membership_id, expires_at) makes it a range over the expired rows (0052).
- The arena collector re-aggregated the whole 30-day window every 120 s: three multi-second
aggregates plus a retention DELETE over a multi-GB table, about a quarter of DB time.
A 30-day leaderboard refreshes every 30 minutes now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
66 KiB
title, status, sources, related
| title | status | sources | related | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Enrich Arena — paid comparisons, one-click feedback, and visible waterfalls | shipped |
|
|
Enrich Arena
/enrich-arena and /enrich-arena/leaderboard share a public standalone Vue app, with
Arena | Leaderboard navigation centered in the account header: icon links share a rounded
white pill container, with a softly tinted sage active pill and dark green text. On narrow screens the control occupies a centered
second header row. Links preserve task and input type; leaving Arena saves its
query draft. Leaderboard does not restore or overwrite drafts, resume runs, fetch history or
request quotes. It reads the same cached database snapshot as Arena. The account credit badge
is a button opening /app?from=enrich-arena#billing through topUp, preserving the selected team and Arena draft.
The shared header also links to GitHub ("Open source"), Discord and X using the same icons and
destinations as the people-search landing page. The links remain visible across Arena, Leaderboard
and Benchmark. Account controls stay on one line above 600px; the centered page pill moves
onto its own row at 1800px and below. On mobile, community icons sit beside the brand and
balance; team selection and Sign out are in the account dropdown beside the balance.
Support chat is enabled on these shared pages only after /auth/me establishes a signed-in
user and /meta supplies intercom_app_id. syncIntercom boots once, updates the active team
on team switches or saved-run restoration, and follows /app identity verification: email and
company are sent only with intercom_user_hash; without the hash, chat remains anonymous.
Logout, an expired session, or an account change shuts down the previous Intercom session.
Guests and deployments without an Intercom app ID load no widget; widget failures do not block Arena.
Its visual system follows the treg redesign reference (https://treg-design.vercel.app/#start):
Geist Pixel headings, Google Sans Flex body text, DM Mono for technical values, a cool gray canvas,
white rounded cards with fine borders, black actions and restrained teal status accents. A static
pixel texture echoes the reference background, while the standalone account header stays compact.
The entry screen leads with “Enrich Arena” and a small “Powered by [treg logo] Treg” label beneath the title. The composer keeps its compact, single-column layout and repeatable input rows.
ArenaTaskTabs sits above the inputs in a single compact row with icons and readable action
labels such as “Find work email” and “Enrich person”. A subtle filled state marks the active tab.
The row starts with Find work email, Find phone number, Enrich person and Enrich company, followed
by verification, LinkedIn lookup and the discovery tasks. Every option stays in one horizontally
scrollable row, with full labels and icons, on desktop and mobile. The native scrollbar is hidden;
faded edges with small clickable arrows appear only where more content is available. Scroll and
resize updates keep the arrows in sync, and selected tabs are revealed clear of the arrows. Selecting a task or resizing
brings the active option into view without scrolling the page vertically. Arrow keys and Home/End switch
tasks with roving focus; the input panel is labelled by its selected tab. Clicking the active tab
preserves the query. Add entry, the Using input-type selector and Run share the row below the inputs.
A quiet “or paste rows from a spreadsheet” hint sits immediately to the left of Run.
Visible field labels and the Using input-type selector
remain, with Add entry and spreadsheet paste for batches. Multiple entries share one column-label
row above the aligned inputs; each field retains its own hidden label and entry number for
assistive technology. A single entry keeps its visible labels inside the input row. Discovery adds a short result/batch-limit
note. Fresh Arena visits, new queries and task/input-type switches prefill two editable public
examples from domain.arena.example_inputs, exposed by /arena/tasks. Saved drafts (including
intentionally cleared inputs) and historical results take precedence. No example dispatches
automatically. Vendor selection resets on task switches. Shared column labels stay aligned with
the fields on mobile as well as desktop. Narrow screens keep wider input grids in a local
horizontal scroll region, with 16px editable text and 44px primary touch controls. The mobile
header keeps the brand, community links and signup/credit controls together above the page tabs.
At widths up to 600px, community links use accessible icons without the Open source text; Sign out and team selection live in an account dropdown beside the credit balance
when signed in. Comparison filters use a two-column grid.
Result tables keep their own horizontal scroll region and stats retain the vendor column while
scrolling. Expanded answers become cards on small screens; selected overview rows and table
headers stop pinning at 600px so they cannot cover those answers. The provider queue contains
its positioned accessibility labels, avoiding page-wide overflow. Dialogs scroll within the
dynamic viewport and prevent the page behind them from scrolling.
Waterfall/Battle controls are centered in the fighter card, with Waterfall first and step/crossed-swords icons, VS connectors
for battles and directional step arrows for waterfalls. Battle retains the internal compare mode value. Changing mode updates the query
and invalidates pricing without dispatching; controls are locked during a run. Old result cards
retain their recorded mode when a new query is being configured. Waterfall is the default; explicit choices in a restored draft or
URL remain respected. The header has no bottom rule and the hero uses tighter spacing.
There is no Options section or editable estimate limit. Vendor avatars act as native toggle
buttons: enabled fighters have a check, excluded fighters remain dimmed and clickable. Selection
updates pricing and the saved login draft without dispatching. All services start enabled; task
or input-type changes reset the selection. No enabled services blocks submission. Avatars lock
during execution, and historical sessions restore their recorded vendor set. The existing API
$10 admission ceiling remains; the page no longer carries a hidden user budget from old drafts.
Selecting a task or input type immediately
shows a compact vendor/pricing table, including before login or input completion. /arena/tasks
provides catalog estimates per input variant using verified adapters, canonical derivations and
request pricing (including adapter constants and margin), with no team reads, stored quotes or
upstream calls. A ready team quote replaces these public estimates and supplies the actual
waterfall order and own-key pricing. Matching results replace the preview after dispatch; editing
the query restores its pricing preview while preserving the previous result below it. Results appear below the composer, which stays
visible with its inputs and controls disabled while a run is in progress. After completion, users
can edit the inputs and submit another run. A compact table shows each vendor's bundled logo,
key result fields, status, cost, timing and one-click thumbs up/down actions. Full fields and original
responses expand beneath a row by clicking it or pressing Enter/Space while focused. Row action
buttons act independently without toggling details. Waterfall uses the same table with numbered steps and timing bars;
its stop reason appears as a short footer. Neither mode has a results heading or aggregate-charge
bar. Either thumb records a per-result rating immediately; there is no
selection/submit step, reveal gate, none-useful button or skip button. Work-email rows flag a
returned email domain that differs from the requested company domain (subdomains are accepted),
while preserving the vendor’s answer. This signals a possible mismatch, not automatic invalidity.
Vendor icons are bundled locally from official sites, with source URLs in asset comments.
Original PNG/ICO icons are embedded in SVG wrappers to preserve the shared logo paths; existing
vector marks remain vectors. Context.dev uses its current icon under the stable branddev provider id.
Initials appear only as an image-load fallback.
A compact pixel-fighter lineup sits above pricing and results, using those same logos as heads.
It is idle before dispatch and follows each real attempt: running punches, hits celebrate,
misses fall, errors/timeouts show an alert, and uncalled services remain on the bench. Compare
animates concurrent active attempts; waterfall animates only its active step. A current thumbs-up winner receives a crown. A thumbs-down rating immediately overrides the celebration/crown with the
defeated pose and “Thumbs down” label; changing to thumbs up restores the recorded outcome pose.
One thumbs-up makes that result the winner, with a crown and Winner label, in either mode.
Fastest and Cheapest belts still compare successful, non-downvoted results when there is only
one thumbs-up, so those performance badges may belong to a different vendor than the winner. With multiple
thumbs-up results, Fastest and Cheapest compare only those results, and their metric winners get
crowns. No unrated vendor can outrank a thumbs-up vendor. Changing a rating recomputes the winner
and belts immediately; saved historical comparison evaluations remain intact but do not drive
these current winner indicators. Without thumbs-up votes, completed Battles with at least two
successful, non-downvoted results show Fastest and Cheapest
badges as pixel-edged belts across the fighters’ waists and as compact labels in result rows.
The belts move with the sprite and counter-mirror their text for facing opponents. They compare recorded duration and actual settled charge,
respectively; ties share badges, genuine zero charges qualify, and an unknown metric withholds
that metric's award. Running sessions never show provisional awards. Hits indicate returned data, not a ranking or verified correctness. Terminal
runs stop all looping motion; reduced-motion preferences disable animation. The lineup scrolls
horizontally for larger cohorts and has per-vendor accessible status labels.
Results start directly with the fighter card, with no query-summary row, aggregate progress count
or New query link. Inputs remain editable after completion; Stop sits in the fighter toolbar
only during an active run.
Visitors can select a task, input variant, execution mode, and services before signing in.
Submission checks the existing browser session. Signed-out visitors see Sign up. Successful email
or Google/GitHub authentication opens the existing agent setup modal next, without dispatching a run.
A ten-minute, single-use session-storage marker resumes setup after OAuth; ordinary page loads do not
reopen it. OAuth returns allow only Arena, Leaderboard and Benchmark paths with task/run/team query
parameters, validated both before redirect and at callback. The return cookie holds a Fernet-encrypted
target; callbacks authenticate and decrypt it before repeating the allowlist check. Missing or tampered
cookies fall back to the dashboard. A ten-minute session-storage draft
preserves the query through signup. The top navigation also links to https://treg.to via Treg.
New users can create a team here. Valid inputs for signed-in
users fetch a quote after an 800 ms pause. The compact Run button displays the estimated cost
(waterfall: “Run from” the cheapest selected vendor’s quoted price); per-service prices appear below the input. The page
has no price-confirmation modal. Clicking a current affordable quote dispatches immediately.
An absent or expired quote is first refreshed on the button; login never automatically spends.
Changes to identity, mode, team, budget or services invalidate the displayed quote and discard
late responses. A completed run consumes its quote; a fresh run gets a new quote.
Insufficient-credit buttons open /app#billing with the Arena team selected via the dashboard's
treg-active preference, preserving an unsubmitted draft. A 402 at start uses the same path.
The navigation shows a labeled Team switcher only for users with multiple teams; a single team
is selected automatically without an extra dropdown.
Tasks are work email, person enrichment, company enrichment, phone lookup, email and phone verification, email-to-LinkedIn, people search, people at a company, and similar companies. The catalog's existing contracts and adapters define supported inputs and normalized result fields. Name-based comparisons require both first and last name, validated before quote creation or upstream dispatch; use a LinkedIn URL when that input is unavailable.
BounceBan joins email verification only through its verified standard-single adapter. The adapter
maps result=deliverable to valid, preserves other provider verdicts as status, and treats a missing
result such as a pending verifying body as a routing miss. Public estimates use the fixed $0.004
upstream cost before the configured platform margin. Waterfall and bulk BounceBan tools remain BYOK
only and do not enter Arena.
ZeroBounce also joins email verification through its verified single-validation adapter. It maps
only status=valid to true, preserves the provider's other verdicts as negative answers, and treats
unknown as a miss. Public estimates use the fixed $0.0138 upstream replacement cost before the
configured platform margin. Account reads and the excluded batch/file surface do not enter Arena.
Email inputs require a nonempty mailbox and dotted domain. Malformed domain/LinkedIn URLs,
invalid ports, embedded credentials and non-web schemes return validation errors before pricing
or charging, including malformed bracketed hosts that URL parsing would otherwise reject with an exception.
Each provider contributes one eligible endpoint whose adapter verifies the task contract. Verified
asynchronous submissions participate through the same planner and quote surfaces as synchronous
providers; Arena handles submit, poll and terminal normalization internally. Bulk jobs and
personal-email finders remain excluded from the work-email task.
?capability=people.email.find&mode=waterfall opens a task/mode directly.
Historical vendor insights
Arena shows historical stats only in the existing vendor pricing table, with four columns:
Vendor, Hit rate (Verdict rate for verification), Response time and Price. There is no separate
insights section or highlight card. insightRows matches the snapshot by task, input shape and exact endpoint ID;
previewProviders attaches that evidence without changing quoted order, avatar selection or
team-specific pricing. Missing endpoints show a dash rather than zero coverage. Billing terms
appear beneath the price. A compact About these stats disclosure includes dates, exclusions and
the aggregate download.
The Arena table includes horizontal bars for rate and response time beside their values. Rate uses a fixed zero-to-100% scale; durations share a zero-based scale up to the largest known median, so shorter is faster. Missing evidence has a dashed track rather than a zero bar. The vendor/price columns remain available. Leaderboard has one centered use-case heading using the same pixel font, weight and responsive sizing as the Arena heading, with Setup independently aligned to its right (stacked on small screens), a centered content-width horizontal use-case tab container using the same white rounded shell and sage active pills as People Search Bench, a left filter rail for metric and input type, and one chart canvas, inspired by Design Arena. The rail stacks above the chart on mobile. It offers hit-rate bars (default), response-time bars, horizontal price rows (cheapest first) and price versus hit-rate scatter. Price bars use current per-entry catalogue estimates and include priced vendors without historical samples; missing or invalid prices are omitted and genuine zero prices remain visible. Each price row shows the vendor logo, proportional bar and exact USD value. All vendors appear in the vertical list so expensive vendors cannot be hidden off-screen while determining the scale. Only enrichment tasks covered by the aggregate pipeline appear there; discovery tasks remain in Arena.
chartRows uses the selected Arena vendor cohort or the full public catalogue cohort on
Leaderboard, with the same sample thresholds. Rate bars sort
descending on a fixed zero-to-100% axis; response-time bars sort ascending with a zero-based axis.
Bar columns constrain their inner grid width independently of label length. Bars share a width
cap, vendor names wrap at word boundaries, and large cohorts scroll horizontally rather than
squeezing labels into adjacent columns.
Bar charts omit vendors without usable evidence for the selected metric; genuine zero results remain visible. The table retains every selected vendor. chartPoints includes only vendors
with both a usable rate and a current estimated price (including a true zero price). Leaderboard
uses public per-entry catalogue estimates independent of team quotes, batch size and vendor
selection. Charts follow task and input selections without dispatching calls. Vendor buttons and the scatter legend support pointer, keyboard and touch selection, with a
shared rate/time/price detail line and no lookup counts. The scatter uses provider logos in white markers (a provider initial if its logo fails),
retains accessible labels and selection details, and omits unavailable pairs. The chart explains that these are observational results, not a controlled benchmark;
verification rates measure returned verdicts, not accuracy. All geometry is derived in the browser
from the already-loaded aggregate, with no extra requests, chart dependency or embedded metrics.
insightRate uses only unique-request coverage; lookup counts are omitted from the table.
Fewer than 20 unique lookups withhold a rate; small-sample labels are hidden.
Verification shows the share returning a verdict, not an accuracy percentage. Response time
is the median of successful calls (including repeats), displayed only with at least 20 hits and
a known timing aggregate. Current prices remain estimates for the selected entries, and own-key
prices retain their no-treg-charge label. Historical stats do not alter dispatch or result awards.
GET /arena/insights first reads the current version's persisted database aggregate by primary key, with
updated_at, source window, actual first/last observation dates, collection status and only public aggregate fields. The disclosure shows recorded dates when observations exist, so a sparse or historical sample does not appear to span the whole rolling window. No production
metrics ship in assets or fixtures. If that version has not published yet, one bounded snapshot query
serves the latest completed, compatible publication from a previous version, preserving its source
window and update timestamp. Ordering uses the payload's publication timestamp because the worker
clears the cursor's updated_at during refresh. The current version takes precedence as soon as it
publishes, including a completed snapshot with no rows. A fresh database returns warming with no rows. The UI polls
every two minutes while visible, preserves the last successful values after a refresh error, and
shows the last update time. Prices still come from the catalog and team quote.
application.arena_insights.drain is the collector, run by the treg-worker arena insights cron
(every two minutes; --max-seconds bounds a pass and the next run resumes from the cursor). Once
caught up it re-aggregates the 30-day window at most every REFRESH_SECONDS (30 minutes): each
aggregate scans the whole window on the primary, so the cron cadence is not the refresh cadence. It no
longer runs inside the web processes: as a lifespan coroutine, every web process (and every extra
instance during a deploy) contended for the cursor row and each walked callrecord on the
database the money path depends on. In the worker process it uses the API pool, the only one open
there. The rewind that
revisits ten minutes of evidence is constrained to the Arena's endpoints so it rides
ix_callrecord_endpoint_id_created_at instead of walking the table.
It reads a bounded batch of 100 audit records and their exact archive key/content pointers, closes
its metadata session, then resolves bodies through archive_bodies.read with path arena and the
result read switch. Object reads have concurrency eight and retain the existing decode-size cap.
Invalid compressed DB bodies remain unresolved per record rather than stopping collection of
other evidence, including when decompression occurs in the common reader's DB fallback.
The worker uses bootstrap.archive_object_store for the client lifecycle and drains read telemetry
before exiting. It reacquires the cursor row lock and validates cursor, cutoff and completion state
before publishing; evidence from a superseded batch is discarded. Thus no cursor lock or DB
connection is held across R2 I/O. It reclassifies responses with current required-field rules and upserts anonymous
ArenaObservation facts. It never calls vendors or trusts CallRecord.hit. No money writes or proxy
changes are involved. Evidence lookup deduplicates key/content pairs and finds each pair's newest
matching snapshot through the existing (key_id, version) index. This avoids repeatedly scanning
the global content index for identical responses shared by many requests. Missing exact content
remains unresolved; newer different answers never substitute for historical evidence. The values
CTE requires SQLAlchemy 2.0.42 or newer, reflected in the server dependency floor.
ArenaInsightState serializes the cursor across workers and stores the aggregate;
partial initial history is withheld until the first pass completes; a previous completed publication
remains visible during a version-triggered rebuild. Steady state refreshes about every two
minutes (plus collection time), allowing one minute for audit/archive writes and revisiting ten minutes
of recent evidence. The first backfill may take longer. Evidence that arrives later than this revisit
window remains unresolved until a version-triggered rebuild; lossy audit cannot establish complete traffic.
The source window is 30 days, with old facts pruned. Rule, adapter or contract changes produce a new
version/cursor and rebuild from available evidence. PostgreSQL computes medians and unique-request
counts in SQL; local SQLite uses an exact Python median. The collector owns writes to the two new
tables, created by Alembic revision 0028. Run python -m treg upgrade before restarting the app.
A refresh failure retains the last aggregate and its original timestamp; partial collection is never
presented as a complete window. The API does not expose identities, hashes, organization IDs or bodies.
Coverage is structurally usable results divided by usable results plus genuine misses, using the latest decided observation per endpoint/input/request hash. Wrong requests, rate limits, balances, access/service failures, refusals and pending results are excluded. Ambiguous errors, unavailable bodies and missing request hashes remain unresolved. Own-key, overflow and cached scopes are omitted. Only inputs attributable to the selected shape appear in that row. Response time includes repeats but requires 20 successful calls with recorded timing. Different cohorts and waterfall positions prevent controlled rankings, and returned fields are not independently verified.
Multiple entries in the same composer
The heading's “Setup treg in” button shows Claude Code, Codex, OpenClaw and Hermes logos plus
the count of other choices. It opens a native dialog using the same AgentPicker and
SetupInstructions components as the dashboard welcome modal: their source is
frontend/src/agent-setup/, compiled for this page into /agent-setup.js (window.TregAgentSetup).
The instruction
heading sits above the prompt card; the card is a flat panel with Copy floating in a right gutter
on desktop and above the command on phones. Both stylesheets style that one shape, and the
component carries no layout of its own, so a change to its markup is checked on both surfaces
(the dashboard defines .agent-setup-instructions .text-button for "Show key"). The remembered
treg-agent choice, expanded agent list and Grok Bot plugin step are shared. The setup text points
to this deployment's /llms.txt. Continuing as a signed-in team member fetches /auth/cli-token
for the active team; the token is masked by default, copied only on click, never persisted by the
modal, and cleared when it closes. Stale responses cannot restore a closed or wrong-team token.
Next opens the shared third “Try it out” step: four copyable example prompts, the waiting-for-agent
message, grouped OAuth provider links, a secondary Browse all catalog action, and a primary Done
button that closes setup and keeps the user in Arena. Copying an example
only writes its prompt to the clipboard; provider links open the main app’s provider page without
starting OAuth. The example definitions and TryItOut component are shared with dashboard onboarding.
Signed-in visitors without a team first name and create their team inside the setup modal, matching
the dashboard welcome flow. The agent step then fetches a token for that team; authenticated users
never silently fall back to guest instructions. Anonymous visitors still receive the setup line and
agent-guided login instructions. Opening setup does not start an Arena run or change its inputs/history.
The composer starts with two example rows. Add entry and per-row remove controls expand that same input to at most 50 enrichment/verification entries or 10 discovery queries, sharing the task, input variant, mode and vendor selection. Spreadsheet paste accepts TSV/CSV, quoted fields and matching column headers; it replaces the focused row and inserts the remaining pasted rows beside it, preserving other entries. Overflow and malformed column shapes leave the input unchanged. Blank/incomplete rows and duplicate identities block submission; the server repeats validation after normalizing domains, names and LinkedIn URLs. Only three input rows appear initially, with Show all/Show fewer for longer lists. Login drafts and history restore all entries. Editing any entry invalidates the quote, including late responses.
POST /arena/plans accepts either the original identity or an identities list, never both.
The server requires one input shape and a common provider cohort across the list. Every entry is
planned through the existing routing planner, with its own frozen adapter requests and estimates.
The encrypted payload carries identities plus the first-entry identity compatibility field;
each attempt includes an entry_index. Existing single-entry records default to index zero.
Raw response snapshots share a 2 MB per-run allowance, split equally across attempts and capped
at the existing 256 KB per response. A response exceeding its raw-storage share still undergoes
normal classification and field extraction; raw_omitted explains its absent source snapshot
in expanded details. This bounds repeated encrypted writes and polling payloads for large batches.
The quote aggregates estimates per provider and reports entry_count; history includes the count,
while full run reads/export include all identities and individual results. No schema change is needed.
Batch Battle calls each selected vendor for every entry with a shared four-leg concurrency limit. Batch Waterfall walks each entry's ascending-price steps, completing the first step across the list before advancing unresolved entries to their next step. It remains serial at dispatch; each entry has independent hit/error-fallback state, while the run spending ceiling is shared. Waterfall admission and the Run-from button use the sum of the cheapest first steps across all entries. Battle admission uses the sum of all attempted estimates. There is no implicit partial purchase when the batch is unaffordable. Runtime balance/policy refusals stop the waterfall and leave untouched attempts available for the explicit, priced per-cell Try action. Manual plans use the identity belonging to that cell, retaining all other results and feedback.
Batch runs have a deadline of 240 seconds per entry, capped at one hour; each leg retains its 90-second limit. One batch occupies one active-run slot. State persists after every attempt, so refresh/history resumes observation without spending. Cancellation and process loss retain completed work and never retry dispatched calls. Uncalled cells can be tried individually, including while another additional call is running; there is no automatic batch resume or automatic retry after an ambiguous outcome.
For multiple entries, fighters summarize each vendor. Vendor scoreboard rows with attempts expand in place into that vendor's
attempted entries (including misses/errors, excluding queued, skipped and uncalled entries).
The shared result table labels these rows by entry and keeps per-result details and feedback.
Only one vendor or entry expansion is open at a time, avoiding duplicate feedback forms and IDs.
An entry-by-vendor matrix replaces the single-entry table. Selecting a matrix row/result expands a nested vendor table immediately beneath
that entry; selecting it again collapses it. The shared ArenaResultTable component renders both
single-entry and nested details. Overview cells show only attempted results; queued, skipped and
uncalled cells remain empty. Thumbs, Try and optional report forms appear only inside the detailed
table, avoiding duplicate controls. The overview entry column stays pinned during horizontal scrolling.
The run cost summary appears above the entries controls (or the single-entry result table).
It uses the server's settled run total, which includes verification and unsuccessful calls.
Average per result divides that total by distinct input entries with a found, non-rejected result;
multiple vendors finding the same entry count once. Discovery labels this average per matched query,
not per returned contact. Pending charges withhold the average, and runs with no found entries show a dash.
The summary is independent of the entry filter and is the completion auto-scroll target.
A repeated Setup treg in agents button sits to its right and wraps beneath on narrow screens,
opening the same onboarding modal as the page header.
When a newly started or resumed running battle/waterfall finishes, Arena scrolls once to the
run cost summary above the entries matrix (or the single-entry results table) after Vue renders the result.
Completed history views and subsequent manual provider/verification calls do not trigger another
scroll. Reduced-motion users get an immediate scroll. Results grow to full height in the page,
without an internal vertical scrollbar. The stickyHeader
directive keeps real table headers aligned at the viewport top during page scrolling. The expanded
entry row stays beneath the overview header until its detail section ends; the nested vendor header
sits below both, preserving the entry name while reviewing a long vendor list. The directive observes
table resizing, responds to entry changes, and removes its listeners on unmount.
The matrix keeps readable vendor column widths and scrolls horizontally for larger vendor sets.
The nested detail panel stays aligned to the visible scroll container with a container-relative
width and sticky left offset; its columns do not scroll with the overview. Nested details use fixed
column proportions with stacked vendor rows in narrow containers. Found uses a green check badge;
No match uses a red cross badge so outcomes differ by both color and symbol.
Filters show all, found, unresolved, or differing answers. Immediate per-result thumbs and
optional rejection details use the same durable rating/report endpoints. Raw hit status and found
percentages remain unchanged by ratings; kept counts hits without a thumbs-down rejection.
Batch thumbs up approve their cell; one upvote does not crown a vendor across the whole list.
Completed Battles with a complete vendor cohort award Coverage to the largest kept count, Cheapest to total settled charges (including misses) divided by kept count, and Fastest to the median recorded duration of kept results. Ties share awards; only Coverage receives crowns. Vendors with zero kept results cannot win. Missing charges/timings withhold the respective metric; uncalled/interrupted/cancelled outcomes withhold batch awards. Running batches show progress without provisional awards. Waterfall displays found/attempted counts and contributions, with no comparative crowns or belts because later vendors receive only the unresolved subset. Found is explicitly not verified accuracy, and speed excludes queue time. Single-entry winner rules remain unchanged.
Discovery queries
Discover contains Find people (people.search) and Find similar companies (companies.similar).
Find people offers four input shapes, ordered by real demand: job title plus company domain
(the default), company domain alone, a free-text search description, or job title plus a
recognized ISO country code. The former "People at a company" task (people.company.search)
was merged into it on 2026-09-14; runs saved under the old id still resolve through
domain.arena.catalog_capability.
Similar-company discovery accepts a seed domain. These are direct catalog queries, with no
agent harness, model-written plan, automatic pagination or implicit follow-up enrichment.
Discovery shares the existing quote, admission, cancellation, feedback and batch execution paths. Each batch query returns its own list per vendor, with at most ten normalized matches displayed. People search uses the contract's bounded default limit; CompanyEnrich similar search fixes pageSize to ten, while Tomba's similar endpoint has a fixed-price response without a limit input. Preview prices are computed from those exact requests. Both public previews and private plans exclude adapters that drop requested identity constraints or contract filters, including the result limit. Unknown country codes are rejected before planning rather than silently omitted.
Returned lists have bounded scalar names, roles, company, location, contact and company fields. URLs pass the existing safe-field normalizer; raw responses retain the existing separate storage budget. The displayed count describes the normalized list, not an upstream total across pages. Expanded vendor rows display the individual matches; batch disagreements compare the returned lists rather than counts alone. Batch coverage and cost-per-kept use queries as their denominator, not the number or relevance of contacts. Waterfall still stops on the first structural hit, which for discovery means a usable nonempty list, not a relevance judgment.
Discovery previews show vendor prices without historical enrichment hit-rate charts. Discovery endpoints are excluded from the enrichment insights aggregation until an appropriate discovery metric exists; this also preserves the existing enrichment metric snapshot and fingerprint.
Execution and costs
safe_output normalizes the Arena fields before structural classification and again on presentation
for saved sessions. Vendor masking booleans cannot become text fields such as a name or location;
only explicit verification fields retain booleans. Numeric metrics and flexible employee/founding
values retain their types. Domains become hostnames, website values gain a web scheme when absent,
and LinkedIn handles/schemeless URLs become canonical person or company URLs. Malformed or non-web
addresses are unavailable. Original response snapshots, recorded charges, and historical outcomes
remain intact. Masked required fields in new responses cannot qualify as structural hits.
Person and email-to-LinkedIn results display an identity notice when the returned name/profile differs
from the supplied identity or vendors disagree within the same entry. The notice does not choose a
correct vendor, change votes, or rewrite the answer. A vendor HTTP 402 displays an exhausted-credit
or lookup-allowance message recommending another vendor; it is distinct from a team-balance refusal
and does not direct the user to top up team credits. upstream_status preserves this distinction in
both live reads and saved sessions.
POST /arena/plans validates input, applies the normal team's routing eligibility and freezes
the exact adapter requests, endpoint/adapter hashes, identity, ordering, estimates and budget in
an encrypted ArenaRun. A quote expires after five minutes. Creating it does not reserve credits.
Battle requires credit for the sum of estimates. Waterfall sorts selected vendors by their exact
quoted price, starts with credit for only the cheapest vendor (including an exact balance match),
and displays “Run from” that price. Own-key calls have a zero treg price. Each subsequent attempt
still passes the normal runtime balance check and the remaining run-budget check. These are admission limits at quoted prices, not a guarantee
against a higher provider-reported final charge. The UI discloses that difference.
POST /arena/runs/{id}/start rechecks catalog hashes and available credits. A conditional database
update claims the run once, including across web workers. Duplicate starts never dispatch again.
Execution is owned by an in-process task: compare has four concurrent legs per run; waterfall
executes serially. Each leg captures current membership/policy and calls the existing
application.call.service.execute_call against the direct endpoint. The ordinary call runtime
owns pricing, credential priority, authorization, reserves, settlement and cancellation cleanup.
Arena never writes balances or holds. Own keys remain unmetered by treg. Aggregator overflow is
disabled for these comparisons, and archive lookup is bypassed so runs measure fresh calls.
Database sessions are short and closed before upstream requests. Attempt state and call references persist before dispatch. Synchronous legs have a 90-second deadline; an async leg may poll within the remaining 240-second per-entry run deadline. No database session remains open during polling waits or upstream I/O. Cancellation is polled between writes and interrupts in-flight tasks through the normal call cleanup. Shutdown drains Arena owners before closing the shared HTTP client. A process-lost run becomes interrupted after its persisted deadline; it is never automatically retried. Unknown charges remain unknown until a durable ledger settlement/release can resolve them on a later read. Actual run starts are limited to 100 per user/hour, with an admission check for three active runs. Automatic price previews do not count toward that limit. Quote creation retires the oldest unused quotes to retain at most 100 per user, preserving completed/running sessions. Start admission serializes on the user row across drafts and teams; hitting the execution limit still allows pricing.
Run completion signals the cancellation poll to stop and waits for its current read/session to
close before the final save. It never cancels that poll inside database I/O: under SQLite's
rollback journal, cancellation during cursor execution can retain a read lock, blocking the
final commit and subsequent test schema resets. test_run_finishes_while_cancel_poll_is_reading
forces that overlap through a real Arena run; repeated xdist tests with CPU pressure cover the
original intermittent failure. Audit/archive drains alone cannot release this reader's lock.
Waterfall uses ascending quoted prices, retaining planner order for ties, and the bounded error fallback policy. It stops at the first structural hit: the adapter supplies the contract's required fields. Found work email does not mean verified deliverability; phone found does not mean a live line. A negative mailbox verification verdict is a successful answer. An async attempt that remains in progress, or whose foreground poll cannot prove a declared terminal state, is saved and shown as pending with its reservation and call reference. Pending stops only that entry's Waterfall; Battle may finish other explicitly selected providers concurrently. Only a declared terminal provider status permits the entry to advance. Terminal attempts show the actual settled charge, not the maximum reservation. Each step shows queued/running, pending, found/no match, error/timeout, skipped/not attempted, timing, charge, and the reason for stopping or skipping.
Additional vendor calls and issue reports
Each uncalled (not_attempted or skipped) result has a priced Try action. Completed attempts
never expose a rerun action, including errors, timeouts and interrupted manual attempts.
POST /arena/runs/{id}/attempts/{attempt_id}/plan rechecks current routing access for that exact
endpoint and computes the real adapter-request estimate. It stores a five-minute quote in the
run's encrypted payload without reserving credits or calling a vendor. The UI dispatches only
at or below the displayed estimate; an increase requires another click at the new price.
Known insufficient credit changes the action to Top up without dispatching. A fresh quote,
start admission and ordinary runtime reservation independently check paid-call funds; zero-cost
and own-key calls remain available even with a nonpositive balance. Insufficient balance uses
the existing team top-up flow.
The matching /start locks the owned run, checks the quote, catalog hashes, credit admission and
active-run cap, then claims the uncalled attempt once. It reopens a completed session or adds
an independent worker to a running session using the ordinary call runtime. Up to four manual
attempts may run at once. Only the clicked Try action is pending during its plan/start requests;
other uncalled vendors remain available. Queued automatic steps and dispatched calls cannot
be claimed again. Cancellation blocks new claims until the run has stopped. This explicit extra call is admitted separately from the
original waterfall budget. Original identity, earlier results, reports, and call references stay
intact. The result carries manual: true; timing continues after the previous attempts. Duplicate
starts, expired quotes and already-attempted vendors cannot dispatch. Every worker stays registered for shutdown. Persistence locks the run and merges only the
worker’s own attempts, preserving concurrent quotes, results and feedback; a run becomes terminal
only after all active attempts finish. Manual cancellation and process loss use the same
settlement and interruption handling as initial runs.
Thumbs up and thumbs down save immediately through
POST /arena/runs/{id}/attempts/{attempt_id}/rating, for returned results in either mode.
The selected thumb updates optimistically; failed saves restore the prior selection and show an error.
Thumbs up has no follow-up form. Thumbs down opens optional reason and note fields after beginning
the independent rating save; closing them keeps the rating. Either reason or note may be supplied,
and a later click on thumbs down reopens the details. The first submitted details are idempotent
through /report; they never overwrite an upvote from another tab. Historical reports remain intact.
Ratings store the current up/down value, creation/update timestamps and attributed context in the attempt's encrypted payload. Repeating the same rating is idempotent; choosing the other thumb updates it. Ratings and optional reports are private to the session owner/team, survive additional calls, and travel with history/export under the same 30-day retention. Terminal payload reads, ratings, reports and manual claims serialize on the run row so writes cannot erase one another. Feedback never changes the vendor's output, hit classification or charges.
Visible vendors and durable feedback
GET /arena/runs/{id} returns private, attributed snapshots, including existing runs. The UI polls
every 1.5 seconds while running and shows each vendor's queued, running or completed result as it
arrives. Comparison rows retain their frozen randomized display order; waterfall steps retain
execution order. Provider, endpoint, raw response, cost and timing are visible without voting.
Viewing a result never creates an evaluation or makes a paid call.
The page collects per-result thumbs in both modes, including individual batch cells. The previous
POST /arena/runs/{id}/evaluations API remains available for compatibility, including winner,
tie/none/cannot-judge/skip kinds and POST .../reveal. Its first evaluation remains immutable under
a run-row lock and unique run-id constraint. New thumbs do not create or rewrite those historical
comparison votes.
Version 2 evaluations carry feedback_context: attributed, the frozen cohort fingerprint,
exposed candidate ids/order, outcome and adapter versions. This distinguishes vendor-visible
preferences from historical blind votes. Existing evaluation rows remain immutable. The legacy
revealed_at column records the evaluation event; it no longer controls result visibility or
eligibility to submit the first vote. No schema change is required for this presentation change.
History appears as a left-hand session list anchored near the viewport edge, loaded for the
active team and hidden when empty. The Arena column is centered independently in the remaining
space, with a capped width on larger screens. The history list remains scrollable with its native
scrollbar hidden, including the horizontal list on mobile.
The list starts with 30 runs. Load more appends older runs in groups of 30 and disappears at the
end of the retained history. GET /arena/runs keeps its array response and default limit of 30;
optional limit (1–100) and before (an owned, retained run ID) paginate by descending creation
time and ID. The UI requests one extra row to detect the next page. New runs cannot shift older
page boundaries, repeated clicks share the in-flight request, and stale team/refresh responses
cannot append to the current list. Reading additional history makes no vendor calls.
Selecting a session restores its inputs, mode and results with a read; New query clears the
current inputs/results while keeping saved sessions. The list refreshes after dispatch and
completion, ignores responses from a previously selected team, and clears on logout/team change.
Session switching is disabled during an active run. On narrow screens it becomes a horizontally
scrollable strip above the composer. There is no history modal. History/export do not invoke services. The public leaderboard displays aggregate call stats, not feedback rankings. There is no
feedback-reporting dashboard; durable evaluations are the source for later feedback analysis.
Privacy and operation
Runs and evaluations belong to both their creator and active team. Another team member cannot read the creator's runs. Inputs, response snapshots and comments are encrypted with the existing Fernet key. Results are bounded to 256 KB per provider. Access expires after 30 days; a bounded lazy sweep deletes expired runs and evaluations on history/quote requests. Team deletion removes evaluations before runs. Normal ledger retention remains unchanged.
The page opts out of PostHog autocapture and session recording through sitetrack.js's
data-private-page flag. Inputs stay out of URLs. Mutating endpoints use the existing same-origin
guard. All Arena routes belong to the control role (and default all role), including interactive
paid execution; the dataplane's /call/ contract is unchanged.
Alembic revision 0027 creates arenarun and arenaevaluation. Run python -m treg upgrade
before serving the new release. Tests cover auth/private access, aggregate admission, direct billing,
own keys, cancellation, duplicate start/vote, attributed progress/results and pre-charge name validation, waterfall progression and OAuth return.
Conversion tracking
TregTracking in sitetrack.js connects anonymous pageviews to the authenticated email and
active team group after loadIdentity, including OAuth returns and team creation. Team switches
clear the previous group; account changes clear the previous identified analytics session. The
page still disables autocapture and recording. Explicit events contain product metadata only:
arena_page_viewed(immediately at mount, before data requests, once per page,surface=arena|leaderboard|benchmark) andarena_signup_openedare browser events.entry_surfaceis the first observed product surface from the 90-day first-party cookie, distinct from UTMs/referrer; it is not historical attribution for visits before this tracking shipped.signup_completedis emitted by email OTP and GitHub/Google provisioning after commit and only for a new User. Returning sign-ins and failed proofs do not count.signup_methodand the allowlisted entry surface are included. The event uses the same email identity as calls.arena_run_startedis emitted after a successful database claim, not for quotes or duplicate start requests.arena_run_completedis emitted after saving the initial run's terminal state. Both include run ID, capability, mode, entry count and team; completion includessuccessful_call(at least one hit or evaluated miss) andreturned_data(at least one hit). Manual Try and verification continue to emit ordinarytool_calledevents withclient=enrich-arenawithout creating another logical-run event. Completion after a process crash is not reconstructed.arena_topup_clickedrecords the link click. The app carriescheckout_source=arenato both top-up entry points even if SPA navigation removes the query.topup_startedand the creditedtopup_completedinclude both first entry and checkout source. Stripe Session and PaymentIntent metadata carry both so either webhook order works; the top-up ledger metadata persists them.
For an Arena visitor funnel, count unique people with arena_page_viewed filtered to
surface=arena, then signup_completed, then tool_called filtered to client=enrich-arena
(and outcome=ok for successful calls). Use an ordered conversion window, e.g. 30 days; report
returning-user activation separately from new-signup activation. Do not count child calls as users
or batches as multiple runs. To include a teammate paying, join an activated person's run/call
team to subsequent topup_completed events and deduplicate people/teams at the intended grain;
a person-only payment funnel misses those conversions. Count the first paid top-up per team for
new-payer conversion, not repeat purchases or checkout redirects. Acquisition and checkout-source
breakdowns answer different questions and should stay separate.
Events remain best-effort PostHog analytics (disabled without a configured key); the existing ledger is the durable payment source. No schema migration or historical backfill is part of this tracking change. Live dashboard configuration and production event delivery must be checked after deployment. Tests exercise fresh-vs-returning auth, batch deduplication, both webhook orders, identity/group switching and exclusion of enrichment inputs from event properties.
Optional verification after contact lookup
Find work email and Find phone number enable verification by default for new queries. The switch choice is persisted in the draft and bound into the server quote.
Restoring an explicit draft opt-out or historical run preserves its recorded setting. New email checks,
automatic and table-triggered, call treg.people.email.verify through the ordinary routed runtime.
The quoted ceiling sums accessible verifier estimates (own keys cost zero), with candidates preferred
in price order. The frozen provider set, catalog hashes and route max-cost header bound execution;
newly added providers cannot silently enter an existing quote. The router falls back on unavailable
providers and missing verdicts, subject to its existing error-fallback limit, and stops on completed
valid, invalid or risky verdicts. Every child uses normal credit reservation and settlement.
Phone checks remain a direct Tomba call because there is only one integrated phone verifier.
Battle quotes include the verification ceiling for every vendor/entry; lookup waterfall quotes
include one ceiling per entry. Lookup misses incur no verification call. The original found contact
is retained alongside the actual serving verifier, tried-provider trace and total verification charge.
Saved quotes retain their original policy; obtain a fresh quote to use the email waterfall.
Invalid and catch-all verdicts are completed checks, not failed lookups. Waterfall still stops
at the first structural lookup hit; verification does not silently start another lookup.
An unchecked found result has a priced Verify email/phone action in its table row. Verification plan/start routes are owned, same-origin, five-minute quotes; they read the contact from the encrypted stored result. They check current access, catalog hashes and team credit before claiming a nested attempt exactly once. Per-row UI state allows independent checks without blocking other providers. Automatic checks, explicit checks and extra Try lookups use the normal reserve/settle/release call runtime. Try admission and its displayed total include automatic verification when enabled. Nested charges are included in row/run totals; cancellation, deadline and process-loss recovery preserve the lookup and never retry paid work automatically.
Verify phone number is also a standalone batch-capable task using Tomba's existing phone-validator
endpoint. The default input requires an international number with + and country calling code;
spacing and punctuation are normalized. Requests can alternatively supply a national-format
number with the optional ISO-2 country_code. verification_identity carries a lookup's reported
country context into automatic and manual verification, and the Tomba adapter forwards it as a
query parameter. QuickEnrich preserves its reported data.country_code in normalized output.
An explicit international calling code takes precedence over that country context. Missing or
unusable context for a national number produces a specific explanation without making or charging
a verification call; no default country is guessed. Pre-dispatch failures carry not_started so
the table shows “Verification not run” with the reason and does not attribute an unmade call to
the planned provider. It returns numbering-plan validity, country, line type and carrier when supplied.
It does not establish that the line is live or belongs to the intended person. Arena can plan
a single-provider task through the existing candidate planner; public synthetic routes still
require two verified adapters. No public single-provider routed endpoint is added.
An explicit invalid/undeliverable email verdict automatically creates an incorrect_data report
on the original lookup, for both automatic and table-triggered verification. This happens in
the locked backend result merge, so it works without an open browser and is idempotent across
worker saves. Reports record automated_verification provenance and the verification attempt ID;
the UI labels them “Invalid email · automatically reported”. Existing reports and human ratings
are preserved. Catch-all, unknown, risky, failed/cancelled checks and phone verdicts never trigger
this email issue rule. The original contact, lookup outcome and billing remain unchanged.
Verification cost annotations sit below the total in the Cost column, with a wrapping label and a separate amount line. Cost/Time column sizing keeps the annotation inside its own cell, including nested result tables and the existing mobile card layout.
Arena migrations are ordered after main’s 0026 (call reviews): 0027 (runs and evaluations), 0028 (rolling insights), and 0029
(published verification aggregates). Public snapshot reads use the API pool; the incremental
collector runs in the treg-worker process and draws on no web-process pool. Email verification adapters join the existing
task through catalog-driven discovery.
Email-verification result rows use emailVerdict and outcomeClass to show “Verdict: Valid”
(green), “Verdict: Invalid” (red), or “Verdict: Risky” (amber), instead of a green returned-answer
badge. Catch-all/accept-all and explicit risky statuses take precedence over the adapter's
valid boolean. Unknown, unverified and unrecognized statuses remain neutral “Unknown”; they
are not displayed as invalid simply because the adapter projects valid: false. Original
provider status is retained in the result and expanded data. Nested email verification uses
the same verdict colors and keeps the original status in its tooltip. This display grouping
does not change call outcomes, billing, waterfall behavior or automated issue-report rules.
The optional verification control is an inline “Email verification” / “Phone verification” switch beside the input selector, with a green enabled track and no surrounding pill. The footer wraps its controls on narrow screens. Extra per-found-result pricing lives in a hover/focus tooltip, using the current quote when available and the catalog minimum otherwise. The tooltip explains that verification is included in the run estimate; Escape dismisses it. The native checkbox retains switch semantics, keyboard operation and the existing disabled rules.
People Search Bench
/enrich-arena/people-search-bench is the third top navigation destination, labelled Benchmark. It shares the Arena
shell and account/setup controls. Its read-only startup loads the existing same-origin
/people-search landing page once and uses ArenaBench.parseDocument to extract its published
category scores. Scores are not copied into a second dataset. Incomplete categories, unknown
systems or nonnumeric/out-of-range scores show a retry/source-link error instead of partial charts.
It never restores query drafts, resumes runs, requests quotes or polls live call insights.
A selected-category horizontal chart sorts complete search systems by published score. Four small charts compare Recruiting, B2B prospecting, Deterministic and Influencer on a common 0–100 scale, with system logos and text values. Source links attribute the landing snapshot and the original LessieAI benchmark. The original repository's current figures differ from the landing; the page discloses that difference and does not manufacture an overall score or describe these numbers as individual vendor hit rates or email-verification accuracy. The new charts update when the existing landing source changes. No paid benchmark execution is triggered by viewing them.
Published verification pilot
application.arena_verification_insights validates and publishes aggregate-only pilot data in
ArenaVerificationSnapshot (revision 0029). GET /arena/insights attaches the latest publication
as verification alongside rolling call stats. Both reads are bounded snapshot queries; rendering
the table or Leaderboard never scans evidence or calls a verifier. The rolling worker cannot overwrite
a publication. Reimporting an identical run is a no-op; different contents under the same run ID fail.
The Arena table adds Email validity rate for email lookup. The Leaderboard
uses the same label. Rows match task, endpoint and input type. Validity is the share of sampled
distinct returned emails labelled valid by both verifiers, divided by completed checks. It does not
multiply by lookup hit rate or require a historical baseline. This is observed verifier agreement
within the sample, not guaranteed delivery or ownership. The entire column header opens an explicit tooltip explaining the sampled denominator on hover;
its text button also supports focus, tap and Escape. The tooltip is rendered outside the scrolling
table to avoid clipping. No sample-status sublabels are shown.
Row hover details identify the verifier providers and check date; the chart source shows the sample period. Fewer than 20 completed checks
are withheld; small-sample labels are hidden. Risky, unknown and conflicting verdicts stay
in the denominator but do not count as valid; unfinished checks are excluded. A genuine zero is
displayed and missing values have no chart bars. Phone lookup uses a separate Phone format validity
metric: the percentage of sampled returned numbers passing format checks among completed checks.
The table hides this column when none of the displayed vendors has a published numeric rate.
The header tooltip explains that this does not confirm a live line, deliverability or ownership.
The API publishes this only as format_validity_rate for phone_format rows with at least 20
completed checks; it never promotes phone format checks into email validity or verified hit rate.
The publication preserves the legacy rate projection
for older consumers and adds explicit checked_n and validity_rate fields; the UI only consumes
validity_rate for email and format_validity_rate for phone, so older snapshots cannot
accidentally display the lookup projection as validity.
Publication data stays outside the checkout. To populate a migrated deployment, pass the private
aggregate JSON to scripts/import_arena_verification.py with its configured TREG_DATABASE_URL;
--check validates without writing. The importer accepts only schema-whitelisted aggregates,
source dates, endpoint/input identifiers, verifier names and counts; raw contact fields are rejected.
It does not import private evidence or run paid verification. The same aggregate file can seed local
and production databases after their migrations. Publishing a new run updates the public snapshot
on subsequent reads; the normal frontend refresh picks it up.
Missing or insufficient historical samples display a dash in the vendor table without a “No sample” or “Insufficient sample” label; small-sample labels are also hidden.
Request a vendor
Both draft and result fighter lineups end with a small, muted + button labelled Request a vendor.
ArenaFighters emits request-vendor; the Arena opens a native dialog using the dashboard's
existing Request a tool form fields and submission flow: what's missing, optional details,
and optional contact when signed out. submitVendorRequest posts to the existing open,
rate-limited /tool-requests endpoint with source: web and a public task identifier as query
context. It never copies enrichment inputs, requires credits or starts a run. Pending submissions
disable repeat sends; errors preserve the draft for retry and success shows a compact Request sent
confirmation. Before and after submission, a vendor self-serve section offers the published
https://treg.to/vendor-listing.md agent prompt and a copy button, inviting API owners to open a PR.
Clipboard failures keep the prompt selectable and show an explicit fallback message. Native dialog focus handling supports Escape and returns focus to the trigger on close.
Task navigation and saved-result URLs
Task and input-type changes push the selected capability/variant into the current page URL, clear the previous result and save the draft. Switching between Arena and Leaderboard carries the current selection. Browser Back/Forward reloads that URL, so its state is reapplied consistently. Bare Arena visits can restore a recent draft, but never silently reopen a session from the legacy active-run cache. Explicit run links also bypass pending drafts and never dispatch lookups.
Selecting history or starting a run sets /enrich-arena?run=<id>&team=<slug>. That URL loads
the saved result through the existing authenticated, owner-scoped GET /arena/runs/{id}.
The selected team must be in the signed-in user's memberships. Signed-out visitors are prompted
to sign in; email and social sign-in preserve the run destination. Missing, expired or inaccessible
runs show an error instead of falling back to another result. These are private bookmarks with
the existing retention limits, not public share links. New queries remove the run parameter.
Harvest profile lookups
Arena discovers Harvest through the shared verified adapter categories. With LinkedIn URL input, Find work email selects the email profile tool, Enrich person selects the full profile tool, and Enrich company selects the company profile tool. Basic profiles do not enter person enrichment. Both Battle and Waterfall use the existing planner and ordinary call/billing path; there are no Harvest branches in Arena. Name, company-domain and email inputs do not select these Harvest tools. Native LinkedIn routes remain available. Additional adapter categories also make the full and company tools candidates in the corresponding public enrichment routes.
_fresh_caller carries the initiating managed key into each paid step. It rechecks that the key
is active and still belongs to the same membership and team. A Default-key generation change
also stops later steps. The snapshot retains key attribution; browser-session runs keep no key.
This recheck happens before a new call and does not cancel a request already in flight.