Signed customers still get reminders from outdated agreements

Objective

  • Stop clients who have already signed from receiving agreement reminder emails and texts when an outdated or extra agreement row still sits on the work order next to the valid signed agreement (or when stale / superseded rows keep comms alive).
  • When an agreement moves to Outdated (data: isStale on a signed agreement after content drift), automatically archive that agreement document and automatically create a new agreement for the same inspection + template in pending status with up-to-date merged content so it can be signed—without waiting for staff to use Recreate Outdated or manual archive.
  • Align cleanup expectations with operations: service changes should not leave orphan signing obligations that fight the denormalized agreement-present / agreement-signed gates used in flows—decision needed on full parity with reports when services drop off.

Background

  • Support and scheduling describe cases where the customer already signed, but duplicate reminder emails and texts still went out because an outdated agreement remained on the job until staff removed it.
  • Example repro context — work order: May 9 inspection — workorder tab.
  • Product language vs schema: the UI label “Outdated” maps to Agreement.isStale (with staleAt). status remains pending, viewed, signed, or declined in attik-backend/src/models/agreementSchema.ts; there is no separate outdated enum value—staleness is orthogonal to status.
  • Stale detection today: attik-backend/src/events/bullmq/agreementStaleWorker.ts compares regenerated content to signedContentHash (or legacy content hash) and sets isStale / staleAt on signed agreements when content would differ; it does not archive, does not enqueue a replacement pending row, and does not cancel comms by itself.
  • Manual staff path today: attik-frontend/src/app/tools/inspections/[id]/components/Agreements.tsx exposes Recreate Outdated, which calls POST /agreement/manual (implemented in attik-backend/src/routes/agreements.ts) to add pending rows when a template has a signed + stale agreement and no non-stale row; archive is a separate PATCH action (handleSetArchived). The new bar is automatic archive + automatic recreate at the moment an agreement becomes outdated.
  • Active-agreement guard: rejectIfActiveAgreementExists in attik-backend/src/routes/agreements.ts treats active as not archived, not stale, not declined—stale rows already do not block a new instance, but unarchived stale signed rows may still interact with reminder and portal list behavior until archived and superseded.
  • Denormalized gate: inspection.agreement.present and inspection.agreement.signed are recomputed from active Agreement documents when rows change (attik-backend/src/events/streamHandlers/agreementStream.ts with agreementLockStateFromDbLean in attik-backend/src/util/functions/agreements/agreementLockSync.ts). Flow conditions use agreement-signed and agreement-present via attik-backend/src/util/functions/actionFlows/attributePathResolver.ts; handleEventTriggerdByBull drives scheduled email and sms.
  • Auto-generation today: attik-backend/src/util/functions/agreements/autoGenerateAgreements.ts creates missing template agreements and regenerates unsigned content when charges change; it does not implement delete-on-service-removal or the new archive + replace on stale behavior—decision needed for interaction with charge streams.

Scope

Backend

  • Stale transition hook: extend or follow agreementStaleWorker (attik-backend/src/events/bullmq/agreementStaleWorker.ts) so that when a signed agreement first becomes isStale, the system can archive that document (today PATCH allows archived only for signed rows—see attik-backend/src/routes/agreements.ts PATCH /:id) and then create a pending replacement equivalent to POST /agreement/manual (template merge via mergeAgreementBlocksToContent, InspectionFullPopulatedForFlow, hashContent—same route file).
  • Idempotency and ordering: ensure one replacement pending per template per transition and safe behavior if staff or other jobs also mutate rows—decision needed (compare to existing rejectIfActiveAgreementExists and queue dedup in queueAgreementStaleCheck).
  • Reminder / flow evaluation: trace handleEventTriggerdByBull and time-based steps so archived + superseded rows no longer satisfy unsigned signing nudges; relate to inspectionStream / chargeStream callers of queueAgreementStaleCheck.
  • Activity: attik-backend/src/util/functions/activity/agreementHandlers.ts logs isStale changes—consider whether automated archive/create should emit clearer audit trails.

Frontend

  • Agreements.tsxstaleAgreementsNeedingRecreation, handleRecreateOutdated, handleSetArchived may become partially redundant or need copy/UX updates if the server always performs the two steps.
  • templateIdsBlockedByActiveAgreements (attik-frontend/src/util/agreements/templateIdsBlockedByActiveAgreements.ts) must stay consistent with backend active definition if automation changes timing of stale vs archived.
  • AgreementsNeeded.tsx and portal lists — confirm tasks and client surfaces match single active signing obligation per template after automation.

Config / product

  • Company-configured flows (reminder cadence) may need audit per branddecision needed whether automation is global or configurable (see related ATT-1609).
  • Decision needed: behavior when isStale is cleared (worker unmarks stale if content matches again)—whether to leave archived history vs other product rules.

References

  • Example inspection — workorder tab
  • Related: ATT-1609 — configurable agreement staleness / inspection triggers
  • attik-backend/src/models/agreementSchema.ts
  • attik-backend/src/routes/agreements.tsrejectIfActiveAgreementExists, POST /manual, PATCH /:id (archive rules)
  • attik-backend/src/events/bullmq/agreementStaleWorker.ts
  • attik-backend/src/events/streamHandlers/agreementStream.ts
  • attik-backend/src/util/functions/agreements/agreementLockSync.ts
  • attik-backend/src/util/functions/actionFlows/attributePathResolver.ts
  • attik-backend/src/util/functions/actionFlows/handleEventTriggerdByBull.ts
  • attik-backend/src/util/functions/agreements/autoGenerateAgreements.ts
  • attik-frontend/src/app/tools/inspections/[id]/components/Agreements.tsx
  • attik-frontend/src/components/task-check/AgreementsNeeded.tsx

Please authenticate to join the conversation.

Upvoters
Status

Completed

Board
🏠

Main App

Date

About 1 month ago

Author

Linear

Subscribe to post

Get notified by email when there are changes.