diff --git a/Sources/Dev/pbs-hold-and-flush.md b/Sources/Dev/pbs-hold-and-flush.md new file mode 100644 index 0000000..49a17f8 --- /dev/null +++ b/Sources/Dev/pbs-hold-and-flush.md @@ -0,0 +1,44 @@ +--- +created: '2026-07-08' +path: Sources/Dev +project: pbs-hold-and-flush +tags: +- n8n +- automation +- pbs +type: project-plan +--- + +## Goal + +When a comment arrives for a post we can't auto-reply to yet (**unresolved** — no recipe mapping), **hold** it keyed on the native IG post id instead of dropping, so comments that land *before* a post is set up survive. A **flush workflow** later replays every held comment for that post through the normal reply path once the post is resolved. + +## Locked Decisions + +1. **No special hold rules.** Additive hold branch on the reply flow's *existing* unresolved/data-error node (`set_Send_Data_Error_Msg`). The flow already routes muted→drop, near-miss→override, resolved→reply; only the unresolved case reaches that node. The hold just taps it — `continue-on-fail` so a hold failure never breaks the reply path. Keep the existing alert. +2. **Anchor = native IG post id** (string). Hold table keyed on it; comments accumulate under it; **no stub, no FK** to `platform_posts.id`. +3. **Store the full replay payload** per held comment: `ig_post_id`, `comment_id`, `comment_text`, `commenter_handle`, timestamp — enough to replay both the DM and the public reply-to-comment. +4. **Flush = a dedicated n8n workflow.** Webhook in = post id → query held comments for that `ig_post_id` → **re-invoke `Sub-instra-reply-handle-new` (`xCRsBINfaVQGLA0g`) per comment** (reply rules honored automatically) → mark each flushed. +5. **Manual trigger for now.** Hub flush button / resolve action POSTs the post id to the flush workflow's webhook. Auto-flush-on-resolution deferred. +6. **Park + alert.** Visible via the alert + a hub pending-comments view so a human knows there's a post to resolve. +7. **Short TTL ~30 min** (sub-60), configurable. Expired holds are **swept (dropped), not flushed** — rationale: past ~an hour Jenny has likely handled the post manually, so a stale held comment shouldn't fire. + +## Open Items + +- Auto-flush-on-resolution (deferred fast-follow — the resolve action would call the flush webhook). +- Retire vs keep the old Phase-6 `pending_comments` (it FKs to the internal id; the new hold keys on native id) — decide migration/coexistence. +- Exact TTL value + sweep cadence. + +## Phases + +1. **Hold table + service** — new table keyed on `ig_post_id`, full payload; queue / flush-query / expire-sweep. +2. **Hold trigger** — additive branch on `set_Send_Data_Error_Msg` → POST hold API (`continue-on-fail`); alert stays; muted still drops. +3. **Flush workflow** — new n8n workflow per decision #4. +4. **Hub** — flush button on the post detail page (calls the flush webhook) + pending-comments visibility. +5. **Test via the Case Tester** — hold-before-resolve → flush replays; multiple comments accumulate & all flush; muted drops (not held); near-miss overrides; expired swept. + +## Notes + +- Sandbox VM 202; snapshot-first; prove via the mock harness + Case Tester. +- Reuse: existing reply sub-workflow `Sub-instra-reply-handle-new`; the old `pending_comment_service` / `/flush-comments` as a starting point but re-anchored to the native id + `comment_id` added to the payload. +- Related: thread `pbs-case-tester` (999) and the resolution path (recipe panel + handoffs + deep-link) shipped earlier this session. \ No newline at end of file