Skip to content

fix(crisp): re-arm stale check on customer messages only; dedupe skips threads with no recent customer message - #121

Merged
lihsaa591 merged 3 commits into
masterfrom
fix/crisp-customer-only-rearm-and-dedupe-freshness
Oct 14, 2026
Merged

lihsaa591 merged 3 commits into
masterfrom
fix/crisp-customer-only-rearm-and-dedupe-freshness

Conversation

@lihsaa591

Copy link
Copy Markdown
Contributor

Why

Two support-reported symptoms, each diagnosed from live Crisp metadata (message types, roles and timestamps only; no content).

session_3931e910 was re-investigated 7 times in 3 weeks. Three of those (Sep 28, Oct 9, Oct 13) were triggered by a support agent's reply, not the customer. Both that finished returned no_defect and still posted another note. The stale path treated any text message as new activity.

session_f9e20b9b was matched to everest-forms-pro#1245 two months after support resolved it (Aug 14). The state:resolved event is real, but Crisp had the thread as unresolved again, with no new message or event, from at least the Sep 2 seed (commit 3be1fa7). The bot never touched it before Oct 13, so it did not reopen it. crisp-dedupe-active.mjs had no freshness gate and matched it anyway.

Also found: active.last is absent (not frozen) on email-origin conversations, so active.last ?? created_at measured age-since-creation. That is why every "stale NNNh" log line counted back to the creation date.

Changes

  • crisp-classify.mjs: the stale path re-arms only on a new customer message (shouldReclassifyStale). checkedThroughAt never moves backwards (nextCheckedThroughAt).
  • crisp-classify.mjs: the pre-fetch gate takes the newest of active.last / updated_at / created_at as a hint only. Staleness is recomputed from real message timestamps once messages are fetched.
  • crisp-dedupe-active.mjs: skips conversations whose newest customer message is older than 14 days (DEDUPE_MAX_QUIET_DAYS). Skipped threads are not marked notified, so a new customer message re-evaluates them. The step summary gains a "Skipped" count.
  • New pure helpers and tests in scripts/crisp-activity.mjs / crisp-activity.test.mjs.
  • CHANGELOG entry, and debug-crisp-triage-not-investigated skill updated (it said active.last was "frozen", which was wrong).

Not changed

  • Reopen path. A reopen is itself a signal and its delta still includes agent text.
  • seed-escalated.mjs. It stamps from all message types, which only makes a thread re-arm later.
  • The unresolved-state flip itself. Its cause isn't visible from the API. Support can check the conversation's activity history in Crisp.

Behavior change to be aware of

The 30-day AUTO_ESCALATE_MAX_HOURS cap is now measured from the last real message, not from creation. A long-lived thread that is still active can therefore become eligible, where before it was excluded by age. I expect this to be rare: conversations active at onboarding were seeded, and ones created later were already processed while under 30 days old.

Testing

  • node --test scripts/*.test.mjs: 73 pass, including 13 new tests. Cases include the real shapes of both sessions (roles and timestamps only).
  • npm test fails with MODULE_NOT_FOUND on a clean master too (Node 22 and the directory arguments), so it was already broken locally and is unrelated to this change.
  • Not tested: the changed main() paths against live Crisp, since they aren't unit-testable as written.
  • After merge, watch the next few runs' stale ...h log lines and the dedupe step summary's "Skipped" count.

@lihsaa591
lihsaa591 merged commit 82890ad into master Oct 14, 2026
@lihsaa591
lihsaa591 deleted the fix/crisp-customer-only-rearm-and-dedupe-freshness branch October 14, 2026 11:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant