You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up from the round 2 review of the #1099 PR (AGENTS.md section 5 convergence rule: P2/P3 left out of that PR).
P2: a threat_intel row with a NULL risk_score reads as a real 0.apps/web/src/lib/rate-api.tsmapTrustViewRowToMerchantOffer treats a view dosafe_risk_score of 0 as a real score whenever threat_source is set. The view computes COALESCE(t.risk_score, m.risk_score, 0), so a dosafe.threat_intel row with no score plus the default merchants.risk_score 0 still shows "DOSafe: 0". Needs a check of the dosafe.threat_intel schema (not in this repo); a fix may need the view to expose t.risk_score nullness (DDL, out of scope for web-only work).
P2: the inverse. If threat_intel.source can be NULL or empty, a real DOSafe 0 from that row now shows as missing ("-"). Display only; trust is unchanged.
P3: no test pins threat_source in TRUST_VIEW_SELECT. If it is dropped, every view 0 silently becomes "-" and the mapper tests still pass. Add a one-line assertion on the select string.
[Agent: Web] Claimed this issue (Ready -> In Progress). Branch: fix/1108-offer-risk-followups. Plan: pin threat_source in TRUST_VIEW_SELECT, honour merchants.metadata.dosafe_risk_score on the view path through the existing merchants side read (the view's metadata column is the product's, not the merchant's), and write up the view change items 1 and 2 need (no DDL here).
No later migration in the ledger alters these columns. I did not query production; this is the schema of record, not a live check.
Item 1 (NULL risk_score reads as 0): cannot happen.risk_score is NOT NULL, so COALESCE(t.risk_score, m.risk_score, 0) only falls through to m.risk_score when the LEFT JOIN found no row. No web or view change needed.
Item 2 (NULL/empty source hides a real 0): NULL cannot happen; empty string was possible and is now fixed in web code.source is NOT NULL, so on the view a threat_source that is any string means a joined row. The mapper now treats any string (even '') as "row present", and only null/absent as "no row". Tested in section 126j.
Item 3 (metadata parity): fixed in web code. The view's metadata column is p.metadata (the product), not merchants.metadata, so the view cannot carry it. The existing rate.merchants side read (follower counts for unscored sellers, #685) now also selects metadata->dosafe_risk_score for sellers the view has no DOSafe score for. It only fills a missing score; it never overrides a view score. One remaining difference: for a seller with no threat_intel row, a synced merchants.risk_score > 0 and a metadata score, the fallback path prefers metadata while the view path keeps merchants.risk_score. Both are DOSafe numbers; I kept the view path from overriding a stored score.
Item 4 (pin threat_source): fixed. Section 126j asserts the view request's select includes threat_source for both getMasterProductOffers and getMasterProductOffersBatch.
New finding, needs a view change (not done here, no DDL): because threat_intel is unique on (entity_hash, source), a merchant listed by two sources joins twice, so rate.v_products_with_merchant_trust returns the same offer row once per source. The view also does not filter t.status = 'active' or t.expires_at, so expired or disputed rows still raise threat_risk_score. Whether any merchant actually has entity_hash matches today is unverified (needs a read-only count). A possible view shape for the DOS-Me ledger, for the PO to decide on:
LEFT JOIN LATERAL (
SELECTti.risk_score, ti.category, ti.sourceFROMdosafe.threat_intel ti
WHEREti.entity_hash=m.entity_hashANDti.status='active'AND (ti.expires_at IS NULLORti.expires_at> now())
ORDER BYti.risk_scoreDESC, ti.sourceLIMIT1
) t ON true
Column names and order stay the same, so CREATE OR REPLACE VIEW works and no web change is needed.
[Agent: Web] Review round 1 (fresh-context subagent) on #1119: no P0/P1. Fixed: P2 a rejected metadata path dropped the #685 follower counts (now falls back to id, follower_count); P3 test gaps (seller missing both scores, rejected path, row with no seller). Left as documented: P2 view/fallback precedence for a seller with merchants.risk_score > 0 plus a metadata score (see above); P2 the side read now covers most sellers because merchants.risk_score defaults to 0 (more rate.merchants rows per offer read, not a correctness issue). Not from this diff: the view path also ignores merchants.metadata.combined_risk_score, which the fallback path folds into risk_score.
Activity
JOY commentedon Oct 27, 2026
[Agent: Web] Claimed this issue (Ready -> In Progress). Branch:
fix/1108-offer-risk-followups. Plan: pinthreat_sourceinTRUST_VIEW_SELECT, honourmerchants.metadata.dosafe_risk_scoreon the view path through the existing merchants side read (the view'smetadatacolumn is the product's, not the merchant's), and write up the view change items 1 and 2 need (no DDL here).fix(web): any threat_source string marks a joined threat_intel row (#…
JOY commentedon Oct 27, 2026
[Agent: Web] Findings on the P2 items (branch
fix/1108-offer-risk-followups).Source checked (read-only): the DOS-Me migration ledger,
DOS.Me/supabase/migrations/20260418000000_baseline_from_prod.sql, which definesdosafe.threat_intelas:No later migration in the ledger alters these columns. I did not query production; this is the schema of record, not a live check.
Item 1 (NULL
risk_scorereads as 0): cannot happen.risk_scoreisNOT NULL, soCOALESCE(t.risk_score, m.risk_score, 0)only falls through tom.risk_scorewhen the LEFT JOIN found no row. No web or view change needed.Item 2 (NULL/empty
sourcehides a real 0): NULL cannot happen; empty string was possible and is now fixed in web code.sourceisNOT NULL, so on the view athreat_sourcethat is any string means a joined row. The mapper now treats any string (even'') as "row present", and onlynull/absent as "no row". Tested in section 126j.Item 3 (metadata parity): fixed in web code. The view's
metadatacolumn isp.metadata(the product), notmerchants.metadata, so the view cannot carry it. The existingrate.merchantsside read (follower counts for unscored sellers, #685) now also selectsmetadata->dosafe_risk_scorefor sellers the view has no DOSafe score for. It only fills a missing score; it never overrides a view score. One remaining difference: for a seller with no threat_intel row, a syncedmerchants.risk_score > 0and a metadata score, the fallback path prefers metadata while the view path keepsmerchants.risk_score. Both are DOSafe numbers; I kept the view path from overriding a stored score.Item 4 (pin
threat_source): fixed. Section 126j asserts the view request'sselectincludesthreat_sourcefor bothgetMasterProductOffersandgetMasterProductOffersBatch.New finding, needs a view change (not done here, no DDL): because
threat_intelis unique on(entity_hash, source), a merchant listed by two sources joins twice, sorate.v_products_with_merchant_trustreturns the same offer row once per source. The view also does not filtert.status = 'active'ort.expires_at, so expired or disputed rows still raisethreat_risk_score. Whether any merchant actually hasentity_hashmatches today is unverified (needs a read-only count). A possible view shape for the DOS-Me ledger, for the PO to decide on:Column names and order stay the same, so
CREATE OR REPLACE VIEWworks and no web change is needed.fix(web): keep follower counts when the metadata path is rejected (#1108
JOY commentedon Oct 27, 2026
[Agent: Web] Review round 1 (fresh-context subagent) on #1119: no P0/P1. Fixed: P2 a rejected metadata path dropped the #685 follower counts (now falls back to
id, follower_count); P3 test gaps (seller missing both scores, rejected path, row with no seller). Left as documented: P2 view/fallback precedence for a seller withmerchants.risk_score > 0plus a metadata score (see above); P2 the side read now covers most sellers becausemerchants.risk_scoredefaults to 0 (morerate.merchantsrows per offer read, not a correctness issue). Not from this diff: the view path also ignoresmerchants.metadata.combined_risk_score, which the fallback path folds intorisk_score.fix(web): view offers read the merchant metadata DOSafe score (#1108) (…
JOY commentedon Oct 27, 2026
[Agent: PO] Released to production in #1128 (main c23ab85). verify-live: https://rate.vn serves main c23ab85, database ok, 5 routes 200.