The machine API product clients ship against
Externally consumed contracts: experiment assignment, the onboarding bundle and events endpoints, the two analytics collectors, the public blog read API, and the health probe. Everything else under /api is dashboard-internal.
Experiment assignment#
A product client with only a subject identifier gets a sticky variant and its public config. The same subject gets the same variant, and an exposure is committed in the same transaction. A journey whose contract sets kill_switch returns the empty assignment rather than a wrong one.
GET /api/experiments/assign?app_id=…&platform=…&surface=…&subject=…
{ "variant": "…", "config": { … }, "experimentId": "…" }Deployment-owned experiment audiences#
The Deep Analytics client helper grants a test-only assignment only when the authenticated account has boolean app_metadata.wisent_internal set to true. Personal email addresses, domains, source-owned UUID lists and user-editable wisent_internal metadata grant no eligibility. This is the client helper's audience check, not an authorization override for Echo's assignment API.
The authentication administrator owns account membership. After changing application metadata, refresh the account session so graphical clients receive the new claim. The analyzer reads current database metadata and excludes boolean wisent_internal or analytics_excluded application claims, plus E2E accounts whose user metadata source is e2e. No private account roster is compiled into the package.
Experiment event properties retain the complete experiment, variant, assignment and source identifiers, trimming only surrounding whitespace. The client applies no character cutoff: URL assignments, React properties and server exposure events keep the same identity. An oversized collector request can still receive the documented 413; identifiers are never silently shortened to avoid it. Deep Analytics tests/experiments/identity.test.mjs covers long distinct identities and URL-to-event round trips through the real client exports.
Run deep-analytics analyze-experiment --experiment-id onboarding_entry_v1 through its normal Skarbiec launcher. It requires WISENT_APP_DATABASE_URL, ECHO_SUPABASE_URL and ECHO_SUPABASE_SERVICE_ROLE_KEY. An optional --excluded-users-file /absolute/private/users.json adds a declared JSON array of user UUIDs. No implicit source-relative list is read; unreadable files, malformed JSON and non-UUID entries fail with the actual cause rather than being treated as an empty list.
The reusable real journey is node tests/audience/journey.mjs --fixture /absolute/checkout/.build/audience.json in the Deep Analytics checkout. Its owner-only fixture names dedicated real accounts and an isolated registered experiment with independently known Echo events and expected funnel output. It checks eligible and ineligible account claims, an untrusted user-metadata marker, the actual analyzer output and file refusals. Commands, exit statuses, source identity and results remain private under .build/audience. The repository README defines the complete fixture.
Declared adaptive decision policy#
Deep Analytics adaptive-policy show|set|clear --experiment <id> manages a stored decision declaration; set requires --file policy.json. The file explicitly supplies minExposurePerVariant, minDurationMs, graduateThreshold, floor, samples, scale, initialControlWeight and rewardWindowHours. No source-owned sample, graduation, traffic-floor or allocation default fills an omitted field. The database connection comes from the normal DATABASE_URL or SUPABASE_DB_URL_ECHO credential launcher.
Apply Echo migration 20261004220000_declared_adaptive_policy.sql before the updated worker. Existing experiments and assignments remain, but unconfigured running experiments make adaptive-tick refuse before decision writes. show reports configured:false with the missing-field reason. clear removes the decision declaration without deleting the experiment. Invalid set operations report the failed operation, field and observed cause without committing a partial policy.
The worker returns the normalized declaration with each decision and stores it in reward snapshot decision_policy, including guardrail specifications. New generations inherit it. Allocation uses the reported probabilities and preserves the declared integer total without negative weights; a small scale can round an arm to zero. Client-facing assignment response fields remain unchanged.
The Deep Analytics CLI worker acquires one PostgreSQL connection, locks running experiments in identity order and commits each complete tick in one repeatable-read transaction. Variant updates, captured policy snapshots, graduation and successor creation cannot be reported as a successful partial decision. A failed operation triggers rollback; diagnostics name the original operation and preserve a separate rollback failure. If the commit response was lost, inspect retained decisions before another invocation: a failed response does not prove the database rolled back. Library consumers of runTick own their transaction.
The Deep Analytics README defines field bounds and the real tests/adaptive/journey.mjs fixture. That journey uses an isolated real Echo database, invokes the policy CLI and worker, checks stored decisions and meaningful refusals, removes its own experiment, and retains source identity, commands and results. It adds a run-owned database check constraint to refuse a snapshot insert after a variant update, checks that all variant and snapshot rows remain unchanged, then removes the constraint and verifies recovery. The test role needs permission to manage that constraint; cleanup attempts its removal independently. Source publication without executing that journey does not establish runtime success.
Research coverage and declared collection bounds#
Deep Analytics and Competitor Research acquisition reports retain every ranked host, landing page and referrer with its observed count. A populated report or candidate list does not establish complete market coverage. Consumers must preserve coverage, omissions and errors rather than treating a truncated result as exhaustive.
Deep Analytics discover-competition requires --source-file or explicit --query/--keyword-query inputs. It normalizes all supplied candidates without a score threshold or candidate-count cutoff. The retired --min-score, --max-candidates and --skip-web options are refused; use --skip-keyword to disable keyword collection. Apple --limit defaults to its documented request maximum of 200; reaching the request boundary or receiving inconsistent resultCount reports incomplete coverage because Apple provides no documented next-page parameter.
Keyword collection supports google_custom_search, serpapi_google and brave_search. Omit --keyword-limit to follow the provider's continuation; a declared positive integer limits organic/web records per query, with SerpAPI ads from visited pages additional unless --skip-search-ads is supplied. Google follows nextPage.startIndex within its documented num <= 10 and start + num <= 100 range; SerpAPI follows its supplied next URL; Brave uses count <= 20, offsets 0 through 9 and the provider's more_results_available flag. Unknown or inaccessible continuation, provider limits, uncovered estimated totals and missing credentials remain incomplete.
Discovery JSON includes sourceReports with query, received/returned counts, pages where pagination exists, declared limits, stopReason, partial and exhausted. Exit 0 means complete selected-input coverage, exit 1 means incomplete coverage or a provider/runtime failure, and malformed arguments exit 2 before collection. --text exposes the same diagnostics. Keyword SDK collectors return { records, sourceReports }, not bare arrays; callers must carry both. A source file is complete only for the records supplied in it.
Competitor Research discoverCompetitors has no implicit three-round ceiling; maxRounds is an optional positive integer, with omission or null meaning no local round budget. coverage.maxRounds, roundLimitReached, sourceReports and sourceErrors distinguish saturation, incompleteness and a blocked run. Its npm collector advances from through total with the provider's 250-result page maximum; GitHub reads accessible 100-result pages up to the provider's 1,000-result search ceiling, retaining incomplete_results and distinct-result gaps; Hacker News follows every page reported by nbPages and exposes uncovered or inexact hit totals. Apple's maximum is 200. These are provider contracts, not locally invented research targets.
Website evidence, linked titles and collection errors are retained without short local excerpts. Failed website collection remains partial. Full provider bodies and source URLs explain failed operations; candidate existence is not a substitute for the failure state.
Deep Analytics research-competition --config config.json supplies no value of its own for maxSourceScanFiles, maxSourceCandidateBytes, maxSourceFiles, maxSourceBytesPerFile, maxSourceFindings, maxSourceQueryContextBytes or maxRoadmapItems. An omitted field means no local bound; a supplied field must be a positive safe integer. Explicit existing configuration is unchanged. Reports persist sourcePolicy, using null for omitted fields, and sourceComparisons[].selection preserves context and source-selection counts, limits, omissions and completeness.
Source files rank by model-supplied term matches then path, without documentation bonuses. The owning competitors-cli source catalog keeps whole UTF-8 characters at a declared byte boundary and records truncation and omissions. Its coverage retains the declarations; source comparisons remain partial when supplied evidence was lost or a findings bound left model items unprocessed. unprocessedFindings counts unexamined model items, not a claimed number of valid missing findings.
The Deep Analytics local onboarding runtime accepts valid cached graphs without a source-owned screen-count ceiling while retaining identity, hash, unique-screen, content-key and transition-target checks. This does not change another product's validator or establish central-service or graphical qualification.
Research qualification evidence#
Deep Analytics npm run test:research -- --fixture /absolute/checkout/.build/research.json exercises actual CLI input/refusal paths, full filesystem selection, persisted and resumed local onboarding, live Google/SerpAPI/Brave pagination and real Brama source analysis. The README specifies dedicated provider queries and expected domains/stop reasons, real source checkout paths, the research signing configuration, a retained deep-analysis result beyond the former context bound and an expected supported search term.
The journey retains source revisions and patches, the declared and installed dependency identity, fixture hash, command exits, TAP, actual provider output, signed request bodies without authentication headers, model results and persisted local state under .build/research. Source changes, mismatched dependencies and unavailable services refuse qualification. It does not clone repositories, simulate providers or claim the scheduled cloning pipeline, central onboarding service or GUI was exercised.
Competitor Research npm run test:discovery -- --fixture PATH exercises real npm, GitHub, Apple, Hacker News and website inputs. Its README defines identities that cross page boundaries, full website evidence and expected partial states. Private build/discovery reports retain exact source identity, inputs, commands, exits and responses. competitors-cli npm test separately records source-catalog full-input, UTF-8 and omission-boundary checks under .build/source-evidence.
Publishing source and qualification journeys without running them is not evidence of runtime success or deployment. CLI and graphical consumers must use the same complete reports and display incomplete coverage; no graphical result is claimed by these source-only changes.
Analytics collectors#
Browsers and mobile apps report user-visible actions and failures to POST /api/analytics/web/collect and POST /api/analytics/mobile/collect. Events land de-duplicated by event id, with attribution recorded once. Only allow-listed site ids are accepted, and oversized bodies are refused with 413. Reporting never throws in the client — a lost telemetry row never turns a handled failure into an unhandled one.
Public blog read#
The only unauthenticated read API serves published blog content per surface, consumed by the landing application.
GET /api/public/blog/[surface]
GET /api/public/blog/[surface]/[slug]Liveness#
curl -s https://<deployment>/api/health
{"status":"ok"}