0148 — A conflict row with a clear higher-ranked winner fills

Context

propflowai reads ONE item of ingest's: the pre-fill contract (PREFILL#<org> / v1). Until now two rules held:

  1. Ingest filled only questions the company-records computation (company-suggestions.ts: AppFolio charges, the buildings' own PMS fees, the listings, the website) never filled, so the two sources never met.
  2. A conflict row never filled anything. Its values were shown side by side for the customer to pick.

Western Slope showed what that costs. The application fee had a staff statement ($40 per application, from the onboarding call) and an AppFolio charge ($27, the most common billed amount). Under rule 1 the staff value was never offered. Under rule 2, if it came as a conflict, nothing was offered at all, although one source is plainly better than the other.

Decision

  1. One resolver decides every pre-fill (src/lib/domain/prefill/resolve-prefill.ts in propflowai). For each question it gathers every candidate value, from the records computation and from every contract row, including each value inside a conflict row.
  2. Source precedence, written down in one place (PREFILL_PRECEDENCE), highest first:
    1. staff: what the customer's own team said (ingest staff)
    2. pms: the PMS's records (ingest pms; the records computation's values)
    3. document: other emails and documents (ingest other)
    4. website: the company's own website
    5. listing: public listings
  3. Fill when the best available source is consistent; listings count. The highest tier present must hold one value (case, spacing and money formatting do not make a difference). Then it fills, down to and including listings (FILL_FLOOR = 'listing'), with its source shown behind a small (i). If the highest tier disagrees with itself, nothing fills and its values are offered for the person to pick, as before.
  4. A conflict row with a clear higher-ranked winner fills. This is the amendment. The other values are not lost: they appear under the value as "Also found (N)", each with where it came from, plus a hint to update their records if one is out of date.
  5. Until ingest names the kind of every value in a conflict, an unknown-kind value blocks the fill. conflicts[] entries carry source_label but no source_kind, so such a value might outrank the winner. When one differs from the would-be winner, nothing fills and every value is offered to pick. A same-value entry is no disagreement.
  6. One rule for what the hub takes from a row (ingestRowCandidates): the page, the onboarding gaps count and the save's ingest stamp all read it. A staff statement may now fill any listed question, not only the three phone questions, and a fee row offers one dollar amount read off its words.

Consequences

Alternatives considered