# Reason codes

Stable, language-independent tokens for why something happened. Branch on the code. Show the text to a person.

State fields answer what happened. Reason codes answer why. The code is stable and language-independent.

## Selection

| Code | Meaning |
| --- | --- |
| manual_pin | A person pinned this company to the top. |
| good_market_fit | The offering assessment rated this company a strong or good fit. |
| recent_relevant_signal | A Watch source reported a relevant event inside the project's lookback. |
| several_sources | More than one project source contributed this company. |
| held_by_budget | The cycle reached its research budget before this company's turn. |
| selected_for_research | The cycle selected this company and spent a research place on it.*Beyond the first design:* The original design lists the reasons a candidate waits, not the one it is chosen for. A cycle item stores `selected`, and a caller reading the cycle needs a code for the successful case too. |
| current_research_exists | Research against the current offering already exists, so the cycle skipped it.*Beyond the first design:* Emitted by `ProspectCycleItem.reason == 'current_research'`. It is the commonest reason a candidate is not in a cycle's spend, and without a code it reads as an unexplained omission. |
| no_current_source | Every source that once contributed this company has retired its contribution.*Beyond the first design:* A Watch signal ages out of the lookback and a Market drops a company from Core. Neither is an eligibility decision, so it cannot borrow `manual_exclusion` or `not_a_fit`. |
| dismissed | A person dismissed this company from the project.*Beyond the first design:* `Prospect.disposition` is a separate axis from selection state, and a dismissed row still appears in the feed with its history. |

## Eligibility

| Code | Meaning |
| --- | --- |
| manual_exclusion | A person excluded this company from the project. |
| relationship_excluded | An account relationship the project excludes applies to this company. |
| outside_territory | The company sits outside the project's territory.Documented and reserved. No rule computes it: Prospect has no territory field, and the geographic focus it would have used was removed from the product in August 2026. It stays in the taxonomy so a later territory rule does not have to invent a second name for it. |
| not_a_fit | The offering assessment rated this company a poor fit. |
| insufficient_information | Too little is known about the company to assess it. |
| awaiting_external_qualification | An external qualification job for this company is still open.*Beyond the first design:* New with external executors. Without it an unassessed candidate ranks ahead of `not_a_fit` and would enter research and spend a unit before its qualifier answered. |
| awaiting_external_research | An external research job for this company is still open.*Beyond the first design:* The same hold one stage later. The cycle that dispatched the job has already finished, and the prospect stays out of the next one. |
| awaiting_external_outreach | An external outreach job for this company is still open.*Beyond the first design:* The same hold at the writing stage. Research is already accepted and ready. The copy is with the customer's worker, and a later cycle must not write a second opener for the same revision. |

## Identity

| Code | Meaning |
| --- | --- |
| identity_confirmed | The researched pages are this company. |
| identity_ambiguous | The researched pages could belong to more than one company. |
| identity_mismatch | The researched pages belong to a different company. |
| identity_unresolved | The supplied identifiers did not resolve to one Prospex company. |

## Research

| Code | Meaning |
| --- | --- |
| specific_angle_supported | The evidence supports a specific, project-relevant angle. |
| no_defensible_angle | Research finished without a defensible project-specific angle. |
| source_unavailable | A cited or required public page could not be read. |
| research_failed | Research stopped on an operational error. |
| research_stale | The offering changed after this research was accepted. |
| research_needs_review | Research stopped on an ambiguity a person or a later pass must resolve.*Beyond the first design:* `needs_review` is one of the three qualification outcomes the product exposes everywhere else, and the original design's example list names only the other two. |
| evidence_unverified_external | The cited pages were submitted by an external worker and not retrieved by Prospex.*Beyond the first design:* The honest name for the gap this release leaves open. It rides on every externally accepted revision so a consumer can route those rows differently without parsing the caveat text. |

## Outreach

| Code | Meaning |
| --- | --- |
| bundle_ready | A verified outreach bundle is ready for approval. |
| bundle_needs_review | The outreach bundle has validation findings to resolve before approval. |
| bundle_approved | A named immutable bundle revision was approved. |
| unsupported_claim | The copy makes a factual claim no submitted evidence supports. |
| new_angle_available | A newer bundle exists on the current offering than the approved one. |
| ambiguous_identity | The bundle rests on research whose company identity is not confirmed.*Beyond the first design:* Emitted by `outreach.validate_draft`. |
| missing_copy | The subject, opener or angle explanation is empty.*Beyond the first design:* Emitted by `outreach.validate_draft`. |
| subject_too_long | The subject exceeds 200 characters once its evidence marks are removed.*Beyond the first design:* Emitted by `outreach.validate_draft`. |
| unknown_angle_type | The angle type is not one of the five the product defines.*Beyond the first design:* Emitted by `outreach.validate_draft`. |
| language_mismatch | The copy is not in one of the project's three languages.*Beyond the first design:* Emitted by `outreach.validate_draft` and by the model verification stage. |
| unknown_evidence | The copy cites an evidence key the research revision does not contain.*Beyond the first design:* Emitted by `outreach.validate_draft`. |
| untracked_claim | A listed claim does not appear in the copy it is supposed to describe.*Beyond the first design:* Emitted by `outreach.validate_draft`. |
| interpretation_as_fact | The copy presents an interpretation as a sourced fact.*Beyond the first design:* Emitted by `outreach.validate_draft`. |
| dishonest_date | The copy calls something recent that no cited evidence dates inside six months.*Beyond the first design:* Emitted by `outreach.validate_draft`. |
| verification_failed | The verification stage rejected the copy for a reason outside the named set.*Beyond the first design:* Emitted by `outreach.validate_draft` for an unrecognised verifier finding. |

## External stage

| Code | Meaning |
| --- | --- |
| input_stale | The submitted input fingerprint no longer matches the job's snapshot. |
| result_schema_invalid | The submitted result did not match the documented result schema. |
| evidence_reference_missing | A claim references evidence the submission does not contain. |
| job_expired | The job reached its deadline unfinished. |
| external_worker_failed | The external worker reported that it could not complete the job. |
| job_cancelled | A customer cancelled the job explicitly.*Beyond the first design:* `cancelled` is one of the eight documented job states and needs a code like the rest. |
| lease_expired | A claim lease ran out and the job returned to the external queue.*Beyond the first design:* Distinct from `job_expired` on purpose: the work is still available, and a worker that conflates the two stops polling. |
| job_allowance_full | The project is at its outstanding-stage-job allowance, so no job was created.*Beyond the first design:* The original design gives projects a visible limit on outstanding jobs but names no code for hitting it. Without one, a cycle that dispatched nothing looks identical to a cycle with no candidates. |
| job_not_open | The job has reached a terminal state and takes no further submissions.*Beyond the first design:* The refusal for a late submission, distinct from a stale fingerprint. |
| job_not_claimed | A result was submitted for a job this worker does not hold a live claim on.*Beyond the first design:* The claim lease decides who may submit, so a result arriving without one has to be refused for the lease to mean anything. Correcting a rejected result is the same two calls as the first attempt: claim it, then submit. |
| upstream_revision_changed | The research or offering revision the job was built on is no longer current.*Beyond the first design:* A submission can be structurally perfect and still target copy for research that has since been replaced. Rejecting it as `input_stale` would point the worker at the fingerprint rather than at the revision that actually moved. |
| identity_not_confirmed | An external result declared a company ready whose identity is not confirmed.*Beyond the first design:* The original design states the rule (an external qualification result cannot declare an ambiguously resolved company ready) without naming the code that refuses it. |
