Skip to content

fix(web): follow-ups from #1099 review (threat_intel null score, metadata parity, select guard) #1108

Closed

Description

@JOY

Follow-up from the round 2 review of the #1099 PR (AGENTS.md section 5 convergence rule: P2/P3 left out of that PR).

  1. P2: a threat_intel row with a NULL risk_score reads as a real 0. apps/web/src/lib/rate-api.ts mapTrustViewRowToMerchantOffer 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).
  2. 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.
  3. P3: view and fallback paths disagree on metadata. The view path ignores merchants.metadata.dosafe_risk_score, which the fallback path (mapOfferRowToMerchantOffer) honours, so a seller scored only in metadata shows "-" on one path and a number on the other. Predates fix(web): follow-ups from #1096 review (missing risk scores, token caps, Markdown code span) #1099 (it showed 0 before).
  4. 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.

Activity

  1. added
    status:readyTask ready to be picked up by an Agent
    status:in-progressTask currently claimed and being worked on by an Agent
    agent:webClaimed by Agent: Web
    and removed
    status:readyTask ready to be picked up by an Agent
    on Oct 27, 2026
  2. self-assigned this
    on Oct 27, 2026
  3. JOY commented on Oct 27, 2026

    @JOY
    ContributorAuthor

    [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).

  4. JOY commented on Oct 27, 2026

    @JOY
    ContributorAuthor

    [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 defines dosafe.threat_intel as:

    "source"     text     NOT NULL,
    "risk_score" smallint DEFAULT 50 NOT NULL,   -- CHECK 0..100
    CREATE UNIQUE INDEX idx_threat_intel_hash_source ON dosafe.threat_intel (entity_hash, source);

    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 (
      SELECT ti.risk_score, ti.category, ti.source
      FROM dosafe.threat_intel ti
      WHERE ti.entity_hash = m.entity_hash
        AND ti.status = 'active'
        AND (ti.expires_at IS NULL OR ti.expires_at > now())
      ORDER BY ti.risk_score DESC, ti.source
      LIMIT 1
    ) t ON true

    Column names and order stay the same, so CREATE OR REPLACE VIEW works and no web change is needed.

  5. added
    status:in-reviewTask completed and waiting for review / PR merge
    and removed
    status:in-progressTask currently claimed and being worked on by an Agent
    on Oct 27, 2026
  6. JOY commented on Oct 27, 2026

    @JOY
    ContributorAuthor

    [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.

  7. added
    status:doneTask completed and verified
    and removed
    status:in-reviewTask completed and waiting for review / PR merge
    on Oct 27, 2026
  8. JOY commented on Oct 27, 2026

    @JOY
    ContributorAuthor

    [Agent: PO] Released to production in #1128 (main c23ab85). verify-live: https://rate.vn serves main c23ab85, database ok, 5 routes 200.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

agent:webClaimed by Agent: Webstatus:doneTask completed and verified

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions