Capability design scenarios

Normal, failure and recovery cases for the Phase 7 capability contracts, with implementation owners.

1 min read Updated Sep 5, 2026

Capability design scenarios

Every implementation ticket on this page is merged, and tests prove the rows. Each row defines an expected result under the capability contract.

Three test sets hold the proof:

  • ac-python-api scripts/agentic_phase7_exit_test/run.sh drives the five contracts through the live API and the live ac CLI. Its steps are read, start, refuse, attribute, cancel, isolate and parity.
  • ac-python-api tests/agentic/ holds the unit and integration tests. capabilities/<capability>/ covers each product. runtime/definitions/ and runtime/runs/ cover the registry and the Run lifecycle. entry_control/front_door/ covers routing.
  • ac-frontend e2e/features/agentic-phase7-exit.spec.ts drives the five browser journeys.

Each section names its implementation owner. A section names more proof only when that proof is specific to it. The runtime repositories are on their agentic-platform branches. This page is on ac-docs main.

Superadmin company identity writes

ENG-2311 owns these cases. Apply each write case to create and update unless the row states otherwise.

IDInput or eventRequired result
IW01Mixed case, locale host, surrounding spaces or trailing slashStore linkedin.com/<kind>/<slug> with lowercase kind and slug
IW02Query, fragment or page subpath such as /about/Store the same key as the organization root
IW03A match key without a schemeStore the same value; repeated normalization changes nothing
IW04The same slug under company, school and showcase pathsKeep three distinct keys
IW05Person profile, wrong host, misleading host suffix, free text or missing slugHTTP 400; no database call or cache invalidation
IW06Malformed URL that raises a parser errorHTTP 400; no database call or cache invalidation
IW07Non-null, non-string JSON valueExisting request validation returns HTTP 422 before the service
IW08Omitted, null, empty or whitespace-only LinkedIn field with other valid fieldsWrite only the other fields; preserve the stored key on update
IW09Create with only name, only domain or only a valid LinkedIn valuePreserve all three supported create forms
IW10Create has no usable identity; update has no fields after cleaningKeep the existing HTTP 400 validation errors
IW11Create or update collides with an existing match key, including concurrent writesExisting unique index rejects the collision; return HTTP 409; do not merge rows
IW12Update repeats its own key; another field changesKeep the same identity and write the other field
IW13Valid write succeeds or the database reports a missing row or errorKeep existing success, 404 and error handling; invalidate cache only after success
IW14A write succeeds, then CompanyIntelStore or company.search reads by LinkedInThe normalized key selects the written identity; old display-form rows still require ENG-2310

Intelligence identity repair

ENG-2310 owns these cases. Its migration is ac-backend supabase/migrations/20260903161608_intel_linkedin_match_keys.sql. The migration is merged on agentic-platform. It has not run against staging or production.

IDInput or eventRequired result
IR01One person display URLStore its match key; keep its UUID and facts
IR02One company, school or showcase display URLStore its key; keep all three kinds distinct
IR03One row already holds the match keyNo update; preserve updated_at
IR04A display row and a key row resolve to one identityPreserve both; report both UUIDs
IR05Several company rows with different domains resolve to one identityPreserve all rows, including an existing key row; report the whole group
IR06Duplicate company keys with different domains already existPreserve and report the duplicate group despite the partial unique index
IR07The same slug exists in both tables or in different organization kindsKeep separate groups; do not merge identities
IR08A company URL is nullPreserve null; include the row in the inventory
IR09Blank, wrong kind, wrong host, free text or unsupported URL variantStop before persistent writes; report the unsupported count
IR10Case, locale, query, fragment, subpath, Unicode or malformed URL requires parsingStop for inspection; do not implement another parser in SQL
IR11A supported slug contains percent escapes, digits, apostrophes, ampersands or other allowed punctuationPreserve its exact bytes; do not decode or resolve aliases
IR12Empty tables or no display rowsComplete safely; record zero changes
IR13Repeat the migration against unchanged dataNo row update and no duplicate report snapshot
IR14A prior display-form collision report existsPreserve it; write a separate match-key report with all current members
IR15A writer runs during candidate selectionThe table lock serializes the write and repair; no stale candidate set
IR16Lock timeout, statement error or interrupted connectionRoll back row changes and new report rows together
IR17An ordinary reader runs during the repairReads continue; committed state changes atomically
IR18The caller uses anon or authenticated credentialsNo access to the retained cross-tenant report, including TRUNCATE
IR19Repair changes a row with facts, source IDs, freshness dates or referencesPreserve those values and its UUID; only the URL and update timestamp can change
IR20CRM display rows or stored Run results existLeave them unchanged; shared CRM canonicalization still returns display URLs
IR21PeopleIntelStore writes a repaired identity, then people.search reads itReuse the same row and return its stored fact; create no duplicate person
IR22Target writers still store display URLsStop rollout until the writer fixes are deployed
IR23Staging or production cannot be inventoriedRecord the exact access failure; do not claim its counts or deploy there

Start and lifecycle

IDInput or eventRequired resultOwner
C01Valid input for each of the five IDsResolve one tenant executor; validate version and schema; enter RunManager onceENG-2275, ENG-2276
C02A guest or an actor lacks a required scopeRefuse before Run creation or provider workENG-2275, ENG-2276
C03Another tenant's Run, CRM row, prospect, list or saved-search refProduct input resolvers refuse before provider work; start replay cannot cross tenantsENG-2276, ENG-2301, ENG-2278, ENG-2279
C04Unknown ID; missing installation; disabled executorDistinct unknown/unavailable results; no guessed definitionENG-2275, ENG-2276, ENG-2286
C05Two active bindings or schemas from an unpublished draftUnavailable; fail closed and report the binding errorENG-2287, ENG-2275
C06Missing field, unknown field, wrong type, negative count or excessive listPath-specific invalid-input result; no provider callENG-2276
C07Double click or replay with identical input and keySame Run and version, including after a binding upgradeENG-2276, ENG-2303
C08Same key with a different capability, version or normalized inputIdempotency conflict; no second RunENG-2276
C09Policy denies, asks approval or refuses budgetPreserve RunManager's admission result; no bypass or hidden retryENG-2276
C10Concurrent install or crash before binding activationOne active binding; retry incomplete install; preserve healthy prior stateENG-2277
C11Incompatible schema or a stale form after a version upgradeRefuse the version; do not translate silently. A schema change at an unchanged contract version refuses the switchENG-2303, ENG-2284
C12Upgrade, rollback or disable while a parent Run is liveFrozen child UUIDs, contracts and grants remain unchanged. A switch writes only capability_activeENG-2303, ENG-2298
C13Managed upgrade collides with a tenant-authored definitionPreserve tenant work and report the conflict. A bound row outside the released revisions is a conflictENG-2277, ENG-2303
C14Child binding changes but a published parent still names the old UUIDKeep the old executor valid; publish a replacement bound parent and switch its binding. The manifest test refuses a child bump that its bound ancestors do not followENG-2303
C15Profile request with an email-enabled executor, but actor lacks an email tool scopeRefuse at admission under the whole published scope set; do not narrow grants from request fieldsENG-2281, ENG-2299

Initial installation and reconciliation

ENG-2277 owns these cases. They test the initial V1 installer and its durable drivers.

IDInput or eventRequired result
I01Organization has no managed definitionsPublish and activate all five capabilities in dependency order
I02Reconcile a complete installationReturn five current results; write nothing
I03Two reconcilers start togetherDeterministic IDs and storage constraints leave one tree and one active binding per capability
I04Process stops after draft creationRetry locates the exact draft, validates it and continues
I05Process stops after child publicationRetry reuses the exact child and publishes the remaining tree
I06Process stops after binding attachmentRetry activates the exact binding when no active binding exists
I07Process stops after activationRetry reports current; it creates no second row
I08Managed draft matches its manifestPublish it through the normal validator
I09Managed draft was editedReport conflict; do not overwrite or publish it
I10Deterministic UUID holds another kind, name or configurationReport conflict; preserve that row and retain exact managed progress
I11Another definition has the same display nameIgnore the name collision; use deterministic identity only
I12Tenant-authored binding is active for the capabilityPreserve it and report conflict; do not install a competing active binding
I13Tenant-authored binding is inactiveReport conflict; attach or activate no managed binding for that capability
I14Exact managed binding is inactive and its executor is activeActivate it only when no active binding exists
I15Exact managed executor is disabledReport disabled; do not enable or replace it
I16Managed binding metadata, scopes or graph is invalidReport invalid; do not infer or repair data
I17Two active bindings exist in corrupted storageReport invalid; select neither row
I18One independent capability fails validationReport that capability; continue independent capabilities and block its dependants
I19Company Search does not installBlock People Search and Signals Search; continue Company Enrich and People Enrich where valid
I20Any required child of Signals Search is unavailableDo not create or activate the Signals root
I21Database or network read failsRaise for durable retry; never convert the fault to a stable conflict
I22New organization event is delivered twice or after 24 hoursDatabase idempotency produces the same complete installation
I23New organization event send failsOrganization creation succeeds; the scheduled sweep sends the same reconcile event later
I24Backfill and new-organization delivery overlapBoth call the same reconciler and converge on the same rows
I25Organization is deleted before reconciliation startsReturn run-level absent_organization with no capability results; create no definition
I26Operator runs report modeReturn missing or the exact stable state for each capability; change no data
I27Operator runs apply modeUse the same reconciliation service and print all capability results
I28Any released manifest content or UUID namespace changesGolden contract tests fail; a content change requires a new ENG-2303 revision
I29Existing binding is valid but differs from the manifestReport conflict; do not replace its text, schemas, version or managed marker
I30Organization count exceeds one durable run's step limitContinue the sweep by cursor event; process one bounded page in each run

Binding persistence

ENG-2287 owns these cases.

IDScenarioExpected resultOwner
B01Existing custom or platform definitionNo implicit binding; existing lifecycle remains validENG-2287
B02Each of the five stable IDsPersist the exact ID and positive contract versionENG-2287
B03Unknown or case-changed ID; JSON null; missing or extra metadata fieldReject the metadataENG-2287
B04Zero, negative, Boolean or fractional versionReject the versionENG-2287
B05Invalid, oversized or open root schema; remote schema referenceReject before attachmentENG-2287
B06Missing, non-string or malformed required scopesFail closed; do not infer a run.start-only contractENG-2287
B07Draft, Agent, Skill, disabled executor or platform templateRefuse binding attachmentENG-2287
B08Foreign tenant UUID or missing UUIDSame non-disclosing result; no writeENG-2287
B09Publish or draft save races with attachmentExact row token fails; reload and validate againENG-2287
B10Attach metadata while changing published configuration or referencesDatabase refuses the mixed writeENG-2287
B11Two activations for one tenant and IDOne succeeds; one returns a bounded conflictENG-2287
B12Same capability in different tenantsBoth activate independentlyENG-2287
B13Active and inactive executor versionsOne active binding; older executor stays runnableENG-2287
B14Change or remove inactive binding metadata; change tenant, kind or UUIDDatabase refuses the writeENG-2287
B15Edit a bound draft or fork its definitionPublished contract stays fixed; fork is unboundENG-2287
B16Generic republish of a bound definitionRefuse; require a new definition rowENG-2287
B17Disable races with activationFinal disabled row has no active binding; stale activation writes nothingENG-2287
B18Enable a disabled bound definitionRevalidate; keep binding inactiveENG-2287
B19Delete an active or inactive bound definitionRefuse; preserve retained executor identityENG-2287
B20Anonymous, tenant, foreign tenant and service-role accessEnforce schema grants, column grants, RLS and service-only writesENG-2287
B21Crash before or during replacementTwo guarded writes; a half switch leaves no active binding and the next pass repairs itENG-2303
B22Child instructions or Skill change with the same scopesThe child needs a new revision, which needs a new bound-root revision, which needs an operator switch. Nothing is implicitENG-2303
B23Replacement during a RunFrozen UUIDs and capability metadata remain unchangedENG-2298

Registry read stress cases

These refine C01 to C05. ENG-2275 owns them. Its implementation tests and one local dry run prove them.

IDInput or eventRequired result
R01Organization installed nothingFive records, each not_installed; the vocabulary is code
R02One binding metadata or scope contract does not validateThat ID reads invalid_binding; the other four stay available
R03Executor is disabled, or its binding was deactivatedinactive_executor, never not_installed
R04Bound row is an Agent, a Skill, a platform template or a withdrawn rowinvalid_binding; no delegable result
R05Two active bindings for one IDinvalid_binding; no executor selected by row order
R06Bound row stores an ID outside the fiveIgnored; it adds no record and replaces none
R07Actor holds no run.startThe registry answers five unauthorized records; no binding read runs
R08Actor holds run.start and lacks one required scopeunauthorized for that ID alone
R09Child Run actor carries its parent's narrowed grantThe parent grant decides; no role read
R10Rights read or binding read failsRaise; never report an empty catalogue for a full tenant
R11Unknown ID, or a stable ID in changed caseUnknown, not unavailable; the vocabulary is case-sensitive
R12Organization retains many superseded executorsActive rows are read first; a truncation loses a retained row alone

Read API stress cases

These refine C04 at the HTTP and CLI layer. ENG-2286 owns them. The registry answers each case; these rows fix how a reply reports it.

IDInput or eventRequired result
A01Actor holds no run.start; default available_only200 and an empty items; the reply names no capability
A02The same actor with available_only=false200 and five unauthorized records, each without a contract
A03Organization installed nothing; member holds run.start200 and an empty items, which A01 cannot be told from; both mean the caller starts nothing
A04Two capabilities available and three not; defaultOnly the two, in stable capability-ID order
A05available_only holds a value that is not a boolean422; no registry read
A06The rights read or the binding read raises500; never an empty catalogue for a full tenant
A07Any list or single replyThe body holds no executor_id, no executor configuration and no provider Tool definition
A08An available recordIt carries required_scopes; an unavailable record omits it
A09Unknown ID, and a stable ID in changed case404 for both; the vocabulary is case-sensitive
A10Single read — unauthorized, from no run.start or from one missing required scope403 in both cases. The list answers 200 for the same actor; see A01 and A02
A11not_installed, inactive_executor or invalid_binding200; the body carries availability: unavailable and that reason, and no contract field
A12Unauthenticated caller403 from the shared bearer dependency, which answers Not authenticated; no route body runs
A13The organization of the replyIt comes from the token claims alone; a query or body value never selects it
A14ac agentic capabilities list and getThe same fields as the API, under --json; a table renders the ID, the availability and the reason

Direct start stress cases

ENG-2276 owns these cases. The harness step start checks ST01, ST02, ST07, ST08, ST12, ST13, ST22 and ST27 against a live stack. The unit tests own the other cases. The start identity contract defines the decision order.

IDInput or eventRequired result
ST01All five known IDs with valid published inputResolve and start the selected executor; do not hard-code one product
ST02Unknown or case-changed ID with a fresh key404 capability_not_found; no Run
ST03Missing, inactive, duplicate or invalid binding409 capability_unavailable with a bounded reason; no Run
ST04Guest or missing required scope403 capability_unauthorized; no Run or provider call
ST05The shared rights reader reports a missing scope during resolutionRunManager refuses before insertion
ST06Extra envelope field, missing input, null input or invalid version type422; no service call
ST07Missing, empty, blank or 201-character key400; no start
ST08Missing or extra input field; wrong type, enum, count or list size422 with a field path; no Run
ST09Invalid UUID or URI under a declared format422; local schema references still resolve
ST10Input at 32768 bytes and one byte above, including UnicodeAccept the limit if schema-valid; refuse the excess with 413
ST11NaN, infinity, invalid Unicode or excessive nestingBounded refusal; no Run
ST12Equivalent object key order and JSON whitespaceSame digest and Run
ST13Changed array order, string case, parsed numeric type or omitted default409 idempotency_conflict for the reused key
ST14Same key with another capability or contract version409 idempotency_conflict; no second Run
ST15Identical key in different tenants, callers or sourcesIndependent Runs; no cross-boundary replay
ST16Same raw key on the generic Run endpointSeparate namespace; generic replay behavior stays unchanged
ST17Identical simultaneous requestsOne insert wins; one admission and dispatch; all others return that Run
ST18Different simultaneous requests sharing a keyOne insert wins; the loser returns conflict
ST19Upgrade or disable before the final executor checkFresh start refuses; no replacement executor is selected
ST20Upgrade after the final executor checkThe selected immutable executor remains fixed for this start
ST21Replay after upgrade, disable, budget exhaustion or scope removalReturn the admitted Run without current binding or admission checks
ST22Replay of a queued, running, waiting, succeeded, failed or cancelled RunReturn current durable status; do not dispatch again
ST23Crash before insertionNo key claim; a retry can start
ST24Crash after insertion or lost HTTP responseThe Run and digest survive together; retry returns the same Run; incomplete admission remains held
ST25Dispatch fails after insertionReplay does not dispatch; the existing reaper owns recovery
ST26Stored digest missing or malformedConflict; no guessed replay and no new Run
ST27Stale version with a fresh key409 contract_version_conflict; no silent conversion
ST28Policy deny or approvalPreserve failed or waiting Run and HTTP 200 started outcome
ST29Day budget exhausted or policy service unavailablePreserve 429 with Retry-After or 503; create no Run
ST30Reference belongs to another tenant or provider connection is missingThe product resolver refuses before provider work; shared start does not duplicate the resolver
ST31CLI start, JSON output, malformed input and each HTTP refusalExact request, required key and version, unchanged Run response, standard exit codes
ST32Crash after insertion but before admission completesTemporary approval hold prevents dispatch; wait recovery fails it with wait_abandoned
ST33Cancellation or human approval hold races with admission releaseConditional release writes nothing; it cannot queue terminal or human-held work
ST34API/CLI route registrationLive strict audit: zero API-only, CLI-only and method differences

Search and identity

IDInput or eventRequired resultOwner
S01Supplied domains, repeated domains and a domainless company LinkedIn URLStable refs; merged duplicates; LinkedIn fallback; no enrichmentENG-2278
S02Provider and supplied observations identify the same companyDeduplicate before gates; retain bounded evidence from bothENG-2295
S03Domain and LinkedIn keys identify different canonical companiesIdentity conflict; no automatic merge or writeENG-2278, ENG-2279
S04Company filter value is missing versus explicitly contradictoryKeep unknown with a diagnostic; exclude contradiction onceENG-2278
S05Technology, city, web thesis or funding-event filter in direct Company SearchUnsupported-filter error; no silent broad queryENG-2295
S06Page limit, 500 candidates, 60-second deadline or budget reachedBound calls and retries; preserve usable rows with partial diagnosticsENG-2295, ENG-2291
S07Provider timeout after one good page; all provider calls failPartial result in the first case; failed Run in the secondENG-2295, ENG-2291
S08Valid query finds no matchesSuccessful empty result; no infinite refillENG-2278, ENG-2280
S09Known-company People Search with supplied person LinkedIn refsSkip company discovery; normalize identities; apply persona gatesENG-2280
S10Name-only person, profile URL conflict or duplicate provider person IDsUnresolved/conflict diagnostics; never use provider ID as canonical identityENG-2280, ENG-2291
S11Company brief resolves 25 companies or no companiesFirst 20 distinct refs with explicit truncation; complete zero scope is empty; partial zero scope stays partialENG-2293
S12Both company-scope alternatives, or 21 explicit company refsInvalid input; no arbitrary scope choiceENG-2293
S13One company has many people; another has noneRespect per-company and total caps; report both companiesENG-2291
S14Person location differs from employer locationGate on person location; do not substitute employer countryENG-2291
S15Retry selected Search rows; source Run is deleted or foreignReuse stable refs; report stale or unauthorized refs without guessingENG-2301, ENG-2279

Company supplied-list stress cases

These cases refine S01, S03, S04 and S08. ENG-2278 owns them. They cover the supplied-list tool contract. The public discovery contract also supports Explorium; see CP01 to CP28. The review uses runtime trunk fa3edcc92 and documentation main b24e147.

IDInput or eventRequired result
CS01Missing source, duplicate sources, Explorium or unknown sourceReject before cache work
CS02Zero or 101 refs; target 0, 101, boolean or numeric textReject the input
CS03Unknown fields, mixed reference tags, malformed UUID or invalid identityReject the input; never accept display text as identity
CS04Valid uncached domain or organization LinkedIn URLReturn a selectable normalized identity without a canonical write
CS05Same company by domain, UUID and LinkedIn; repeated inputsOne row, first-input order, all input indexes, at most three evidence records
CS06Domain and LinkedIn identify different cache records; duplicate canonical keysExclude the conflict; never pick a record by query order
CS07Domainless LinkedIn observation joins one known domainMerge before gates; preserve the domain as primary identity
CS08Same LinkedIn key links two different domainsDo not merge the companies; report identity conflicts
CS09Missing, matching and contradictory values across duplicatesKeep missing values; any explicit contradiction excludes the group once
CS10Multiple filter values and fields; unknown observed bandOR within a list; AND between fields; unknown band stays unknown
CS11Empty filter list, duplicate values, unknown field or unsupported bandReject before cache work
CS12Zero matches; all identities fail; one identity fails and another fails filtersEmpty success; empty partial result; empty partial result, respectively
CS13100 rows survive, target is 1Return the first row; no refill or extra provider work
CS14More than 100 diagnosticsKeep 100 and count every omitted diagnostic
CS15Long Unicode text and evidence exceed the byte limitRemove optional content in stable order; retain identities; report truncation
CS16Required identities alone exceed the result limitFail with payload_too_large
CS17Same Run replays; new Run uses the same inputsSame result UUIDs on replay; different UUIDs for the new Run
CS18Foreign or missing source Run beside valid direct refsRefuse the whole request before cache reads; use the same error
CS19Accessible Run has a missing row, wrong capability or malformed outputItem stale_reference; never guess from current display data
CS20Cache read fails or times outFail the tool; never report an empty successful search
CS21Publish succeeds, draft creation fails or publish refusesUse the standard lifecycle; report the exact refusal and any retained draft
CS22Published graph, grants and dry runOne read tool; no provider, Agent, enrichment, canonical write or CRM scope

People supplied-list stress cases

These cases refine S08, S09 and S10. ENG-2280 owns them. The review uses runtime trunk 5b3ed1ca2 and documentation main ca6954b.

IDInput or eventRequired result
PS01Missing source, duplicate sources, Explorium or unknown sourceReject before cache work
PS02Absent scope, a company_search brief, 21 company refs or both scope formsAccept the absent scope; reject the brief and the oversized scope
PS03Zero or 101 person refs; target_per_company 0, 11, boolean or numeric textReject the input
PS04A crm_person tag, mixed reference tags, unknown fields or a malformed UUIDReject the input; never accept display text as identity
PS05Absent persona, empty list, duplicate values, unknown field or unsupported valueReject before cache work
PS06Valid uncached individual LinkedIn profile URLReturn a selectable normalized identity without a canonical write
PS07Organization LinkedIn URL, or a name with an employer, offered as a personUnresolved; never guess a profile
PS08Same person by UUID and LinkedIn; repeated inputsOne row, first-input order, all input indexes, at most three evidence records
PS09Canonical UUID with no record; record with no LinkedIn URLnot_found and unresolved, respectively
PS10One LinkedIn key links two canonical records; duplicate canonical keysExclude the conflict; never pick a record by query order
PS11Provider person ID supplied as the only identityUnresolved; a provider ref is evidence, never an entity key
PS12Missing, matching and contradictory persona values across duplicatesKeep missing values; any explicit contradiction excludes the group once
PS13Multiple persona values and fields; a department or seniority criterionOR within a list; AND between fields; the cache stores neither field, so both stay unknown
PS14Person country differs from the observed employer countryGate on person country; never substitute the employer country
PS15Supplied person with a company scope and a cached employer nameKeep the employer unknown; a display name is never an identity, so the scope excludes nobody
PS16Zero matches; all identities fail; one identity fails and another fails gatesEmpty success; empty partial result; empty partial result, respectively
PS17100 rows survive with a company scope and target_per_company 1Return the first 100; per_company stays empty; the target changes nothing
PS18More than 100 diagnosticsKeep 100 and count every omitted diagnostic
PS19Long Unicode text and evidence exceed the byte limitRemove optional content in stable order; retain identities and counts; report truncation
PS20Required identities alone exceed the result limitFail with payload_too_large
PS21Same Run replays; new Run uses the same inputsSame result UUIDs on replay; different UUIDs for the new Run
PS22Foreign or missing source Run beside valid direct refsRefuse the whole request before cache reads; use the same error
PS23Accessible Run has a missing row, a company.search capability or malformed outputItem stale_reference; never guess from current display data
PS24Cache read fails or times outFail the tool; never report an empty successful search
PS25Publish succeeds, draft creation fails or publish refusesUse the standard lifecycle; report the exact refusal and any retained draft
PS26Published graph, grants and dry runOne read tool; no provider, Agent, enrichment, canonical write or CRM scope
PS27Cached person holds skills, languages and the three history listsReturn skills and languages; omit the history lists, which have no closed schema

Company provider stress checks

ENG-2295 owns CP01 to CP28.

IDInput or eventRequired result
CP01Provider only, supplied only, both source ordersExact source set; supplied-first order; no implicit source
CP02Provider selected without filters; empty lists, nulls, duplicate sources or unknown propertiesInvalid input before work; supplied-only filters remain optional
CP03Mixed-case supported industries, unknown industry or unsupported cityNormalize supported names; reject unknown values without a broad query
CP04Every country and employee/revenue tokenExact mapping; no inferred numeric bands
CP05Old supplied-only definition or grantZero provider calls; no paid authority from the old grant
CP06Foreign or missing saved Run with provider selectedNon-disclosing refusal before provider work
CP07Same domain in provider and supplied inputOne group; preserve supplied indexes and evidence from each source
CP08Duplicate provider ID with different domains; same domain with different IDsProvider ID does not merge identities; domain duplicates merge
CP09Domainless LinkedIn row; name-only row; malformed siblingKeep LinkedIn identity; diagnose unresolved siblings
CP10Provider keys identify two cached canonical companiesIdentity conflict; do not merge canonical companies
CP11Cache contradicts a provider filter valueExclude the merged group once; no refill
CP12Missing industry in a filtered responseUnknown diagnostic; do not manufacture a matching industry
CP13Exact country name versus unrecognized country nameMap the exact ISO name; otherwise keep unknown
CP14Target reached before gates remove duplicates or contradictionsStop collection; return fewer rows without another request
CP15Successful empty page or terminal pageStop cleanly; empty succeeds only without other failures
CP16More pages than allowed, oversized page or invalid page numberBound calls and rows; diagnose truncation or provider failure
CP17First request fails with and without usable supplied identityPartial with usable identity; otherwise fail the Run
CP18Later request fails; earlier identities all fail gatesPreserve partial status, counters and failure reason even with zero output rows
CP19Slow provider or shortened root deadlineTen-second attempt bound; reserve result time; keep earlier rows
CP20Insufficient root budget before the first or next pageNo unaffordable call; partial budget result; no no_matches claim
CP21Timeout, 429, 5xx or malformed JSONNo hidden retry; meter uncertain spend once; stable error
CP22Valid credits, absent credits or invalid creditsUse valid charge or conservative estimate; never count total query matches as billed rows
CP23Meter read or write failsTerminal failure; no next provider request
CP24Missing key or entitlement; development dummy keyNo fake results; explicit provider failure; preserve usable supplied identities
CP25Cancellation, completed-step replay or process stop before meter persistencePropagate cancellation; reuse completed result; interrupted steps can repeat calls and leave uncertain charges
CP26Long facts, many diagnostics or required output exceeds byte capTrim optional facts; exact omitted count; fail if identities cannot fit
CP27Repeated call with one Run ID; new Run IDStable IDs within a Run; different IDs for a new Run
CP28Root already has child usage or two concurrent searchesRead canonical tree usage; do not double-charge summaries; document bounded concurrent overshoot

The root meter is an accrual check, not an atomic reservation. Concurrent calls can each pass before either records its charge. One lane has only one in-flight page. Cross-lane overshoot follows the existing platform policy and is bounded per admitted page. Atomic tree-wide reservations require a platform budget change and are outside this provider adapter.

People provider design checks

ENG-2291 owns these cases. ENG-2300 owns the live provider evidence. They use API trunk 73fa64cae and docs 8d58913 as their baseline.

IDInput or eventRequired result
PP01Missing or duplicate source; invalid persona vocabularyReject before reads or calls
PP02Provider source without scope; company brief; more than 20 refsReject; the brief belongs to ENG-2293
PP03Supplied-only request through either toolSame rows and no provider cost; old grant stays free
PP04Canonical, domain, LinkedIn and selected company refs overlapResolve and deduplicate in first-input order
PP05Foreign company or person source RunSame stale-reference refusal before provider work
PP06Missing selected row, company UUID or canonical identity conflictPer-company unresolved reason; continue valid companies
PP07Null match, repeated match, wrong echoed input or item errorNo prospect query for that company
PP08Two distinct company identities match one business IDBoth unresolved; no duplicated provider query
PP09Title, department, seniority and person country togetherOR within lists; AND between fields; no related-title expansion
PP10Employer country differs from person countryUse only the person's country
PP11Missing department, unsupported level or multi-level fallbackUnknown diagnostic; never claim verified fit
PP12Employer ID is absent or differs from requested companyUnknown when absent; exclude merged group on contradiction
PP13Missing, invalid or organization LinkedIn URLUnresolved row; provider ID never supplies identity
PP14Repeated provider ID on distinct LinkedIn URLs; alias arraysKeep primary URLs separate; do not merge aliases
PP15Supplied and provider observations share a profile URLMerge before gates; preserve indexes and source evidence
PP16Cache UUID conflict or contradictory observed titleExclude the full merged group
PP17Empty company, target 1 or 10, twenty full companiesOne page per company; fair order; 100-row output cap
PP18Mixed sources exceed 100 rowsCompany round-robin first, then supplied-only groups; report truncation
PP19Provider returns too many rows, wrong page or malformed payloadBound memory and candidates; explicit partial or failed source
PP20Timeout, 429 or 5xx on one companyKeep earlier rows; continue later companies within remaining bounds
PP21401, 403, missing key or missing licenseStop provider work; preserve usable supplied rows
PP22Deadline or root budget reached before first or later callNo next call; mark all unattempted companies; no false empty success
PP23Missing, invalid, exact or excessive credit reportConservative estimate or valid credits; stop on excessive charge
PP24Meter read or write failsTerminal failure; no later provider call
PP25Completed-step replay, cancellation or process interruptionStable rows on replay; propagate cancellation; document uncertain spend
PP26Long facts, 100 diagnostics or large company refsStable trimming; exact omitted count; fail if required fields exceed limit
PP27All calls fail, successful empty calls, or all matches unresolvedFailed, empty, or partial respectively; never conflate outcomes
PP28Provider failure leaves identities that gates later excludePartial zero-row result; retain failure evidence
PP29Provider query returns the same person for two companiesMerge once; assign first company; report both company counts
PP30Old published supplied grant invokes the new provider toolAuthorization refuses; republishing needs the new grant

Enrichment and asynchronous work

IDInput or eventRequired resultOwner
E01Basic/Standard request with all requested fields currentCache hit; no paid call; no fabricated per-field timestampsENG-2279, ENG-2281
E02Current row has a missing requested fieldFill that gap; currentness does not imply completenessENG-2279, ENG-2281
E03Missing tier stamp, exact TTL boundary or stale refreshTreat as stale; refresh only requested targetsENG-2279, ENG-2281
E04Later source contradicts an accepted earlier sourcePreserve deterministic precedence; do not clear fields with nullENG-2279, ENG-2281
E05All refresh sources fail while old cache values existPreserve old facts and timestamps; report failure, not fresh successENG-2279, ENG-2281
E06Two Runs update the same canonical identityPreserve both source lineages; conflict check or serialize writesENG-2279, ENG-2281
E07CRM identity input includes private notes or manually edited fieldsRead identity under tenant scope; no CRM write or private global-cache writeENG-2301
E08Deep preset, phone, arbitrary research or fields outside the selected presetReject before paid workENG-2279, ENG-2281
E09Profile succeeds; email remains runningPersist profile early; keep root live; show email pendingENG-2299, ENG-2289
E10Callback arrives before wait registrationRead durable terminal job state; do not wait forever for a lost eventENG-2210, ENG-2299
E11Callback arrives before the platform job existsReserve before POST; bind the vendor ID from the verified callback; do not submit twiceENG-2300, ENG-2210
E12Callback replay, reordered contacts, duplicate or unknown echoed refsMatch exact refs; report invalid refs; patch and settle onceENG-2210, ENG-2299
E13Some emails found, others absent, provider reports unverified emailKeep successes; distinguish not-found from failed; preserve verification meaningENG-2299
E14Cancel or timeout, then receive a late callbackNo cancelled-lane writes or resumed Run; settle any real charge onceENG-2299
E15Provider job completes but canonical write failsRetry the write, not provider submission; never report refreshed before persistenceENG-2299
E16Profile-only write while email is stalePreserve the old email hot stamp; do not suppress email refresh. Trunk already does this; keep it under a regression testENG-2281
E17Stale title refresh succeeds, stale summary refresh fails; summary is requested againPreserve warm stamp; repeat the summary lookup; do not mark the retained old summary currentENG-2281
E18Request only email; reopen the result after canonical email changesReturn verification metadata independent of selected value fields; preserve the frozen pair; old cache without evidence is unknownENG-2299, ENG-2289

Company Enrich design stress cases

ENG-2279 owns these cases. Runtime comparisons use ac-python-api/agentic-platform. Documentation comparisons use ac-docs/main.

IDInput or eventRequired result
CE01Empty subjects, 101 subjects, unknown preset or fieldReject before cache reads or paid calls
CE02Repeated domain, canonical ID and selected row for one companyResolve once; retain all input indexes in input order
CE03Selected row belongs to another tenant or deleted RunReject the request before provider work
CE04Missing selected row, failed source Run or wrong result kindReturn an unresolved item with a stable diagnostic
CE05Canonical ID and domain identify different rowsReject the conflicting item; do not join the companies
CE06Client display contains a name, private notes or provider payloadUse verified identity only; never persist client facts
CE07Known canonical company has only a LinkedIn identity, stored as a key or URLResolve company, school and showcase pages to the public URL; do not manufacture a domain
CE08All requested values exist and warm time is currentReturn a hit without provider work; cached LinkedIn logo maintenance can write a stable URL while preserving tier times
CE09Warm time is current but one selected field is absentRequest that gap only; currentness does not prove completeness
CE10Missing warm time, exact 30-day boundary or future timestampTreat as stale; reuse one durable evaluation time
CE11Missing-only refresh finds an existing stale valueKeep it with retained state and a stale cache decision; do not claim verification
CE12Selected stale field succeeds; another retained stale field is untouchedPreserve warm and cold stamps before the write
CE13Selected stale field returns no replacementKeep the stored fact and old stamps; mark the requested field unavailable
CE14All selected providers failKeep prior facts; report partial or failed outcome, never refreshed
CE15Provider returns null, blank, wrong types, fractional counts, negative numbers or NaNDrop invalid facts and their derived fields; retain valid zero counts through the store
CE16Later provider contradicts an accepted earlier providerFirst accepted source wins within the attempt
CE17Domain or LinkedIn evidence conflicts with a providerReject that provider payload before persistence
CE18Request only founding_year or one exact countGate by the selected field; legacy preset omissions and alternative bands do not satisfy it
CE19Provider cannot supply a selected fieldSkip that source; do not fetch unrelated fields to satisfy the legacy gate
CE20Logo rehosting failsKeep the stored old fact and any usable old output logo; omit an unusable LinkedIn URL and mark it failed, never refreshed
CE21Canonical write fails after provider calls completeRetry persistence with durable provider results; do not repeat completed paid steps
CE22Two Runs write the same identityRecheck identity, preserve concurrent fact groups and union full source lineage; report refreshed only for the accepted group
CE23Concurrent writer changes identity or deletes the recordRefuse the stale write and final-row projection; do not attribute another company's facts to the original ref
CE24Partial update leaves old qualifiers or unrelated extra fieldsPreserve extra fields; reject incomplete group changes that would attach old currency, year, count or address data to a new fact
CE25Lineage or diagnostics exceed output limitsTrim them with exact omitted counts; retain full canonical lineage and all selected values and states
CE26Selected values or required row data exceed output byte limitPreflight after deduplication and between children; stop further paid work with payload_too_large and preserve persisted facts and usage
CE27Durable component invoked from an ordinary tool stepReject nested steps; invoke from the workflow orchestration boundary
CE28Component or legacy helper imported into src.agenticImport contracts fail; use the registered function ID across the boundary
CE29Provider timeout, 429, malformed response or uncertain chargeBound attempts and time; retain per-item failures and conservative cost evidence
CE30Completed step replay, settings change or retry after persistenceReuse source eligibility, durable results and usage IDs; do not skip saved usage or charge summary rows again
CE31Worker dies after provider accepts a requestState the uncertain charge; do not promise exactly-once provider execution
CE32Root deadline, cancellation or cost limit reached between sourcesStart no next source and make no late canonical write
CE33Meter read or write failsStop paid work with metering_unavailable; do not swallow the fault
CE34One failed item in a 100-item batch that fits the byte limitPreserve ordered sibling results and bounded diagnostics; unstarted rows use not_read cache state
CE35Publish refusal, missing grants or missing provider connectionPreserve lifecycle refusal; never publish a bypass or synthetic result
CE36Existing Sonar caller omits the new target optionsPreserve the legacy caller contract and source order
CE37Component runs for more than 150 seconds; another worker retriesPreserve the durable claim until its deadline; never dispatch a second invocation
CE38Policy requests approval, then the user approvesWait durably; validate approval coverage; claim and dispatch once
CE39Approval is rejected, expires, or the Run is cancelled while waitingStart no component and take no claim
CE40Worker stops after preparation or after dispatchReplay the preparation and invocation IDs; retain the original claim token
CE41Component completes; worker stops before settlementReuse its result; validate and complete the journal without another provider call
CE42Legacy and agentic apps have different IDsInvoke the configured legacy app and fixed company component ID
CE43Agent or direct ordinary tool path selects the durable bindingReturn an explicit unavailable result; do not execute inside a nested step
CE44Cancellation races with an already submitted provider or database requestStart no subsequent work; record the uncertain outcome without promising rollback
CE45Claim commits but preparation does not checkpointRecover the same durable owner, token and deadline; do not wait for lease expiry or renew the operation

Runtime integration gate

An ordinary workflow tool runs inside step.run. Inngest does not permit a nested component invocation there. ENG-2279 needs a workflow orchestration seam for an authorized durable tool before it can publish the enrichment workflow. Use a trusted durable binding in the tool registry and keep the public workflow ToolNode shape. Only the workflow executor can execute this binding. User definitions cannot supply arbitrary component or app IDs. Extract preparation and settlement from the existing invoker; do not create a second policy or journal implementation. Preparation returns a typed, internal invocation record. The normal result schema never exposes its claim token. Settlement validates, bounds and records the output, completes the claim, and updates the original telemetry. The seam must keep the existing tool validation, policy, approval, idempotency, output and usage owners. It must authorize before dispatch, invoke the registered component outside step.run, and settle the durable result afterward. The invocation must name the legacy component app explicitly; the agentic app has a different ID. The trusted binding has a 1,800-second operation limit, reduced by the remaining root deadline. After approval, persist the operation deadline and owner token in a durable step before acquiring the journal claim. Claim acquisition uses that owner token and the fixed request hash. If the claim commits before preparation checkpoints, the existing journal service must recover the same owner and token. Recovery cannot extend the operation deadline or claim a token owned by another invocation. The claim lasts until that deadline plus 30 seconds. It does not use the ordinary 120-second handler budget. The durable invocation key includes organization, Run and workflow node identity. It does not include the worker attempt. Preparation, dispatch and settlement replays reuse this key and the original claim token. After the operation deadline, a recovered invocation can settle completed evidence but cannot start more provider work. A worker that loses the claim cannot write canonical data or complete another worker's claim. A cached authorization must not permit new work after cancellation, deadline expiry or claim loss. A policy approval request suspends preparation without taking a claim or dispatching the component. Read approval state before and after the durable wait so an early decision cannot strand the Run. Approval resume validates the approval against the same principal, tool and argument hash through the existing invoker. Rejection, expiry or cancellation ends the call without dispatch. Approval replay cannot widen the original request. The component must recheck cancellation, deadline and remaining cost before each source and before canonical persistence. It must preserve completed provider results when only persistence needs a retry. Cancellation cannot undo a request already accepted by a provider or database. Record that race explicitly and stop subsequent work; never promise an external rollback. No raw event dispatch followed by an in-memory wait can replace this seam.

The existing provider log context identifies legacy workflow Runs. An agentic root ID cannot be written into that foreign key. Root usage needs explicit agentic attribution and stable usage IDs at the provider step boundary. A summary may read those charges; it must not write a second aggregate charge. Provider calls interrupted before a durable checkpoint can repeat. The design does not claim exactly-once external billing.

People Enrich design stress cases

ENG-2281 owns these cases. Runtime comparisons use ac-python-api/agentic-platform. Documentation comparisons use ac-docs/main.

Three contract decisions come from this review. Each one answers a case below.

  1. The Standard preset dropped industry, skills, languages, experience_history, education_history and certifications. Only People Search discovery supplies them, and People Enrich takes identities rather than queries, so every request reported them missing.
  2. Only a sourced field holds a tier stamp back. work_phone, mobile_phone and the six discovery-only fields have no V1 source, so under the company rule one stored value would pin the warm stamp forever and every later request would pay Hunter again.
  3. Contract version 1 declares the whole PersonRef union. The tenant adapter resolves both tenant tags without a version bump.
IDInput or eventRequired result
PE01Empty subjects, 101 subjects, unknown preset, or a field outside the chosen presetReject before cache reads or paid calls
PE02A dropped discovery-only field name, such as skills or experience_historyReject as an unknown field; the value stays stored and readable
PE03email_source or email_score requested without emailReject the input
PE04crm_person or search_result refKeep the version 1 schema; apply the tenant adapter cases PI01 to PI28
PE05Same person by canonical UUID and by LinkedIn URL; repeated inputsOne work item, first-input order, every input index retained
PE06LinkedIn URL with tracking query, trailing slash or mixed case; a company or school URLNormalize through the shared profile key; refuse a URL that is not an individual profile
PE07intel_person UUID that never existed or whose row was deletedReturn stale_reference; never create a row from the UUID alone
PE08Canonical row identity changes between the cache read and the writeRefuse that write with identity_conflict; never attribute one person's facts to another
PE09Every selected field is present and the warm stamp is inside 90 daysCache hit; no provider call and no canonical write
PE10Warm stamp is current but one selected field is absentRequest that gap only; currentness does not prove completeness
PE11Missing warm stamp, exact 90-day boundary, or a future stampTreat as stale; reuse one durable evaluation time
PE12refresh: missing finds an existing stale valueReturn it as retained with a stale cache decision; make no provider call for it
PE13Every sourced warm field is verified or refreshed in this attemptAdvance the warm stamp
PE14One sourced warm field stays unverifiedPreserve the warm stamp; report that field failed or not_found
PE15Only work_phone, mobile_phone or a discovery-only field is stale and unverifiedAdvance the warm stamp; an unsourced field never holds it back
PE16Repeat the same Basic refresh: stale request after one successful full attemptSecond request is a cache hit with no provider call
PE17Cached person already has an avatar and current_title is a pending selected fieldRun the source; the discovery avatar guard does not apply to a selected request
PE18The existing Headhunter caller runs with no selected targetsPreserve the avatar self-skip, the source order and every legacy step ID
PE19No pending selected field is inside a source's output setSkip that source; make no paid call
PE20The source returns a new place stringWrite location, country and country_code from that one observation
PE21The source returns a changed place with no countryKeep the stored place group; leave the selected field unavailable
PE22The source emits email, email_source or email_scoreDrop them; FullEnrich alone owns the email family
PE23Provider returns null, blank, a wrong type or an over-length stringDrop the invalid fact; keep the stored value; never write a partial group
PE24Provider misses, rate-limits, times out, or has no configured keyKeep prior facts and stamps; report failed, never refreshed
PE25Two Runs write the same LinkedIn identityUnion source lineage, preserve concurrent groups, recheck identity, report refreshed only for the accepted group
PE26A concurrent writer changes or removes the row the canonical UUID namedRefuse the stale write; never insert a replacement row under the expected UUID
PE27Canonical write fails after the provider call completesRetry persistence with the durable provider result; make no second paid call
PE28Profile-only write while the email is staleNever stamp the hot time; keep the email refresh available
PE29Email refresh failsPreserve the old hot stamp as well as the old email value
PE30Avatar rehost failsKeep the stored avatar, mark the selected field failed, and write no newer tier stamp
PE31A subject reaches the write with no normalizable LinkedIn identityRefuse it with a diagnostic; a silent no-op must not read as success
PE32100 subjects, long Unicode summaries, or lineage beyond the output limitTrim lineage and diagnostics with exact omitted counts; return payload_too_large when required rows cannot fit
PE33One item fails inside a 100-item batchPreserve ordered sibling results and bounded diagnostics
PE34Root deadline, cancellation or cost limit reached between subjectsStart no next child and make no late canonical write; unstarted rows use not_read
PE35Meter read or write failsStop paid work with metering_unavailable; do not swallow the fault
PE36Publish refusal, a missing grant, or a missing provider connectionPreserve the lifecycle refusal; never publish a bypass or a synthetic result
PE37A legacy people helper is imported into src.agenticImport contracts fail; cross the boundary by the registered component ID
PE38email requested under a profile-only executorReport the field unavailable; profile enrichment still succeeds
PE39Warm stamp inside 90 days, hot stamp beyond 30, email requestedJudge each field by its own tier: request the email, never report it current, and hold the hot stamp

People Enrich input adapter design stress cases

ENG-2301 owns these cases. The runtime baseline is agentic-platform. The docs baseline is main. The People Search producer exists on runtime trunk through ENG-2280, ENG-2291 and ENG-2293. Use its frozen output format for adapter tests and live producer handoff checks.

IDInput or eventRequired result
PI01Empty input, 101 subjects, invalid UUID or extra client display fieldsExisting contract rejects before reads
PI02All four reference tags in one requestResolve identities; preserve first-input order and all indexes
PI03Missing CRM person; foreign CRM personSame batch refusal before canonical reads or provider work
PI04Missing source Run; foreign source RunSame batch refusal before canonical reads or provider work
PI05One inaccessible tenant reference follows valid subjectsNo partial dispatch or paid call
PI06CRM LinkedIn key, URL, mixed case, query or trailing slashUse the existing normalized individual profile identity
PI07CRM identity absent, blank, wrong type, company URL or path traversalItem unresolved; valid siblings survive
PI08CRM record contains private notes, email or manual profile changesRead identity columns only; no CRM mutation or global fact copy
PI09Succeeded source Run has a complete or partial people.search outcomeResolve the selected row from its frozen output
PI10Source Run waiting, running, failed or cancelledItem stale_reference; start no work for it
PI11Wrong output slot, capability, version, envelope shape or more than 100 rowsItem stale_reference; no fallback to other output
PI12Selected result ID missing, repeated or malformedItem stale_reference; never choose the first duplicate
PI13Saved row contains another tenant tag, provider ID or display-only identityItem stale_reference; do not follow references or guess
PI14Unselected row has invalid display; selected row has extra private displayIgnore display; use only selected identity fields
PI15Saved canonical reference and canonical_id name different UUIDsItem identity_conflict; do not select one
PI16Saved LinkedIn identity and current canonical identity differItem identity_conflict; no provider work for that item
PI17Saved canonical UUID is deleted; stored canonical profile is invalidItem stale_reference; no fallback or replacement insertion
PI18Same person appears as CRM, selected row, canonical UUID and URLOne work item; preserve the canonical guard and every input index
PI19Two canonical UUIDs normalize to one profileEvery input in that group has identity_conflict; no merged UUID
PI20Valid duplicates surround unresolved or stale itemsStable first-input order; failed inputs retain separate rows
PI21100 selections repeat one CRM ID or one Run IDDeduplicate lookup IDs; no per-selection query
PI22100 distinct CRM IDs or source Runs; large canonical ID setUUID batches at most 50; total preparation at most ten seconds
PI23Tenant or canonical query fails or times outBatch cache_unavailable; no person child dispatch or provider work
PI24Cancellation during a readPropagate cancellation; do not convert it to a cache miss
PI25Required result exceeds the byte cap after groupingExisting payload_too_large refusal before provider work
PI26CRM edited or source deleted after a completed preparation checkpointReplay the saved preparation; a fresh Run reads the current state
PI27Canonical row changes after preparationExisting component rejects the identity change before work or persistence
PI28Process stops before preparation checkpointsReads can repeat; do not promise a transaction across tenant and canonical reads

Composition and product surfaces

IDInput or eventRequired resultOwner
P01Front Door selects a valid product IDValidate the exact product schema; do not inject a text fieldENG-2283
P02Front Door selects a custom Agent/Workflow UUIDPreserve the existing custom-definition pathENG-2283
P03Compound request, missing input or ambiguous "stop that"Clarify once; do not invent a multi-start plan or cancel targetENG-2283, ENG-2297
P04Five products plus many custom definitionsInclude eligible products in the bounded shortlist; retain custom routingENG-2283
P05Signals discovery, company-set and saved-search startsPreserve root schemas, scores, smart-feed/diff behavior and bounded scopeENG-2282, ENG-2302
P06Signals thesis/web discovery has no firmographic equivalentKeep signal research; use supplied child search for identity and gatesENG-2282
P07Child retries, zero people or partial enrichmentReuse child Run keys; retain parent lineage and single root cost accountingENG-2282, ENG-2302
P08Select rows, navigate to Enrich or Find People, then reloadRestore tenant-scoped refs; require a separate start; preserve orderENG-2288, ENG-2289, ENG-2292
P09Switch organizations while a launch or result request is in flightClear selections and ignore the old tenant's late responseENG-2284
P10Stream disconnect, duplicate event or browser reload during email waitReconcile durable root and child Runs; no second start or duplicate rowENG-2284, ENG-2289
P11Capability unavailable, denied or version-changed after form loadShow the reason and retain valid inputs; require an explicit revised startENG-2284
P12Finished Run is reopened after Intelligence changesKeep the frozen Run output; do not replace it with current entity dataENG-2298, ENG-2289

Front Door routing evaluations

ENG-2297 owns these cases. They test one conversational turn, and no other surface. Two layers read one case set. The deterministic layer drives FrontDoorService with a scripted decision and runs in CI. The model layer replays the same cases through the real Agno runtime, scores the route with record_routing(expected=), and runs locally only. A row that trunk already proves names its existing test; it gets no second test.

The model layer scores the rows a request can produce. It does not score a broken vendor answer, a UUID the model never reads, or a live Run, so FD17 to FD23, FD26 and FD27 are proved by the deterministic layer alone.

The shortlist is per case, and it was one fixture for every case. A refusal that rests on what the tenant may start is not presented by a request alone. EMPTY_SHORTLIST in capability_routing_suite.py names the cases whose actor holds no run.start, and the eval builds two empty halves for them.

⚠️ FD16 is model-scored, and FD17 is not. ENG-2341 made one rights answer decide both halves of the shortlist, so an actor that may start nothing reads nothing and the FD16 reply is fixed. FD17 has no such fixture. Three of the five stable IDs are searches, so a partial install always leaves a near neighbour of the missing product, and a delegate to that neighbour is a routing judgement rather than a disclosure. The deterministic layer proves FD17, and the case still fixes that the two replies match.

The case set lives twice, and one change edits both.ac-python-api tests/agentic/entry_control/front_door/capability_routing_suite.py holds ROUTING_CASES, and this section holds the same 28 rows. They sit in two repositories, so no build compares them. test_design_page_sync.py reads this page from the monorepo working tree and fails when the IDs, their order, their group or a required result differs. Run it with the rest of the front door suite.

Positive intents

Each row takes one direct request and two paraphrases.

IDRequestRequired result
FD01Find companies that match a descriptionDelegate company.search. Input holds no text key
FD02Add detail to companies the person already hasDelegate company.enrich
FD03Find people at named companiesDelegate people.search
FD04Add detail or work email to people the person already hasDelegate people.enrich
FD05Find companies by an event or a changeDelegate signals.search

Near-miss separation

Three of the five IDs are searches, so these rows fix the boundary between them.

IDRequestRequired result
FD06"Find companies that are hiring"Clarify once. signals.search owns an event-shaped company request, and company.search holds no event filter
FD07"Get me the CTOs and their emails"Clarify once. Two capabilities cannot start on one turn
FD08"Find CTOs at fintech companies in Berlin"Delegate people.search. The published company brief is its own child, and not a second start
FD09"Find companies, then enrich them"Clarify once. Do not start the first step and imply the rest

An event-shaped company request belongs to signals.search (ENG-2338).company.search filters on country, industry, employee band and revenue band, and provider_filters refuses an empty filter set, so it can answer no event request. Hiring is the first member of the canonical signal vocabulary. FD06 required a question about which product the person meant, and there is one product, so the question is about the field the brief needs instead. The row names the owner, so a turn that selects signals.search and then asks for that field scores a pass, and a turn that selects company.search does not. The published signals.search description carries the event vocabulary, and a front door rule carries the boundary.

Missing input

One required field is absent in each row. The turn asks one question and starts no Run.

IDCapabilityRequired result
FD10company.searchClarify naming the failing field path. No Run
FD11company.enrichClarify naming the failing field path. No Run
FD12people.searchClarify naming the failing field path. No Run
FD13people.enrichClarify naming the failing field path. No Run
FD14signals.searchClarify naming the failing field path. No Run

Refusals

IDInput or eventRequired result
FD15A task no published capability performsAnswer that no published capability matches. No Run
FD16The actor holds no run.start rightThe same non-disclosing answer as FD17. The reply separates no tenant state
FD17The tenant installed three of the five, and the request needs a fourthThe same non-disclosing answer as FD16. No Run
FD18The model writes a stable ID outside the fiveThe decision is refused. The turn fails and creates no Run
FD19The model writes a well-formed UUID the shortlist never heldRefuse deterministically. No Run
FD20The model writes the UUID of a product-bound executorRefuse. A bound executor is never a custom capability
FD21The model writes the UUID of another tenant's definitionRefuse. The reply names no foreign row
FD22The capability is disabled between the shortlist read and the startRunManager resolves again and fails closed. The turn reports the reason

The tool boundary

IDInput or eventRequired result
FD23Any shortlist reaches the promptThe prompt holds no executor UUID, no scope and no output schema. It holds no tool name either, except the four stable IDs a tool shares its name with
FD24The message names a provider by nameThe selectable set stays the five stable IDs and the custom UUIDs. A provider is never one of them

Four tools carry the name of the capability that wraps them: company.search, company.enrich, people.search and people.enrich. FD23 reads the tool vocabulary minus those four, so the shared name is not read as a leak.

Preserved behavior

These rows keep their Phase 6 and ENG-2283 tests. The exit gate cites each node ID and resolves it by import.

IDInput or eventRequired result
FD25A custom Agent or Workflow is selected by UUIDStart it with the message-text fallback
FD26One live Run, and a clear request to stop itCancel through RunManager. Cancel stays idempotent
FD27More than one live Run, and "stop that"Clarify once. Cancel nothing
FD28An answer or a clarify decisionNo Run on either decision

Run cadence

The model layer calls a paid vendor API and it is not deterministic, so it carries the eval marker and pytest.ini excludes that marker. CI never runs it. It runs on a trigger instead, and the owner is the author of the change that fires the trigger.

Three changes invalidate the accuracy number. Each one is a trigger:

TriggerWhat changes
The Front Door promptA new rule, a reordered instruction or a new example
A capability name, description or input schemabuild_capability_manifest writes the text the model reads
The Front Door modelFRONT_DOOR_MODEL: the provider, the model, the version or the temperature
Bash
ANTHROPIC_API_KEY=sk-... uv run pytest \
    tests/agentic/entry_control/front_door/test_routing_accuracy_eval.py \
    -m front_door_eval --no-cov -p no:xdist -o "addopts=" -v

Record the score in the pull request that carries the change.

The observed band is 0.79 to 0.86. Four runs on claude-haiku-4-5 scored inside it. The floor is 0.70, and it is a regression bound rather than a target: a router that guesses over five products and four other outcomes scores near 0.15. A run inside the band is a pass. A run below the band is a finding, and the run prints the missed requests. Raise FRONT_DOOR_ACCURACY_FLOOR only when a prompt change fixes a miss and a second run holds the higher number.

Shared frontend shell stress cases

ENG-2284 owns these cases. ac-frontend src/components/capabilities/__tests__/ and the browser exit suite prove them. Product forms, result tables, provider execution and asynchronous canonical reads keep their existing owners.

IDCaseRequired result
F01Gate absent, false, or another valueHide all five navigation entries; direct URLs show not found
F02Gate true on desktop and mobileAll five routes load; Search and Enrich navigation works; legacy URLs still load
F03No authenticated userExisting login guard applies before product reads
F04Available, unavailable, unauthorized, empty catalogue, read failureShow distinct states; never invent absent schemas
F05Draft contains unknown fields or invalid nested valuesServer decides validity; map bounded paths without rejected values
F06Invalid JSON values or input exceeds 32 KiBRefuse before POST; retain the draft
F07Double click while POST is unresolvedSend one request with one key
F08Server admits a Run but the response is lostExplicit retry uses the frozen input and key; restore the same Run
F09Reload during an uncertain startRestore the attempt; require an explicit retry; no automatic POST
F10Edit input while the earlier start is uncertainRefuse replacement until the earlier attempt is resolved
F11Version conflict, permission change, unavailable binding, budget refusalRetain the draft; show the reason; require an explicit corrected start
F12HTTP 200 contains waiting, failed or duplicateDisplay the durable status; do not claim success from HTTP status
F13Reopen an admitted Run after reloadGET the stored Run ID; never POST
F14Stream gap, reconnect, quiet wait or failed transportRe-read durable state; polling continues while the Run is nonterminal
F15Duplicate hints or refresh while a read is in flightCoalesce reads; no duplicate result state or stale response overwrite
F16Child completes while root waitsKeep following the root
F17Root completes, fails or cancelsRefresh frozen output; stop live resources after durable terminal state
F18Navigation, organization change, logout or impersonationStop resources; clear visible state and selections; retain recovery records in their original scope; ignore late responses
F19Ordered selection repeats a ref or exceeds 100 refsDeduplicate in order; reject excess refs; never put refs in URLs
F20Reload restores malformed, old-scope or wrong-entity selectionClear invalid state; require selection again; never auto-start
F21Session storage fails before launchShow recovery failure; send no POST
F22Search result points to a canonical entityPreserve its typed ref; never infer CRM promotion or enrichment
F23Empty, partial, failed, cancelled or newer canonical factsPreserve the durable status and output; product adapter owns result interpretation
F24Run read returns 403 or 404 after reloadStop following; show the read failure; never launch replacement work
F25Lost response, then rate-limit or auth refusal before replay lookupKeep the original request and key; recover the first Run when retry succeeds
F26Response body fails after HTTP 200Keep the attempt uncertain; retry with the original key

Company Search journey stress cases

These cases define ENG-2290. They use runtime agentic-platform and documentation main. The shared shell cases F01-F26 and provider cases CP01-CP28 retain their owners.

IDCaseRequired result
CJ01Gate off; direct route or navigationUse F01; no Company Search entry
CJ02Loading, unauthorized, uninstalled, read errorKeep the frame; show each reason; disable a new start
CJ03Missing schema metadata or version other than 1Show unsupported contract; do not guess controls or start
CJ04Fresh supported schemaUse published limits and default 50; select only supplied
CJ05Supplied domains and organization LinkedIn URLsKeep order and raw identities; no CSV or name inference
CJ06No companies, 100 refs, 101 refsRequire companies; accept boundary; refuse overflow without truncation
CJ07Provider-only sourceRequire a supported filter; omit companies
CJ08Both sources; source togglesInclude companies only when supplied is selected; no implicit provider source
CJ09Four filters; no filter; blank filterUse documented fields only; omit blank lists
CJ10Duplicate filters; more than 20 values; invalid country or bandShow a field error; no silent broadening
CJ11Unknown provider industryKeep text; executor rejects before paid work; failed Run remains visible
CJ12Count 0, 1, 100, 101, decimal or blankAccept schema boundaries only; never clamp
CJ13Unknown facts or fewer rows than targetShow unknown facts; do not refill or enrich
CJ14Double click, lost response, reload, refusalReuse F07-F13 and F25-F26; retain frozen input and key
CJ15Queued, running, approval wait, provider waitShow durable Run state; lock new starts; retain result frame
CJ16Succeeded complete, empty, partial with rows, partial with zero rowsRead nested company_search; distinguish all outcomes
CJ17Failed or cancelled with no outputShow durable failure or cancellation; never show empty success
CJ18Succeeded with missing output, bad IDs, duplicate IDs or wrong versionShow result error; disable selection; never invent rows
CJ19Funnel counts, source failures, omitted diagnostics, estimated usageShow frozen values and labels; do not recompute or double-count
CJ20Missing display fields; markup or unsafe URLs in factsUse identity fallback and escaped text; do not execute or guess links
CJ21Select, deselect, select all, clear; up to 100 rowsSave ordered search-row refs; preserve the Run and result IDs
CJ22Reload or leave and return to the same successful RunRestore the frozen request and matching selection without POST
CJ23New search admitted, refused or uncertainClear prior selection only after admission; never mix two Runs
CJ24Stale refs or saved selections from another RunOffer selection again; do not select guessed rows
CJ25Storage write fails during selectionShow the failure; retain the previous saved selection
CJ26Actor, tenant or impersonation changes while visibleClear draft, rows and selection; reject late responses through the shell
CJ27Stream duplicates, reconnect or terminal refreshUse F14-F18; no duplicate rows or changed selection
CJ28Navigate to Enrich or PeopleRetain shared refs; no automatic start; handoff controls keep their existing ticket owners
CJ29Keyboard, desktop, mobile and long fieldsReach every control; scroll table inside frame; no page overflow
CJ30Live supplied-only search and provider requestCapture desktop video and mobile screenshot from real Runs; prove no automatic second start

Company Enrich journey stress cases

These cases define ENG-2288 on runtime agentic-platform and docs main. The shared shell and backend cases retain their owners. This slice uses frozen details and defers live canonical reads.

IDScenarioExpected rule
EJ01Gate off; direct URLKeep the existing not-found behavior.
EJ02Loading, read failure, uninstalled, disabled, unauthorizedShow distinct states. Preserve the form frame and valid draft.
EJ03Unsupported version or missing field metadataDisable new starts. Do not guess a schema.
EJ04Fresh supported formUse published Basic and stale defaults. Require explicit start.
EJ05Search selects rows in a non-table orderPreserve selection order and source Run/result IDs.
EJ06Search includes canonical and domainless companiesSend search-result refs. Resolve identity on the server.
EJ07Select all, deselect, clear, zero selectionsDisable handoff at zero. Do not send an empty Run.
EJ08Handoff, back, reloadRestore same-tab scoped refs without a POST.
EJ09New Search refs arrive while Enrich has an uncertain or active attemptKeep the existing frozen request. Require recovery or completion first.
EJ10New refs arrive after an older Enrich Run endedOffer explicit use of the new selection. Do not silently replace restored input.
EJ11Missing or malformed saved selectionExplain how to select again. Never rebuild refs from display text.
EJ12Stale row, deleted Run, wrong result kindShow the server refusal or item diagnostic. Preserve valid input.
EJ13Foreign source Run or ownership changesPreserve non-disclosing refusal. Never guess identities or expose cached display data.
EJ14User chooses direct entry while a Search selection existsUse an explicit input mode. Do not append hidden subjects.
EJ15Direct domains or canonical UUIDsKeep typed inputs, order and errors. Do not accept company names as identities.
EJ16Zero, one, 100 and 101 subjectsRequire 1-100 before POST. Reject overflow without truncation.
EJ17Duplicate domain and canonical refs name one companyLet the server resolve identity. Show one result with all input indexes.
EJ18Malformed UUID, empty domain, overlong value, person or CRM refReject invalid shapes. Let the executor decide semantic identity validity.
EJ19Basic, Standard, explicit field subsetUse published choices. Preserve the submitted field order.
EJ20Change Standard to Basic with extra fields selectedShow the changed field set. Never submit hidden unsupported fields.
EJ21Empty fields, duplicate fields, Deep, private or unknown fieldReject the request. Omitted fields mean the whole preset, not an empty selection.
EJ22Missing-only versus stale refreshExplain retained stale values. Never promise a provider will fill every field.
EJ23Double click, response lost, reload, repeated refusalReuse the shared frozen input and key rules F07-F13 and F25-F26.
EJ24Session storage failsRefuse unsaved starts. Preserve the previous selection and show the failure.
EJ25Actor, tenant, logout or impersonation changesClear visible product state and ignore old-scope responses.
EJ26Queued, running, approval waitShow durable Run state. Keep new starts locked. Link existing approval controls.
EJ27Disconnect, duplicate hints, terminal eventReconcile through the shared store. Do not append duplicate rows.
EJ28Complete, partial, empty, failed or cancelledKeep Run status and product outcome separate. Failure is not empty success.
EJ29Missing output, wrong capability, version or row identityShow result error. Do not fabricate rows or links.
EJ30Deduplicated results, bad or repeated indexesMap through frozen request indexes. Reject invalid row partitions.
EJ31Current, retained, refreshed, not found, pending, failed, cancelledDisplay the reported field state beside its value. Value presence does not prove success.
EJ32Hit, miss, stale, not_read, absent timestampsShow the reported cache decision. Do not invent per-field freshness.
EJ33Estimated/settled usage, zero cost, unavailable Run meterLabel frozen usage. Keep unavailable distinct from zero. Do not add root and child totals.
EJ3431 KiB overflow before or after paid workShow payload failure. Preserve known usage. Explain that prior writes can remain. No automatic batch split or retry.
EJ35Canonical ID without a CRM companyShow the canonical ref and frozen details. Do not build a CRM link.
EJ36Ordinary user opens company detailsShow frozen details and Run Explorer. No admin read or guard bypass.
EJ37Canonical data changes or vanishes after the RunKeep frozen results unchanged. This slice makes no live canonical read.
EJ38Null canonical ID or unresolved itemShow the diagnostic. Do not enable an entity detail link.
EJ39Omitted diagnostics and source IDsShow exact omission counts. Do not claim full lineage.
EJ40Markup, unsafe URLs, long text or monetary valuesEscape text. Validate links. Use existing money and firmographic helpers.
EJ41Keyboard, narrow viewport, many fieldsKeep every control reachable. Keep horizontal table scroll inside the frame.
EJ42Desktop Search -> Select -> Enrich; mobileUse a live API and worker. Capture a desktop video and a mobile screenshot. Verify no automatic start.

Signals Search journey stress cases

These cases define ENG-2294 on runtime agentic-platform and docs main. The shared shell cases F01-F26, persona cases SP01-SP33 and saved-search API rules retain their owners.

IDCaseRequired result
SJ01Gate off; direct route or navigationUse F01; no Signals Search entry.
SJ02Loading, denied, unavailable, empty catalogue or read failureKeep the frame, show the exact state and disable a new start.
SJ03Missing schema field, incompatible constraint or version other than 1Show unsupported contract. Do not guess controls or start.
SJ04Fresh direct formDefault to discovery and require an explicit start.
SJ05Discovery mode with company refs left from company-set modeOmit all company refs. Do not send hidden state.
SJ06Company-set mode with domains, LinkedIn URLs, canonical IDs and prospect IDsPreserve each type and line order. Let the server resolve identity.
SJ07Company-set mode with zero, one, ten or eleven refsRequire 1-10. Reject overflow without truncation.
SJ08Company name, malformed URL or malformed UUIDShow the field error. Do not infer an identity.
SJ09ICP only, company criterion only or bothAccept each valid brief. Keep the unsaved draft; send and store a normalized execution copy.
SJ10Missing, blank or whitespace-only ICP and no company criterionRefuse before POST and retain the draft.
SJ11One to ten company criteria; blank, whitespace-only or 201-character criterionEnforce the documented bounds after whitespace trimming. Never change the saved draft silently.
SJ12Required and optional company criteriaPreserve each flag and the displayed order.
SJ13Each persona field alone or several fields togetherUse the shared persona contract without a copied schema.
SJ14Empty, duplicate, blank, 21-value or invalid-country personaShow bounded field errors and send no POST.
SJ15A separate signal theme or result limit appears in schema or stored dataPreserve stored unknown data, but do not add an unsupported V1 control.
SJ16Source mode changes after validation errorsClear hidden field errors only. Preserve visible brief values.
SJ17Direct start is admittedOpen Prospect Review once with the admitted Run ID. Do not start again.
SJ18Direct start is refused, uncertain or restored after reloadStay on the form and use F07-F13 and F25-F26. Do not redirect.
SJ19Replayed or old admitted direct attempt is reopenedShow the shell state and an explicit Prospect Review link. Do not auto-redirect.
SJ20Prospect Run is queued, running, empty, partial, failed or cancelledLet Prospect Review render durable state. Do not build a second result table.
SJ21Saved-search list has zero, one or several pagesDistinguish empty from failed. Preserve stable ordering and cursors.
SJ22Select a saved search or reload its query URLPut only its ID in the URL and read the detail. Do not expose its brief in the URL.
SJ23Create with a valid brief and unique nameStore once, select the returned search and show its exact detail.
SJ24Blank, overlong or duplicate nameKeep the draft and map the server refusal.
SJ25Edit documented fields in a brief that has unknown fieldsPreserve every unknown field in the submitted brief.
SJ26Rename a saved search with an incompatible legacy personaSend a name-only patch and keep the brief unchanged.
SJ27Edit a brief with legacy persona fieldsRequire a complete reviewed correction. Never drop, merge or guess fields.
SJ28Another editor changes the saved searchA 409 keeps the draft and offers an explicit detail reload.
SJ29Delete is cancelled or confirmedCancellation writes nothing. Confirmation removes the search but does not cancel a Run.
SJ30Saved-search start is admittedStay in the workspace, follow the durable Run and offer Prospect Review.
SJ31Same-key starts overlap while the search changes; the owner crashes before or after snapshot completion; a response is lost or the page reloadsFreeze one normalized input under the exact surface claim. A live loser retries after one second. One caller reclaims after 30 seconds. A lost fence starts no Run. Every completed claim admits or recovers one Run.
SJ32Capability binding changes after the form loadsReturn a version conflict. Never accept or expose an executor UUID.
SJ33Stored brief is invalid or the complete input exceeds 32 KiBStart no Run. Keep the search readable and show correction fields.
SJ34No saved Run versus latest Run with no changesShow distinct states. An empty diff is not the same as no Run.
SJ35Latest diff has added, updated and removed rowsShow reported change kinds. Do not recompute them from live prospect data.
SJ36A newer Run publishes between diff pagesOn 409, discard the cursor and offer an explicit reload. Never join two Runs.
SJ37Diff row opens prospect detailKeep its diff Run ID in the Prospect Review URL and use the existing drawer.
SJ38Watch, dismiss or promote from the routed prospectPreserve the Phase 4 state rules and promotion flow. Signals adds no CRM write.
SJ39Actor, organization, logout or impersonation changes during any requestClear visible scoped state and ignore every old-scope response.
SJ40Markup, long text or unsafe evidence URL appearsEscape text, contain overflow and link only safe HTTP or HTTPS URLs.
SJ41Keyboard, desktop and narrow viewportKeep every control reachable. Keep scrolling inside its result frame.
SJ42Live direct start and saved-search startCapture desktop video and mobile screenshot. Prove one start and correct Prospect Review handoff.
SJ43Direct start, saved create, saved brief patch and saved start receive ICP with 2,000 or 2,001 characters after trimmingApply one normalization rule. Accept 2,000. Refuse 2,001 and retain the unsaved draft.
SJ44The same company ref repeats, or two ref types resolve to one companyKeep the first resolved company, count later refs as dropped and do not research the company twice.
SJ45A domain or LinkedIn key maps to conflicting stored identitiesAdmit no company for that ref. Show the bounded conflict diagnostic and do not fill the freed position.
SJ46An unmatched valid domain, unmatched LinkedIn URL or missing canonical IDKeep the domain as a research seed. Drop the other two with their unresolved or not-found diagnostic. Keep unrelated valid refs.
SJ47A prospect ID belongs to another organizationReturn the same unresolved result as a missing prospect. Expose no foreign identity or cached display data.
SJ48A saved-search start is redelivered after the 24-hour snapshot window or the 396-day Run windowRecover an existing Run through its stable key without a snapshot. If the Run also expired, treat the delivery as a new request and read current state.

Exit evidence

Signals shared persona design cases

ENG-2324 owns these cases. The shared persona contract owns these rules.

IDScenarioRequired outcome
SP01Each of four persona fields aloneAccept the shared contract and omit the other fields
SP02Multiple values and multiple fieldsOR within lists; AND between fields
SP03Missing persona, empty object or only empty fieldsReject before company or paid work
SP04Explicit null, scalar list value or non-string itemReject with the field path
SP05Whitespace, blank item or case-insensitive duplicateApply shared normalization and reject invalid or duplicate values
SP06Unknown key, legacy key or mixed schemasReject without dropping a criterion
SP07Twenty values versus twenty-one; 1,000 versus 1,001 charactersKeep exactly the People Search bounds
SP08Valid, lowercase and unknown country codesUse the existing ISO country validator; do not add aliases
SP09Title matches; different title; related title onlyUse exact case-insensitive matching; do not expand titles
SP10Matching, absent and contradictory person countryKeep matches and unknowns; exclude contradictions
SP11Employer country matches but person country is absentKeep unknown person country; never substitute employer country
SP12Department or seniority has unsupported observed vocabularyKeep unknown with a diagnostic
SP13Duplicate identity has an explicit contradictionExclude the whole merged group once
SP14Duplicate identity has missing facts and a supported matchRetain the group and its source evidence
SP15All candidates fail shared filtersKeep the company; preserve completed-search evidence
SP16Unknown facts survive deterministic filteringRequire evidence before persona-fit selection; never claim verified fit from absence
SP17One provider fails and another returns candidatesKeep usable candidates; do not claim complete no-match evidence
SP18Many alternatives across every fieldKeep every alternative in one query per company/provider; no Cartesian fan out
SP19Maximum Unicode persona and candidate fieldsPreflight all fixed intermediate envelopes before company work; reduce candidate allowance, never persona criteria
SP20Discovery and company-set inputs are invalidRefuse at the first scope step before reads and paid work
SP21Create or patch contains an invalid briefRefuse before a storage write with actionable field errors
SP22Manual start contains an incompatible stored briefStart no Run; return correction fields
SP23Scheduled dispatch contains an incompatible stored briefRecord input_unbuildable; start no Run; preserve schedule
SP24Saved-search read or name-only patch contains a legacy personaKeep the original brief readable and permit the name change
SP25Legacy title, family, function, seniority or locationRequire explicit correction when equivalent meaning is not proved
SP26Legacy and shared criteria coexistDo not overwrite, merge, guess or drop criteria
SP27Dry run sees already valid shared criteriaReport unchanged; perform no write
SP28Confirmed correction preserves meaningKeep a private original; validate and write only the reviewed persona
SP29Report is rerun after the same correctionReport already applied; perform no second write
SP30Another editor changes the brief or update tokenRefuse the stale correction; preserve the newer edit
SP31Search is deleted or belongs to another organizationTreat it as absent; perform no write
SP32Correction fails partway through a batchKeep per-row results and originals; resume without duplicate writes
SP33A corrected search has watch members and a schedulePreserve IDs and relationships; next start uses the corrected brief
SP34Historical Run contains the legacy schemaRead frozen input and results without new persona validation
SP35An admitted legacy Run can still resume at rolloutHold rollout until it finishes or is cancelled; do not rewrite replay data
SP36CLI creates, reads, corrects and starts a saved searchPass the shared JSON unchanged and preserve field errors
SP37Frontend correction form is absent from trunkKeep ENG-2294 ownership; verify the API/CLI correction path here
SP38Capability composition or enrichment changes are proposedKeep those changes in ENG-2302 and their existing owners
SP39Persona fits the root but its six-call plan exceeds the tool limitRefuse before company work; never truncate calls or break the positional join

Signals company lane design cases

ENG-2282 owns these cases. The company lane contract owns the implementation rules.

IDScenarioRequired outcome
SC01Discovery has a valid thesis and personaKeep web research and the existing scoring brief
SC02Discovery produces zero companiesSkip Search and Enrich; preserve the empty Signals result path
SC03Company set contains stored canonical IDsAdmit only those IDs; keep the ten-company limit
SC04Company set contains a new domainMatch the exact domain after research; admit only its resolved ID
SC05New domain remains missing or conflictsAdmit no replacement ID; retain the unresolved scope evidence
SC06References repeat an identityPreserve one canonical company and its existing evidence
SC07Name-only, deleted or inaccessible referencesKeep existing scope refusal or unresolved behavior; do not invent an identity
SC08Saved search watches more than five companiesKeep the existing rotation and permit new discovery within ten results
SC09Root input is malformed or has no usable briefRefuse before provider work
SC10Research returns an out-of-scope companyExclude it before child enrichment and prospect promotion
SC11Thesis has no firmographic equivalentUse supplied Search without translating free text into provider filters
SC12Search returns rows in a different orderMatch canonical IDs and input indexes; preserve research order
SC13Search returns unknown, replaced or duplicate IDsRefuse the invalid handoff; do not attach another company's signals
SC14Search excludes all companiesSkip Enrich and pass an empty scoring batch
SC15Enrich returns no canonical IDExclude that company and retain other valid results
SC16Enrich returns retained facts or partial field failuresKeep the company when its canonical ID remains valid
SC17Enrich returns a different canonical IDRefuse the handoff; do not move signal evidence
SC18Research Agent or Skill can call private enrichmentPublication/configuration tests fail; one paid profile path is required
SC19Enrichment requests unused profile fieldsConfiguration tests fail against the explicit field list
SC20Empty wrapper result is replaced by missing outputRefuse; missing output does not mean zero results
SC21Child result contains a truncation marker or wrong versionRefuse before scoring and promotion
SC22Ten companies contain maximum Unicode text and evidenceMeasure complete inputs; refuse overflow without silent evidence loss
SC23Worker retries after a child starts or completesReuse the same child key and frozen output
SC24Child or root is cancelled; deadline or budget expiresStop subsequent work and prevent complete Smart Feed publication
SC25Meter or durable storage failsPreserve the platform failure; do not add a second aggregate charge
SC26Child binding is absent, disabled, wrong-version or cross-tenantRefuse publication before creating the root workflow
SC27Wrapper publication fails after another wrapper succeedsReturn published IDs and the exact refusal for recovery
SC28Signal scoring, company scoring and prospect promotion runPreserve evidence, grade reuse, scoring and no direct CRM writes
SC29Saved-search baseline changes after a Run completesKeep the frozen result, diff rules and publication behavior
SC30Stream reconnects or replays child eventsPreserve Run lineage and existing replay semantics; no second product start
SC31Warm company row has only country_codeDerive the scoring country name with the shared converter; preserve an existing country name
SC32Custom workflow wraps the complete Signals graphRefuse the fourth workflow level; preserve the existing depth cap and old snapshots

Signals people lane design cases

ENG-2302 owns these cases. They use the shared persona contract from ENG-2324. The people lane contract owns the implementation rules.

IDScenarioRequired outcome
PL01One to three promoted prospects have valid web candidatesKeep the existing six-call maximum and stable company-provider-result order
PL02No prospect survives opportunity scoringStart no People Search or People Enrich child; preserve the empty product path
PL03A provider returns no rows and all provider calls completeSearch the remaining refs; permit no_matching_people only with complete evidence
PL04One provider fails and another returns usable peopleAttach valid people and retain the failed search evidence
PL05All providers failStart no supplied Search; persist each exact reason only while its prospect is pending
PL06A row has no valid LinkedIn person profileKeep it unresolved; never send it to People Search or attach it
PL07Providers repeat one LinkedIn identity for one prospectSend each observation with a stable index; accept one merged Search row and preserve every source observation
PL08One LinkedIn identity appears under several prospectsTreat employer ownership as ambiguous and attach it to none
PL09More than 25 observations exist for one prospectKeep the first 25 in stable order and report the dropped count
PL10The full supplied list has 75 rowsFit the 100-person Search bound without widening the three-company Signals bound
PL11Search reorders rows or merges distant input indexesJoin only by validated input_indexes, never by result position
PL12Search returns one valid row and one row with no input_indexesRefuse the unowned row; never infer ownership from position or identity
PL13Search repeats an input index across rows or returns an unknown indexRefuse the conflicting rows before persona scoring and preserve unrelated valid rows
PL14Search returns a schema-invalid row, wrong contract, version, product key, missing result or truncation markerRefuse the full child envelope before persona scoring and Enrich
PL15Search excludes every observed contradictionPreserve filter diagnostics and do not ask the model to restore an excluded row
PL16Search keeps an identity with unknown persona factsLet the agent assess evidence, but never claim verified fit from missing facts
PL17Complete Search returns no rowAttach no_matching_people with complete search evidence
PL18Partial Search returns no usable rowPersist the exact reason only while the prospect remains pending; do not claim no matches
PL19One prospect is complete-empty while another prospect has a provider or Search failureMark only the complete prospect no_matching_people; keep the other pending
PL20Persona-fit selects known rows with valid scores and reasonsPreserve the exact Search result IDs and compile only those Enrich subjects
PL21Persona-fit returns an unknown or repeated result IDRefuse the agent output before Enrich
PL22Persona-fit returns an invalid score, missing reason, extra field or oversized outputRefuse the closed result before Enrich
PL23Persona-fit selects no row after complete SearchAttach no_matching_people with a persona reason and complete evidence
PL24Persona-fit selects no row after partial SearchPersist the exact reason only while the prospect remains pending
PL25Search returns a canonical person IDPrefer its canonical reference for Enrich and retain Search and provider evidence separately
PL26Search returns no canonical person IDUse its normalized LinkedIn reference for Enrich
PL27A selected Enrich subject repeatsDeduplicate before the 100-subject limit and preserve all owning Search indexes
PL28The selected set is emptyTake the nested empty arm; do not start Enrich or add a capability wrapper
PL29Enrich returns rows out of orderJoin by validated input_indexes, never by result position
PL30A valid Enrich envelope repeats an input index across rows or omits a selected ownerExclude conflicting rows, preserve unrelated valid siblings and keep unresolved owners pending
PL31Any Enrich row is schema-invalidRefuse the full child envelope; do not recover siblings from invalid JSON
PL32Enrich returns a different canonical ID from the one Search suppliedRefuse that identity; do not move evidence to another person
PL33Enrich returns no canonical ID for one rowDo not write evidence or attach that row; retain valid canonical siblings
PL34Selected Exa and Parallel observations reach a canonical Enrich rowPersist them through the bounded Intelligence batch before prospect attachment
PL35The evidence batch returns a changed canonical IDRefuse that identity and its prospect attachment
PL36The evidence batch loses an acknowledgement or retries after a partial writeReuse the UUIDv5 source ID derived from the frozen Run, node, call and result positions; do not duplicate provenance
PL37The evidence batch failsDo not attach affected people or publish a complete Smart Feed result
PL38Profile fields are retained, refreshed, missing or fail independentlyRequest only the nine Signals fields and preserve the child field states
PL39The email child is still runningKeep the prospect people state pending and keep the root live
PL40Final Enrich output contains a pending selected stateRefuse the final handoff; pending is not a prospect contact state
PL41Email is verified with matching verification lineageMap the person contact state to verified without copying the address in Signals
PL42Email is settled not foundMap the person contact state to unavailable
PL43Email is retained without safe verification, failed or cancelledMap the person contact state to unverified; never invent an address
PL44One child or the attachment batch retries after a crashReuse child keys and idempotent prospect writes; do not duplicate people
PL45One bounded attachment batch changes several prospectsPublish one result.item_ready event for each changed prospect
PL46Child or root is cancelled, or a budget or deadline endsStop later work and publish no complete Smart Feed result
PL47A complete capability, agent, evidence or attachment payload exceeds its byte limitRefuse before the next consumer; never trim criteria, identity or evidence silently
PL48A people capability binding is missing, disabled, foreign or wrong-versionRefuse publication before writing the root definition
PL49Signals starts People Search directlyFit Signals, People Search and Company Search in the three workflow levels
PL50Signals starts People Enrich directlyFit Signals, People Enrich and People Email in the three workflow levels
PL51Either people capability is put behind another wrapperRefuse the fourth workflow level in publication tests
PL52An old published Signals Run resumes after deploymentUse its frozen graph and payload; do not apply new child contracts to replay data
PL53A stream reconnects while a child runs or after attachmentPreserve root-child lineage and existing replay semantics; start no second product Run
PL54The old persona agent or Skill declares contact or person-write ToolsPublication/configuration tests fail; People Enrich and deterministic workflow writes own those effects
PL55A partial-pending write races with or follows an existing found or no-match stateChange only a still-pending row; preserve either complete state and its reason

No duplicate product implementation is needed from ENG-2278, ENG-2295 or ENG-2279. Registry, installation, upgrades, people composition and product surfaces stay with their existing owners.

ENG-2285 owns direct API/CLI contract tests, including strict endpoint parity. ENG-2297 owns Front Door routing evaluations; its cases are FD01 to FD28 above. ENG-2296 owns desktop and mobile browser journeys. ENG-2300 owns the live FullEnrich evidence; unit tests cannot establish provider callback timing or account entitlement. Each scenario has one defined outcome and an implementation owner, and the implementation and exit tests pass. This page records the expected results. The test sets named at the top hold the executed proof.

The ENG-2285 harness proves the API and CLI cases against a live stack. Its start step checks three cross-field source rules. Every capability publishes the cross-field rules a schema can state. A rule over a provider vocabulary stays at execution time. Cross-field rule projection names both sets.

Browser exit journey cases

These ENG-2296 cases define the browser exit proof. They test the five product routes together. The shared shell cases F01-F26 and the journey cases CJ, EJ, PJ and SJ retain their owners. BJ01-BJ17 use recorded capability, Run and stream responses, so each durable status is exact and repeatable. BJ18 uses the live local API and worker, and it records the evidence.

F01 stays outside this table. The browser build fixes the rollout flag at build time, so one browser run cannot show both gate states. The route unit test owns the gate-off case.

IDCaseRequired result
BJ01Gate on; sidebar and in-product navigation at 1440 px and 390 pxEach of the five entries reaches its route. The product and segment links mark the current page.
BJ02Company Search succeeds; rows are selected; Company Enrich opensEnrich shows the same refs in selection order. It sends no start request.
BJ03Reload after the Company handoffThe same-tab refs return. No start request follows the reload.
BJ04A second browser context opens Company EnrichNo selection returns. The refs stay inside the tab that saved them.
BJ05People Search succeeds; rows are selected; People Enrich opensThe person refs follow the same rule as BJ02.
BJ06Signals Search starts from the direct form; the Run succeedsThe finished Run reaches the existing prospect review page.
BJ07Signals saved mode with an extra query keyThe route keeps only ?saved=<uuid>.
BJ08Invalid input in each of the five formsThe form shows the field error and sends no start request.
BJ09Two clicks on the start controlOne start request with one Idempotency-Key.
BJ10The Run reports queued, then running, then a terminal statusEach durable status appears. A new start stays locked while the Run is nonterminal.
BJ11The Run succeeds with the empty outcomeAn empty result appears. It is not a failure.
BJ12The Run succeeds with a partial outcome and rowsA partial result appears and it keeps the rows.
BJ13The Run fails, and the Run is cancelledThe durable failure or cancellation appears. An empty success never appears.
BJ14People Enrich has a ready profile while the email job runsThe profile appears and the email state stays pending.
BJ15The Run read answers 404 after a reloadThe read failure appears. No replacement start follows.
BJ16Each of the five products at 390 px and at 1440 pxThe page never scrolls horizontally. A wide result table scrolls inside its own frame.
BJ17Keyboard only, in each of the five productsEvery launch control is reachable and the start control accepts focus.
BJ18Live local API and worker; supplied-only sourcesRecord a desktop video and a mobile screenshot of Company Search to Enrich, People Search to Enrich, and Signals Search to prospect review. Record the viewport and the final authenticated URL.

Run attribution and telemetry stress cases

ENG-2298 owns these cases. The harness step attribute checks T01 against a live stack. The unit tests own T02 to T31.

IDScenarioRequired outcome
T01Each of the five IDs starts through a resolved definitionExact ID and version persist
T02Custom or legacy RunNull identity; no inferred capability metrics
T03Input spoofs identity or versionPublished binding owns identity
T04Definition is renamed or rebound after startRun identity stays fixed
T05API, CLI, Front Door and trigger start the same executorSame identity path; bounded source labels
T06Same start key is delivered twice or racesOne row and one observed start
T07Admission denies or requires approvalAttribution exists; wait emits no completion
T08Root starts, then a child or skill is republishedChild uses the frozen tree
T09Binding switches or old executor becomes disabledFrozen child UUID, contract and scope ceiling stay fixed
T10Parent belongs to another tenantNo snapshot access and no child Run
T11Child UUID is absent from a versioned treeRefuse; no live fallback
T12Legacy parent has no versioned treeKeep existing behavior; do not invent historical metadata
T13Nested child, shared child, agent leaf and rendered skillAll reachable execution data is frozen
T14Cycle, missing child, excessive depth or oversize snapshotRefuse before insert; publish uses the same size check
T15Rights are revoked, budget ends or parent is cancelledExisting admission and cancellation checks still apply
T16Retry, wait, resume or two terminal writersOnly the successful terminal write emits completion
T17Complete, partial, empty, failed and cancelled RunsFixed distinct completion outcomes
T18Failed provider returns no rowsFailure; never a zero-result success
T19Signals result or missing output countRead its supported shape; unknown is not zero
T20Parent and child both incur costCanonical cost deltas only; no duplicate tree totals or per-call rounding
T21Usage read fails or a provider settles lateUnknown stays unknown; meter remains authoritative
T22Enrichment fields succeed, fail or have no valueCount only explicit terminal field states
T23Thousands of tenants, UUIDs, prompts and versionsMetric label set stays bounded
T24Unknown route, custom UUID or absent expected routeFixed route bucket; unscored without an expectation
T25Route is correct but executor failsRouting correctness and execution failure remain distinct
T26Concurrent and nested Run scopesSpans receive only their own Run identity
T27Exporter raises or process stops after database commitNo Run failure; possible sample loss is explicit
T28Migration reruns; paired identity is null, valid or malformedExisting rows survive; invalid pairs and later edits fail
T29Existing Signals graph exceeds 32 KiB as a complete treeSeparate 256 KiB snapshot bound; input/result bounds stay unchanged
T30Indirect descendant grows after ancestor publishPreflight reports oversize graphs; start refuses without trimming or live fallback
T31Usage write retries or has no matching Run scopeOne observed cost delta per successful record call; unknown attribution emits none

People Search company scope

ENG-2293 owns PSC01 to PSC32. These checks cover composition, not the provider adapter in ENG-2291.

IDInput or eventRequired result
PSC01Supplied people with no scopePreserve the existing supplied path; no company child
PSC02One or 20 known company refsSkip the company child; pass the same refs to the selected sources
PSC0321 known refs, both forms, null, empty object or unknown scope fieldReject before child work
PSC04Valid supplied, Explorium or mixed company briefInvoke exactly one source-selected child; normalize defaults and omit companies for Explorium only
PSC05Free text, unsupported company field, or Explorium with missing filters or invalid industryReject before a child or provider call
PSC06Invalid people source, supplied without people, empty persona or excessive targetReject before company work; provider-only correctly omits people
PSC07Missing, disabled, foreign, ambiguous or wrong-version company bindingRefuse publication; create no draft
PSC08Published child upgrades after the parent Run startsExisting Run uses its frozen child; new Runs use their own snapshots
PSC09Known scope on a composed definition without a child grantPreserve normal admission refusal; branches do not widen authority
PSC10Company child waits or needs approvalStart no people source until the child succeeds
PSC11Child fails, is denied or is cancelledStop the parent; do not report an empty success
PSC12Child start repeats after a crash or lost responseReuse the child for the same parent Run and node
PSC13Child succeeds, then parent resumesConsume the frozen result; do not repeat company discovery
PSC14Company child exhausts the shared root budget or deadlineNo extra allowance or hidden retry; preserve the runtime stop
PSC15Child returns zero rows with complete search coverageEmpty people envelope; zero people reads and provider calls
PSC16Partial company child returns zero rowsPartial people envelope; preserve reasons; no no_matches claim
PSC17Partial child returns usable companiesSearch only these companies; final outcome stays partial
PSC1819, 20, 21 or 100 distinct child companiesKeep at most 20 in result order; truncate only above 20
PSC19Duplicate identities occur before and after the ceilingDeduplicate before the ceiling; preserve first appearance
PSC20One canonical company has domain and LinkedIn rowsOne canonical ref; preserve first appearance
PSC21One identity names conflicting canonical IDs or repeats a result IDReject the malformed child output
PSC22Child omits its envelope, uses another version or exceeds the byte boundFail closed before people work
PSC23Display fields claim another company identityIgnore display fields; map only stable refs and canonical IDs
PSC24Every people source is selectedPass exactly the same resolved scope to each source
PSC25Supplied person has only an employer display nameKeep the employer unknown; do not infer company membership
PSC26Company child has 100 diagnostics and omitted diagnosticsBound final diagnostics; preserve omitted counts and scope truncation
PSC27Child diagnostic has a company input indexMap it to a scope warning with item_index null
PSC28Earlier root spend, company calls and people callsRead root cost once, including empty/supplied branches; sum local call counts only; refuse meter failure
PSC29Combined output exceeds 31 KiBTrim optional evidence/display only; refuse if identities cannot fit
PSC30Signals calls People Search, which calls Company SearchFit the existing workflow depth and snapshot bounds without a wrapper
PSC31Supplied person points to an inaccessible source Run; company scope later has zero or more rowsRefuse with stale_reference before company spend; read no person cache
PSC32Child lookup has a wrong tenant, parent, source-selected hashed node key, source or frozen capabilityRefuse before people work; never dispatch from the reader

People Search journey stress cases

ENG-2292 owns these product cases. Runtime uses agentic-platform; design docs use main. The shared-shell cases F01-F26 continue to cover admission, recovery, permission and stream behavior.

IDInput or eventRequired result
PJ01Rollout gate off or onKeep legacy pages; register the People Search view only when enabled
PJ02Loading, unavailable, unauthorized or failed capability readKeep distinct shared-shell states; send no start
PJ03Unsupported version, missing bounds or unknown sourceDisable new starts; retain saved recovery
PJ04Fresh direct formSelect supplied only; no paid source or automatic start
PJ05Supplied URLs, canonical IDs and restored search-row refsPreserve typed identities and order; do not accept CRM refs
PJ06Zero or 101 supplied peopleReject locally; require 1 to 100
PJ07Provider-only or mixed people sourcesRequire a company scope; omit people only for provider-only
PJ08Empty or repeated sourcesReject locally
PJ09Supplied-only without scopeOmit scope; search supplied identities
PJ10Known scope with 0, 1, 20 or 21 companiesAccept 1 to 20 only; do not truncate
PJ11Company Search selection has 1 to 20 rowsFind People opens known scope with ordered refs; sends no POST
PJ12Company Search selection has more than 20 rowsExplain the limit; require a smaller selection
PJ13Company brief selects supplied, Explorium or bothReuse Company Search controls; send the structured branch
PJ14Company brief has invalid filters, no source or invalid targetKeep the invalid draft; block start
PJ15Empty persona or empty optional listRequire one criterion; omit blank optional lists
PJ1621 persona values, case-insensitive duplicates or long textReject locally with a field error
PJ17Several titles and person countriesOR within fields; AND across fields; do not use employer country
PJ18Unsupported provider department or seniorityExplain executor validation; show failed Run without losing the draft
PJ19Target blank, fractional, zero, 1, 10 or 11Accept integer 1 to 10; do not silently clamp
PJ20Refusal or refreshed availabilityPreserve edited fields and show field errors
PJ21Saved request and a newer company selectionRestore the saved request first; offer explicit scope replacement after it ends
PJ22Timeout, double click, reload or reconnectPreserve the frozen key; no automatic POST; lock edits while unresolved
PJ23Queued, running, waiting, failed or cancelled RunShow durable state; no selectable terminal output from a failed Run
PJ24Complete result, empty result or partial result with zero rowsShow distinct outcomes; partial zero is not no matches
PJ25Missing, dropped or malformed envelope; wrong capability/versionShow a result error; disable selection
PJ26Duplicate row IDs, invalid identity or oversized resultReject output before selection
PJ27Unknown facts, missing canonical ID or unknown persona fitShow unknown or not linked; do not claim verified fit or CRM membership
PJ28Sources, funnel, usage or optional output trimmingDisplay reported counts and omitted diagnostics; valid partial rows remain selectable, including with the global truncated flag
PJ29Company has zero people, failure, unresolved match or quota limitShow its reported counts and reason
PJ30Brief returns empty, partial or more than 20 companiesShow people outcome and scope diagnostics; launch no client-side child
PJ31Select, deselect, select all or clearStore up to 100 ordered search-row refs
PJ32Duplicate status read or selection reloadPreserve valid selection and order
PJ33Stale refs or new admitted RunDrop refs absent from displayed results; clear prior person selection on admission
PJ34Refused or uncertain new startRetain prior selection
PJ35Session storage write failsShow error; retain saved refs and checkbox state
PJ36Navigate to People Enrich and backPreserve person selection; send no start; do not build Enrich controls
PJ37Actor, organization, logout or impersonation changesClear visible draft, results and selections; ignore late replies
PJ38Long names, text markup or unsafe evidence URLRender text safely; allow only safe external links
PJ39Desktop, mobile and keyboard selectionKeep stable frames, contained table scroll and named focusable checkboxes
PJ40Live supplied Run and structured provider dry runVerify real results and selection; verify payloads without unnecessary paid discovery

People Enrich journey stress cases

These cases define ENG-2289 on runtime agentic-platform and docs main. The shared shell cases F01-F26, profile cases PE01-PE39, adapter cases PI01-PI28 and email cases EM01-EM45 retain their owners. This matrix tests only the People Enrich product journey. It does not repeat backend behavior.

IDScenarioExpected rule
PEJ01Gate off, direct URL or hidden navigationUse F01. Do not expose People Enrich.
PEJ02Loading, read error, unavailable or unauthorizedKeep the form frame. Show the exact state. Disable a new start.
PEJ03Version other than 1 or incomplete field metadataShow an unsupported contract. Do not guess controls.
PEJ04Fresh supported formUse published Basic and stale defaults. Require an explicit start.
PEJ05Search selects people in a non-table orderPreserve that order and each source Run and result ID.
PEJ06Select all, clear or zero selected rowsEnable handoff only for 1 to 100 valid refs. Start no work.
PEJ07Search handoff, back navigation or reloadRestore same-tab scoped refs. Send no POST.
PEJ08A saved Enrich attempt and a newer Search selection existKeep the frozen attempt. Offer explicit replacement after it ends.
PEJ09Missing, malformed, stale or foreign saved selectionPreserve non-disclosing errors. Never rebuild refs from display facts.
PEJ10Direct LinkedIn, canonical and CRM valuesBuild typed refs in field order. Keep line order inside each field.
PEJ11Restored input contains all four ref kindsPreserve the exact typed order. Do not force it into direct fields.
PEJ12Blank value, malformed UUID, long URL or non-person LinkedIn URLReject the known shape before POST. Let the server decide canonical meaning.
PEJ13Zero, one, 100 or 101 subjectsRequire 1 to 100. Reject overflow without truncation.
PEJ14Repeated refs or different refs resolve to one personSend the ordered refs. Show one result with all input indexes.
PEJ15Basic, Standard or an explicit field subsetUse published choices. Preserve selected field order.
PEJ16Preset changes while extra fields are selectedReplace the visible selection with the complete new preset.
PEJ17No fields, duplicate fields or an unsupported fieldReject the request. Omitted fields alone mean the complete preset.
PEJ18Email source or score without emailShow a field error. Send no POST.
PEJ19Missing-only or stale refreshExplain retained values and provider uncertainty. Make no guarantee of completion.
PEJ20Double click, lost response, reload or refusalUse F07-F13 and F25-F26. Keep the frozen request and key.
PEJ21Session storage fails before a startKeep the prior selection. Show the failure. Send no POST.
PEJ22Actor, organization, logout or impersonation changesClear visible state. Reject late root and child replies.
PEJ23Root queued or running before child creationShow the root state. Do not claim that a profile preview exists.
PEJ24Root is live with one valid email childRead input.profile. Validate it against the root's frozen request.
PEJ25No child, two children, wrong lineage, wrong source or extra child inputClear the preview. Keep following the root.
PEJ26Child list or detail read failsLabel temporary failure. Keep a valid snapshot only for temporary failure; clear it on 401, 403 or 404.
PEJ27Duplicate hints, polling overlap or an older child replyCoalesce reads. Keep the newest scoped snapshot. Add no duplicate row.
PEJ28Profile is ready while email remains pendingShow profile values and pending email states. Label the data as a preview.
PEJ29Child completes before the root finalizesKeep following the root. Do not call the child result the root result.
PEJ30Root returns complete, partial or empty outputCheck V1 identity, all row bounds, complete selected-field coverage and exact input-index coverage. Replace the preview with valid frozen output. Keep outcome separate from status.
PEJ31Root succeeds with missing, truncated or pending output; wrong capability or version; malformed refs or values; missing or extra states; missing, duplicate or out-of-range indexesShow a result error. Resolve no links from invalid output. A valid preview remains a labelled non-final snapshot.
PEJ32Root fails or cancels after the child existsKeep the valid preview. Derive unfinished email state from the root status.
PEJ33Root fails or cancels before the child existsShow only the terminal root state. Do not read canonical data as a fallback.
PEJ34A retained email exists while this lookup is pending, failed or cancelledShow the value and operation state separately. Do not call it refreshed.
PEJ35Email not found, evidence unknown, unverified or verifiedUse field state and matching metadata. Never use score or Run success as proof.
PEJ36Verification metadata names another email or has an invalid shapeReject that result or preview. Missing metadata alone means unknown.
PEJ37Cache hit, miss, stale, not read or missing timestampShow reported profile cache data. Do not invent per-field freshness.
PEJ38Diagnostics, omitted lineage, output limit or unavailable usagePreserve exact counts and labels. Never add root and child usage.
PEJ39CRM input indexes, canonical IDs and LinkedIn result refsLink explicit CRM inputs and safe profile URLs only. Do not infer CRM membership from canonical IDs.
PEJ40Desktop Search to Enrich, mobile and keyboardUse a live API and worker. Capture a desktop video and mobile screenshot.

People email design stress cases

ENG-2299 owns these cases. The baseline is ac-python-api agentic-platform. Provider infrastructure stays with ENG-2210.

CaseInput or eventRequired outcome
EM01Basic or selected profile-only requestNo email provider call; published email scopes still apply
EM02Current requested email familyKeep values and matching evidence; no paid lookup
EM03Missing email with a current hot stampStart a lookup; freshness does not prove presence
EM04Missing, future or exact-boundary hot stampTreat the selected email target as stale
EM05Stale email with refresh: missingRetain the email and evidence without a lookup
EM06Current email with a missing selected scoreLookup only the missing email-family target; do not replace a protected address
EM07Email-only request with cached identity contextUse canonical names and LinkedIn identity; return verification without selecting score/source
EM08Email-only request without canonical names, or with one nameReport unresolved; do not invent names or fetch unselected profile fields
EM09Duplicate identities through different input refsOne contact per canonical identity; preserve all input indexes
EM101, 25, 26, 75 and 100 resolved subjectsAt most four sequential jobs; each job has at most 25 contacts
EM11No eligible contacts in one or all partitionsSkip submit and wait; preserve all result rows
EM12Unicode names or large fields exceed a boundaryRefuse certain overflow before paid work; never silently truncate identity
EM13Signals Search invokes People EnrichPublish within three workflow levels
EM14Actor lacks submit or collect scope, including BasicRefuse at admission using the full published scope set
EM15Profile succeeds while the email job runsPersist profile early; root stays live; email shows pending
EM16Callback before submit responseReuse the reserved job and first terminal data
EM17Callback before wait registrationDurable pre-wait read skips the wait
EM18Callback after pre-wait read, before registrationTimeout read finds completion; bounded delay only
EM19Callback while waitingWake and read stored results; do not trust event payload as evidence
EM20No callback or lost submit acknowledgementTimeout the same reservation; do not repeat the provider POST
EM21Callback after timeout has been frozenNo late canonical write or Run restart; cost can settle once
EM22Callback and worker replaySame job, same source ID and one canonical effect
EM23Reordered contactsMatch exact canonical UUID refs
EM24Duplicate, missing, unknown or malformed contact refFail ambiguous contacts; preserve valid siblings
EM25Wrong tenant, provider, child or root jobRefuse without exposing or writing data
EM26Completed batch with no emailReport not_found; retain an old email and its matching evidence
EM27Partial found results in failed or cancelled provider batchPreserve found contacts; report the remaining terminal states
EM28Deliverable non-role emailPreserve verified evidence and confidence
EM29Catch-all, unverifiable, role mailbox or unknown provider gradeNever label the email verified without explicit eligible evidence
EM30No provider verification timestampKeep checked_at null; do not use the callback clock
EM31Score absent on an email replacementDo not retain the previous address's score
EM32Cached evidence absent, malformed or for a different emailReturn unknown for the exact returned address
EM33Canonical email changes after the final RunFrozen result keeps its original email/evidence pair
EM34Profile-only write or failed email refreshPreserve the hot stamp
EM35Successful email writePreserve the warm stamp and unrelated profile fields
EM36Concurrent profile updatePreserve both profile and email work and all source IDs
EM37Concurrent email-group update or canonical identity changePreserve the concurrent value; report a conflict without false refresh
EM38Canonical write commits but acknowledgement is lostRetry from stored evidence; do not advance freshness again
EM39Canonical storage unavailableRetry persistence; never repeat submit or profile work
EM40Cancellation before submit, during wait or between writesStop further effects; preserve completed profile and email writes
EM41Budget or deadline stops later workStop paid calls; retain completed evidence and an explicit Run reason
EM42Callback settlement is delayed or repeatedUse one existing settlement path and stable usage ID
EM43Final projection exceeds 31 KiBReturn the established size refusal; keep canonical successes
EM44Client reconnects while root is live or cancelledUse profile output plus child/Run state; never claim a final result exists
EM45Empty, failed and partial operationsPreserve complete input coverage and selected field states