Technical spec · all specs
Implement the deterministic ranking system that orders trusted TroveSnap Vision candidates for a specific user, organization, scan type, and ranking intent. The vision model identifies candidates and returns evidence. P3 determines which evidence is trustworthy. P4 converts that trusted evidence, together with versioned application context, into: an eligibility decision; a deterministic priority band; normalized ranking features; an exact final score; a stable total ordering; an explainable contribution breakdown; a reproducible ranking receipt.
The final ordering must never be "because the model ranked it first." TroveSnap must be able to explain which evidence affected the score; which application signals were used; which ranking policy applied; which weights and thresholds applied; why one candidate outranked another; how missing data was handled; which tie-breakers were used; whether a partial validation result affected ordering. The same trusted candidate set, ranking context, policy versions, and engine version must produce the same ordered result.
The model discovers possibilities. TroveSnap decides what deserves attention.
Crowded scene
→ trusted candidates
→ deterministic ordering
→ review the most relevant items first
→ isolate selected items
→ spend more inference only where justified
Ranking surfaces the candidates most likely to matter for the active purpose without pretending potential value, demand, or identity is known with certainty.
The initial sketch assumed the model returns opaque components such as:
{ "visual_confidence": 0.8, "text_match": 0.7, "completeness": 0.6, "condition_certainty": 0.5 }
That is insufficient: providers may interpret those concepts differently; scores may not be calibrated across models; text_match is ambiguous; completeness depends on the scan contract; condition certainty is not equally relevant to every candidate; demand cannot be inferred from the image; value potential must not be confused with an estimated price; the same provider may change score behavior over time.
P4 therefore derives ranking features from validated P1 evidence wherever possible. A model may supply primitive evidence values explicitly defined by P1 — identity confidence; watchlist-match confidence; visibility; identity certainty; appraisal state; appraisal reason codes; supporting claim evidence. It may not supply the authoritative final ranking score.
P4 requires: (1) candidate eligibility; (2) policy resolution; (3) feature extraction; (4) missing-data handling; (5) optional deterministic priority bands; (6) weighted scoring; (7) stable tie-breaking; (8) deterministic explanation; (9) complete provenance.
Reproducibility requires all of these to remain fixed: trusted result; validation receipt; ranking intent; user/org profile; watchlist versions; demand snapshot; feature-extractor version; ranking-policy version; weight-set version; taxonomy version; engine version; rounding rules; tie-break rules. "Same evidence plus same weights" is not sufficient if feature derivation or context changed.
If a P1 candidate contains a model-returned priority, P4 treats it only as a provider hint. It must not be surfaced as TroveSnap's final priority. The authoritative outputs are:
ranking:
priority_band: priority
final_score: 842
rank: 2
A future P1 revision should consider renaming provider-returned priority to model_priority_hint (see §38).
(1) The rankable-candidate contract. (2) Ranking-intent resolution. (3) Ranking-policy resolution. (4) Versioned feature definitions. (5) Deterministic feature extraction. (6) Separation of vision evidence and application signals. (7) Eligibility and exclusion rules. (8) Optional priority-band rules. (9) Versioned weight sets. (10) Missing-feature policies. (11) Integer scoring and rounding. (12) Score caps and thresholds where configured. (13) Stable total-order tie-breaking. (14) Candidate explanation records. (15) Ranking receipts and hashes. (16) Partial-result ranking behavior. (17) Demand-snapshot integration. (18) Personal and organizational ranking profiles. (19) Ranking observability. (20) Golden, property-based, mutation, and replay tests.
P4 does not: invoke vision providers; validate provider output; repair invalid candidates; identify objects; infer buyer demand from an image; aggregate raw demand events; retrieve comparable sales; calculate appraisal values; create offers; apply markdowns; create inventory; merge duplicate candidates; promote candidates into durable items; determine sold/listing state; automatically change production weights from behavioral data; apply hidden manual preferences; generate explanations with another LLM. P4 may consume normalized signals from those systems when explicitly supplied.
rankable_scan_types:
- table_hunt
- room_scan
Later uses (appraisal candidate queues; inventory review queues; post-sale disposition candidates; buyer hunt results; manual review queues) must use separately versioned ranking policies rather than reusing scene-ranking weights without evaluation. item_scan, mark_scan, and condition_scan generally produce one subject and do not require candidate ordering in the initial P4 implementation.
P3 TrustedScanResult + RankingContext
▼
Candidate Eligibility
▼
Ranking Policy Resolution
▼
Deterministic Feature Extraction
├─ validated vision evidence
├─ watchlist/profile inputs
├─ supplied demand snapshot
├─ validation warnings
└─ scan context
▼
Missing-Feature Resolution
▼
Priority-Band Rules
▼
Fixed-Point Weighted Scoring
▼
Stable Tie-Breaking
▼
Ranking Result + Explanation + Receipt
interface CandidateRankingEngine {
rank(trustedResult: TrustedScanResult, context: RankingContext, policyRef: RankingPolicyRef): RankingResult;
}
The function must be pure with respect to its supplied inputs. It must not read current time implicitly; query a database; fetch demand data; inspect mutable global config; use randomness; invoke an LLM; depend on input array order; call a provider. All required context must be supplied explicitly.
interface RankingContext {
tenantId: string;
rankingIntent: RankingIntent;
actorProfile?: { id: string; version: number };
watchlists: WatchlistSnapshotRef[];
demandSnapshot?: DemandSignalSnapshot;
saleContext?: { saleId?: string; zoneId?: string; zoneType?: string; location?: string };
directives?: RankingDirective[];
requestedAt: string;
}
requestedAt is recorded for provenance but must not affect scoring unless an explicitly versioned time-decay feature receives a frozen reference time.
ranking_intent:
- personal_hunt
- estate_organizer
- appraisal_triage
- buyer_discovery
Initial P4 acceptance requires personal_hunt and estate_organizer.
Resolved using scan type + scan version + ranking intent + policy version.
policy_key:
scan_type: table_hunt
scan_version: 2.0.0
ranking_intent: personal_hunt
policy_version: 1.0.0
One global weight set is not sufficient. A person hunting specific collectibles and an estate-sale organizer deciding what to inspect first have different objectives.
ranking_policy:
id: table_hunt.personal_hunt
version: 1.0.0
applies_to:
scan_type: table_hunt
scan_versions: ["2.x"]
ranking_intent: personal_hunt
feature_schema:
id: scene-candidate-features
version: 1.0.0
extractor:
id: scene-candidate-extractor
version: 1.0.0
eligibility_rules:
- candidate_is_trusted
- candidate_has_valid_primary_sighting
- candidate_is_selectable
priority_band_rules:
- id: critical_watchlist
band: critical
when:
watchlist_priority: critical
match_type_in: [exact, brand_model]
- id: appraisal_priority
band: priority
when:
appraisal_state: priority
weights:
id: personal-hunt-default
version: 1.0.0
missing_feature_policy:
id: scene-ranking-missing-v1
tie_break_policy:
id: candidate-total-order-v1
score_scale:
min: 0
max: 1000
priority_thresholds:
critical: 850
high: 700
medium: 450
low: 0
Published policies are immutable. Changing a rule, weight, extractor, threshold, or tie-break requires a new version.
P4 consumes only candidates retained by P3.
rankable_candidate:
candidate_id: cand_7
source:
trusted_result_id: result_812
validation_receipt_id: validation_771
label: sculptural lounge chair
category: furniture.chair.lounge
identity:
certainty: possible
confidence: 86
sightings:
- image_id: img_room_02
visibility: clear
primary: true
region:
bbox: [510, 165, 825, 770]
watchlist_matches: []
appraisal:
state: candidate
reasons:
- unusual_design
next_scan:
type: item_scan
version: 2.0.0
validation:
partial: false
candidate_warnings: []
P4 must not rank candidates removed by P3; untrusted canonical drafts; candidates without required geometry; candidates that fail policy eligibility; candidates from a rejected or failed P3 outcome. Candidates retained in a trusted partial result may be ranked per the partial-result policy.
Every ranking feature is normalized to an integer from 0 through 1000.
ranking_feature:
id: watchlist_relevance
value: 860
availability: available
source:
type: validated_evidence
paths:
- result.payload.candidates[cand_7].watchlist_matches[0]
extractor:
rule_id: watchlist-relevance-v1
rule_version: 1
explanation_codes:
- high_priority_watchlist_match
- visual_pattern_match
feature_availability:
- available
- unavailable
- not_applicable
- suppressed_by_policy
A missing feature must not silently become zero unless the ranking policy explicitly defines that behavior.
feature_source:
- validated_evidence
- deterministic_derivation
- actor_profile
- supplied_watchlist
- application_snapshot
- validation_receipt
- explicit_directive
scene_candidate_features:
- watchlist_relevance
- demand_signal
- appraisal_potential
- evidence_quality
- visibility
Optional future features: scan_actionability, evidence_gap_cost, regional_relevance, novelty_signal, disposition_fit. New features require a new feature-schema and policy version.
How strongly the candidate matches a supplied personal/org interest. Inputs may include validated watchlist match score; match type; watchlist-entry priority; corroborating match clues; exact vs semantic; applicable actor profile.
watchlist_feature_inputs:
- provider_match_confidence
- match_type_factor
- watchlist_entry_priority
- corroborating_evidence_count
Match-type and priority factors live in configuration, not in provider prompts or scattered code. A candidate with no relevant match receives the policy-defined no-match value. P4 may not invent a watchlist match.
Externally observed buyer/market interest — not derived from the image model. P4 accepts a normalized, versioned snapshot from the demand subsystem (F).
demand_snapshot:
id: demand_2026_06_19_1800
version: 4
generated_at: 2026-06-19T18:00:00Z
normalization_policy: regional-demand-v3
candidate_signals:
cand_7:
normalized_score: 720
source_scope: category_and_region
confidence: medium
hash: sha256:...
The snapshot must be tenant/scope-authorized and frozen for the ranking run; absence of demand data is distinct from zero demand; the model's claim that an item appears popular is not demand evidence; demand counts may not be invented by P4.
Whether deeper evidence collection or appraisal appears justified — not an estimated monetary value. Deterministically derived from validated appraisal state + reason codes + identity certainty + visible maker/model/signature/mark + collectible category + high-value variance + possible scarcity + complete-set evidence + unusual design + insufficient identification. State supplies a base; configured reason codes contribute bounded values; duplicates don't stack; unsupported free-form reasons contribute nothing; blocked may cap/lower; insufficient_identification may raise need-for-another-scan while lowering certainty. Never described to users as an appraisal or price.
How trustworthy/complete the candidate's supporting evidence is for ranking — derived by TroveSnap, not an opaque provider completeness. Inputs: identity confidence/certainty; number/quality of claims; validated watchlist evidence; clear primary sighting; corroborating sightings; P3 candidate warnings; partial status; evidence conflicts; missing required info. P3 warnings influence this only through explicit versioned mappings — a warning must not silently change ranking.
Whether the candidate is clear enough to review and scan next. Inputs: primary-sighting visibility; primary bbox size; occlusion; blur; distance; substantially-in-image; usable next-scan target. For room candidates the primary sighting controls the base; extra sightings improve evidence quality but should not auto-increase visibility. Visibility ≠ identity confidence (a clear unknown object: visibility: 950, evidence_quality: 500; a recognizable but occluded one: visibility: 350, evidence_quality: 750).
A future policy may consider whether a valid next scan exists; requested photos are simple to obtain; one follow-up image would resolve the gap; the item is accessible; the candidate is blocked pending manual input. Derived from typed P1 next-scan directives + supplied workflow context, never from provider prose.
prohibited_authoritative_components:
- final_score
- final_rank
- market_demand
- buyer_interest
- likely_sale_price
- organizer_priority
- personal_priority
- profit_score
- listing_priority
- sold_likelihood
- disposition_priority
A provider may return image-derived evidence that contributes to these later decisions, but the application owns the resulting score.
Missing data requires an explicit policy. It must not depend on value ?? 0.
missing_feature_strategy:
- zero
- neutral
- redistribute_weight
- disqualify
- use_configured_prior
missing_feature_policy:
id: scene-ranking-missing-v1
features:
watchlist_relevance:
strategy: zero
demand_signal:
strategy: redistribute_weight_when_snapshot_absent
appraisal_potential:
strategy: zero
evidence_quality:
strategy: disqualify
visibility:
strategy: disqualify
The policy must distinguish: no demand snapshot for the whole run; a snapshot exists but no signal for one candidate; feature not applicable; feature suppressed by policy. demand_signal: {availability: unavailable, reason: no_demand_snapshot} differs from demand_signal: {availability: available, value: 0, reason: observed_no_demand}.
If a policy redistributes unavailable weight: redistribution applies deterministically; the active denominator is recorded; uses all remaining eligible weighted features; applies consistently across the cohort; candidate-specific missingness must not create an unexplained advantage. The effective weight vector is recorded in the receipt.
Eligibility occurs before scoring.
eligibility_rules:
- trusted_candidate_required
- valid_primary_sighting_required
- selectable_candidate_required
- supported_category_required
Outcomes: eligible | ineligible | eligible_with_limits. An ineligible candidate is not assigned a normal numeric rank; it remains in the audit result with its exclusion reason. P4 must not quietly delete candidates from the record.
Some business rules should not be hidden inside a weighted sum.
priority_band:
- critical
- priority
- standard
- uncertain
- excluded
priority_band_rules:
- id: critical_personal_match
band: critical
when:
watchlist_entry_priority: critical
watch_match_type_in: [exact, brand_model]
- id: organizer_appraisal_priority
band: priority
when:
appraisal_state: priority
- id: uncertain_partial_candidate
band: uncertain
when:
candidate_validation_warning_in: [low_visibility, identity_conflict]
Band precedence is defined in the policy. Candidates are ordered first by band then by numeric score unless the policy explicitly defines otherwise. A critical exact watchlist match should not disappear below generic high-confidence furniture merely because the furniture is easier to see. Band rules are optional and intent-specific.
Weights live in configuration, immutable after publication, in integer basis points.
weight_set:
id: personal-hunt-default
version: 1.0.0
weights:
watchlist_relevance: 6000
demand_signal: 1000
appraisal_potential: 1500
evidence_quality: 1000
visibility: 500
total: 10000
weight_set:
id: estate-organizer-default
version: 1.0.0
weights:
watchlist_relevance: 3000
demand_signal: 2500
appraisal_potential: 2500
evidence_quality: 1000
visibility: 1000
total: 10000
Personal hunt prioritizes personal watchlist relevance, exact/strong matches, items the individual wants to inspect. Estate organizer emphasizes market/local demand, appraisal potential, organizational priority, items worth isolating for sale prep. These are initial configurations, not permanently universal weights.
Floating-point arithmetic must not determine persisted results. All feature values and weights are integers (feature: 0–1000, weight: 0–10000).
weighted_numerator = Σ(feature_value × effective_weight)
active_weight_total = Σ(effective_weight)
base_score = floor( (weighted_numerator + floor(active_weight_total / 2)) / active_weight_total )
The resulting score is an integer 0–1000. Example: features 860/400/750/820/900 at weights 6000/1000/1500/1000/500 → numerator 7,955,000 / 10,000 → final_score: 796. Rounding behavior is part of the policy contract and cannot vary by language or runtime.
score_caps:
- id: unresolved_identity_cap
when:
identity_certainty: unknown
watchlist_match_absent: true
max_score: 650
- id: severe_visibility_cap
when:
primary_visibility_in: [blurred, distant]
max_score: 550
Caps must be deterministic; versioned; explainable; applied after base scoring; included in the receipt. Use sparingly. A policy must not contain hidden undocumented adjustments.
P4 must produce a total order.
tie_break_policy:
id: candidate-total-order-v1
order:
- priority_band_precedence_ascending
- final_score_descending
- watchlist_relevance_descending
- appraisal_potential_descending
- evidence_quality_descending
- visibility_descending
- candidate_id_lexicographic_ascending
The final candidate-ID comparison guarantees a stable order. Input array order must never act as an implicit tie-break. Random tie-breaking is prohibited. Provider output order is not a ranking signal.
ranking_result:
id: ranking_441
request_id: req_8f29
source:
trusted_result_id: result_812
trusted_result_hash: sha256:...
validation_receipt_id: validation_771
validation_receipt_hash: sha256:...
context:
ranking_intent: personal_hunt
actor_profile:
id: paul_personal
version: 17
demand_snapshot:
id: demand_2026_06_19_1800
version: 4
hash: sha256:...
policy:
ranking_policy:
id: table_hunt.personal_hunt
version: 1.0.0
feature_schema:
id: scene-candidate-features
version: 1.0.0
feature_extractor:
id: scene-candidate-extractor
version: 1.0.0
weight_set:
id: personal-hunt-default
version: 1.0.0
missing_feature_policy:
id: scene-ranking-missing-v1
tie_break_policy:
id: candidate-total-order-v1
engine_version: 1.0.0
candidates:
- candidate_id: cand_2
rank: 1
priority_band: critical
final_score: 796
display_priority: high
components:
watchlist_relevance: { value: 860, effective_weight: 6000, contribution_numerator: 5160000 }
demand_signal: { value: 400, effective_weight: 1000, contribution_numerator: 400000 }
appraisal_potential: { value: 750, effective_weight: 1500, contribution_numerator: 1125000 }
evidence_quality: { value: 820, effective_weight: 1000, contribution_numerator: 820000 }
visibility: { value: 900, effective_weight: 500, contribution_numerator: 450000 }
band_rules:
- critical_personal_match
explanation:
headline_code: critical_watchlist_match
reason_codes: [high_priority_watchlist_match, collectible_category, clear_primary_sighting]
top_contributors: [watchlist_relevance, appraisal_potential, evidence_quality]
limitations: []
excluded_candidates: []
ranking_hash: sha256:...
Full detail: feature values; source references; extractor rules; weights; weighted contributions; band rules; caps; missing-data decisions; tie-break decisions; policy versions. Used for debugging, audit, provider comparison, weight review, regression analysis, support tooling.
A concise explanation such as "High-priority watchlist match / Possible collectible with visible maker evidence / Clear enough to scan separately." Generated from deterministic templates + reason codes. Must not expose provider chain-of-thought; hidden prompts; arbitrary model rationale; raw internal scoring formulas (unless an advanced detail view is opened).
Full component breakdown available in internal/advanced views; normal users see the 2–3 strongest reasons + meaningful warnings; the UI may show a score or priority label but must not imply false precision; "82% valuable" is prohibited; a ranking score is a prioritization score, not a probability or appraisal.
P4 may rank candidates from accepted, accepted_with_warnings, partial. It may not rank from retake_required, insufficient_evidence, rejected, failed. For a partial result: only retained trusted candidates are ranked; removed candidates remain absent; the receipt records the source was partial; candidate warnings feed evidence quality only through versioned mappings; the UI receives the source limitation; ranking must not imply the candidate set is complete. P4 does not revalidate or restore candidates removed by P3.
The demand subsystem (F) owns event collection; dedup; identity resolution; aggregation windows; geographic scope; category-level demand; user-interest counts; conversion; normalization; privacy; confidence. P4 owns accepting a frozen normalized snapshot; verifying its declared version/scope; matching supplied candidate/category signals; applying the policy; recording the snapshot hash. P4 must never directly query mutable demand tables during a ranking function (that would make the result non-reproducible). A new demand snapshot produces a new ranking event; it does not retroactively mutate an older receipt.
Manual/application directives must not be disguised as scoring weights.
ranking_directive:
- pin_candidate
- suppress_candidate
- exclude_candidate
- require_review
Computed score remains unchanged; directive recorded separately; displayed order may reflect a pinning layer; the UI must distinguish computed rank from manually adjusted order; hidden administrator preferences are prohibited. Manual reordering belongs to the review workflow, not the core ranking formula.
ranking_versions:
ranking_policy: 1.0.0
feature_schema: 1.0.0
feature_extractor: 1.0.0
weight_set: 1.0.0
missing_feature_policy: 1
priority_band_policy: 1
tie_break_policy: 1
explanation_templates: 1
engine: 1.0.0
taxonomy: 12
New versions required when feature meaning changes; a feature is added/removed; derivation changes; a weight changes; a band rule changes; a missing-data rule changes; tie-break order changes; rounding changes; thresholds change; explanation reason semantics change. Historical ranking records are never recomputed silently.
(1) Candidate inputs referenced by stable IDs. (2) Ranking ignores input array order. (3) Feature/component keys serialized in canonical order. (4) Integer math. (5) Dates don't affect scoring unless explicitly supplied to a versioned time feature. (6) No runtime-local number formatting. (7) Canonical JSON serialization before hashing. (8) The result records a deterministic ranking_hash. Two equivalent executions under the same versions produce the same canonical payload and hash. Operational metadata (e.g. execution duration) may be stored outside the hashed payload.
A ranking policy cannot be published unless all referenced features exist; all weights are nonnegative integers; ≥1 active weight is positive; weight totals match requirements; priority-band precedence is complete; thresholds are monotonic; tie-break fields are valid; missing-feature behavior is defined; extractor versions exist; explanation codes resolve; scan type/version are compatible; caps remain within the score range; no duplicate policy IDs; the policy passes all golden fixtures. Production policies are immutable; editing creates a new version.
P4 collects outcome metrics that let humans improve future policies: candidate selected for item scan; dismissed; watchlist match accepted/rejected; promoted to inventory; appraisal requested; appraisal candidate rejected; item sold; manual reorder; pinned; suppressed; organizer review time; top-ranked conversion; rank position at conversion. Analyzed offline; must not automatically mutate live weights.
Observed outcomes → offline analysis → proposed policy/weight-set version → replay against golden + historical sets → human review → explicit publication
No invisible self-modifying production ranker is permitted.
Persist: (1) ranking input reference; (2) trusted-result hash; (3) validation-receipt hash; (4) ranking context; (5) watchlist/profile versions; (6) demand-snapshot reference + hash; (7) every ranking-policy version; (8) extracted feature vector; (9) missing-feature decisions; (10) priority-band decisions; (11) effective weights; (12) exact scoring numerator + denominator; (13) final score; (14) tie-break path; (15) final rank; (16) excluded candidates + reasons; (17) explanation codes; (18) ranking hash; (19) explicit manual directives separately. (Storage schema lives in P6 — see §38.)
tie_break:
candidate_ids: [cand_3, cand_8]
equal_through: [priority_band, final_score, watchlist_relevance, appraisal_potential, evidence_quality, visibility]
resolved_by:
field: candidate_id
winner: cand_3
trovesnap.candidate.rank
├─ trovesnap.ranking.policy_resolve
├─ trovesnap.ranking.eligibility
├─ trovesnap.ranking.feature_extract
├─ trovesnap.ranking.missing_resolve
├─ trovesnap.ranking.band_assign
├─ trovesnap.ranking.score
├─ trovesnap.ranking.tie_break
├─ trovesnap.ranking.explain
└─ trovesnap.ranking.persist
Metrics: candidates ranked/excluded per scan; feature availability rate; feature-value + final-score distributions; priority-band distributions; tie rate + tie-break-field usage; partial-result ranking rate; watchlist/demand/appraisal contribution by intent; top-one/top-three selection rate; item-scan + appraisal conversion by rank position; watchlist-match acceptance; manual reorder/pin/suppress rate; policy version usage; ranking latency; ranking-hash mismatch rate; score drift across policy versions. No image content, private watchlist text, or buyer-identifying data in trace attributes.
P4 requires no live provider calls.
32.1 Golden score tests — fixed candidate sets with exact expected feature values, effective weights, numerators, final scores, bands, ordering, explanation codes, ranking hash; cover both personal_hunt and estate_organizer.
32.2 Input-order invariance — rank([A,B,C]) = rank([C,A,B]) (excluding non-deterministic operational metadata).
32.3 Idempotence — same frozen inputs twice → same vectors, scores, ordering, explanations, hash.
32.4 Monotonicity — increasing only a positively-weighted feature must not lower the score; bands tested separately.
32.5 Bounds — 0 ≤ feature ≤ 1000, 0 ≤ final_score ≤ 1000; property tests over random valid vectors/weights.
32.6 Tie-break — candidates tied through each level resolved by candidate ID; provider order has no effect.
32.7 Missing-feature — no snapshot for run; snapshot without candidate signal; not-applicable; required feature unavailable; redistribution; prior; disqualification; verify exact recorded effective weights.
32.8 Feature-extraction — exact/semantic/no watchlist match; priority levels; appraisal reason mapping; identity uncertainty; partial warning; clear vs occluded; multiple room sightings; unsupported model priority hint; invented demand excluded.
32.9 Priority-band — critical exact match; appraisal-priority; standard; uncertain partial; precedence over score; no matching rule.
32.10 Configuration — reject unknown feature IDs; negative/all-zero weights; missing missing-data policy; invalid scan version; circular/duplicate rules; nonmonotonic thresholds; invalid tie-break fields; unpublished extractor; modified immutable policy.
32.11 Partial-result — only P3-retained candidates ranked; partial limitations preserved; warnings influence score only via registered mappings; rejected candidates cannot reappear; ranking doesn't imply completeness.
32.12 Cross-runtime — same golden input → same canonical output across implementations (integer math, rounding, serialization, sorting, hashing).
32.13 Historical replay — replay recorded trusted results through current/proposed policy or alternate intent; report rank/score/band changes, top-K movement, and the reason for every movement. No provider calls.
P4 is pure once it receives a trusted P3 result, frozen ranking context, versioned policies, and an optional supplied demand snapshot. No live provider calls required. Integration testing uses recorded trusted P3 fixtures. Provider-specific ranking drift is evaluated upstream by replaying different providers' trusted results through the same P4 policy.
34.1 One global weight set or per scan type? Scoped by scan type, scan version, ranking intent. personal_hunt and estate_organizer need different policies.
34.2 Should component breakdown be exposed? Full detail stored + available internally; advanced review may show component values; ordinary UI shows concise reasons; score not presented as value probability.
34.3 Should the model return component scores? Only P1-defined primitive evidence/confidence; P4 derives authoritative features; opaque provider concepts (profit_score, market_demand, final priority) prohibited.
34.4 Where does demand come from? A normalized application snapshot from F or another trusted demand subsystem; P4 doesn't infer/aggregate.
34.5 What happens when demand data is missing? Versioned missing-feature policy; distinguishable from observed zero.
34.6 Does provider order influence ranking? No.
34.7 How are ties resolved? Versioned total-order tie-break ending in stable candidate-ID ordering.
34.8 Does model-returned priority become final priority? No — ignored or advisory; P4 computes authoritative score/band/order.
34.9 Can ranking weights change automatically? No — outcomes inform offline analysis; production changes require an explicitly published version.
34.10 Do manual pins alter the score? No — directives may alter displayed ordering; computed score/rank preserved separately.
34.11 Are historical rankings recalculated when demand changes? No — new context produces a new ranking event; historical receipts immutable.
(1) CandidateRankingEngine; (2) RankingContext schema; (3) RankingResult schema; (4) RankingReceipt schema; (5) ranking-intent enum; (6) ranking-policy schema; (7) weight-set schema; (8) feature-schema registry; (9) feature-extractor registry; (10) missing-feature policy; (11) eligibility-rule registry; (12) priority-band rule engine; (13) fixed-point scoring; (14) stable tie-break; (15) deterministic explanation generator; (16) canonical ranking serializer; (17) ranking hash; (18) personal-hunt policy; (19) estate-organizer policy; (20) demand-snapshot input contract; (21) partial-result ranking policy; (22) explicit directive contract; (23) OTel spans + metrics; (24) golden feature/ranking fixtures; (25) property-based tests; (26) configuration validation tests; (27) historical replay tooling; (28) policy comparison report; (29) operator + configuration documentation.
P4 is complete when: (1) only P3 trusted results enter the engine; (2) the initial engine supports table_hunt and room_scan; (3) ranking intent is explicit; (4) separate initial policies exist for personal_hunt and estate_organizer; (5) provider output order has no effect; (6) model-returned final score/priority is not authoritative; (7) features deterministically derived from validated evidence + supplied context; (8) demand accepted only from a versioned trusted snapshot; (9) missing demand distinguishable from zero; (10) watchlist relevance uses only supplied/validated watchlists; (11) appraisal potential not represented as an estimated price; (12) evidence quality incorporates P3 warnings only via registered versioned rules; (13) visibility distinct from identity confidence; (14) every feature value integer 0–1000; (15) every published weight nonnegative integer; (16) fixed-point integer arithmetic; (17) rounding documented + tested; (18) policy/extractor/weight/missing-data/tie-break independently versioned; (19) eligibility before scoring; (20) ineligible candidates recorded with reasons; (21) optional priority bands deterministic + versioned; (22) band precedence explicit; (23) missing-feature behavior explicit for every weighted feature; (24) effective redistributed weights recorded; (25) every candidate receives eligibility, feature vector, final score when eligible, band, stable rank, explanation codes, component details; (26) equal scores resolve via stable total-order tie-break; (27) candidate ID is the final tie-break; (28) input array order doesn't affect output; (29) partial trusted results ranked only under the partial-ranking policy; (30) candidates removed by P3 cannot reappear; (31) user-facing explanations deterministic + template-based; (32) score never labeled as value probability; (33) manual pins/suppressions separate from computed score; (34) the result records all relevant profile/watchlist/taxonomy/demand/policy/engine versions; (35) the result references trusted-result + validation-receipt hashes; (36) canonical serialization produces a deterministic ranking hash; (37) identical frozen inputs produce byte-stable payloads; (38) production policies immutable; (39) policy changes require new versions; (40) no live provider calls required for testing; (41) golden score/ordering tests pass exactly; (42) property tests prove bounds, input-order invariance, idempotence, monotonicity; (43) historical replay can compare two policy versions and explain every movement; (44) observability tracks selection/conversion/override/rank-position outcomes without auto-modifying production weights.
P4 is done when TroveSnap can take a trusted table or room candidate set and produce an exact, stable, personalized, auditable order for either a personal hunter or an estate-sale organizer. For every ranked candidate, TroveSnap must answer: Why is this candidate eligible? Which evidence affected its score? Which watchlist/demand context was used? Which policy/weights applied? How was missing data handled? Did a priority-band rule apply? What exact arithmetic produced the score? Why did it outrank the next candidate? Which tie-breaker resolved an equality? Can this be reproduced later? The P4 result is a deterministic, profile-aware candidate prioritization and explanation system that preserves the separation between model evidence; application demand; user/organizational intent; appraisal potential; and final TroveSnap ordering.
Adopting P4 closes the typed chain P1→P2→P3→P4 (it consumes the P3 branded TrustedScanResult). Seams to resolve before freeze:
model_priority_hint). A future P1 revision should rename the candidate priority field to model_priority_hint so a provider hint can't be mistaken for TroveSnap's authoritative priority (§3.4). Recorded in P1 §21.This gate splits (both gate P4, owner Paul); P4 is pure/deterministic so it's lighter than P2/P3:
CandidateRankingEngine, policy/feature/weight/missing-feature/eligibility/priority-band registries, fixed-point scoring, stable tie-break, deterministic explanation generator, canonical serializer + ranking hash, the two policies (personal_hunt/estate_organizer), golden/property/config tests.