From a921e07ea34e74e6378ddc9dc404a2412793b43d Mon Sep 17 00:00:00 2001 From: gHashTag Date: Thu, 17 Sep 2026 07:29:38 +0000 Subject: [PATCH 1/3] specs/functions: crm-proactive-sweep, one step per seller after the 502 runs Closes #4112 --- ...9-17-the-sweep-gets-one-step-per-seller.md | 24 +++++++++++ specs/functions/README.md | 2 +- specs/functions/crm-proactive-sweep.t27 | 42 +++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 docs/now/2026-09-17-the-sweep-gets-one-step-per-seller.md create mode 100644 specs/functions/crm-proactive-sweep.t27 diff --git a/docs/now/2026-09-17-the-sweep-gets-one-step-per-seller.md b/docs/now/2026-09-17-the-sweep-gets-one-step-per-seller.md new file mode 100644 index 0000000000..4cdbebfc3f --- /dev/null +++ b/docs/now/2026-09-17-the-sweep-gets-one-step-per-seller.md @@ -0,0 +1,24 @@ +# NOW -- The sweep gets one step per seller (2026-09-17) + +## What was read + +- The self-hosted Inngest dashboard, Status=Failed, last 7 days: three runs, all `CRM: proactive seller sweep`, all on 2026-09-16, durations 15 s, 2m02s and exactly 5m00s. +- All three: "Your server returned HTTP 502 before the SDK responded. No step output was produced before the request failed." +- The function was one `step.run('sweep')`: one HTTP request from the Inngest server to the bot carrying every connected seller in turn, each allowed up to INGEST_TIMEOUT_MS = 170 s on the render. + +## What this spec says + +- `specs/functions/crm-proactive-sweep.t27` is new; the function had no spec since it was born on 2026-09-12. +- STEPS = `resolve-sellers`, `sweep-${owner}`: one request per seller, a finished seller memoized inside the run, the trace names the seller a 502 lands on. +- RETRIES stays 0: a retry re-runs the interrupted step and may push a second card to a seller. +- DOMAIN `crm` is new to the corpus; README's DOMAIN vocabulary gains it. + +## Not verified + +- Which seller each 502 landed on: a single step left no per-seller trace. +- Whether the 15 s and 2m02s failures were a redeploy under the request or an app crash; Railway logs were not read. +- The exact Railway edge request budget; 5m00s is the observed cut. + +## Where the code goes + +- 999-multibots-telegraf, branch `sweep-per-seller-steps`: `crmProactiveSweep.ts`, `functions.manifest.json`, test `crmProactive.sweepSteps.test.ts` citing this spec, lesson `docs/inngest/lessons/2026-09-17-one-http-request-per-seller.md`. diff --git a/specs/functions/README.md b/specs/functions/README.md index 700916b6ae..99c7aa7e03 100644 --- a/specs/functions/README.md +++ b/specs/functions/README.md @@ -45,7 +45,7 @@ and as a function card here. The site joins the two by `REPO` + `LEGACY_ID` = th | `NAME` | `str` | display name | | `REPO` | `str` | `999-multibots-telegraf` | | `SERVICE` | `str` | `file:line` of `inngest.createFunction(` in that repo | -| `DOMAIN` | `str` | `neuro` \| `reels` \| `training` \| `morph` \| `render` \| `payment` \| `broadcast` \| `instagram` \| `content` \| `monitoring` \| `analytics` \| `webhook` \| `welcome` | +| `DOMAIN` | `str` | `neuro` \| `reels` \| `training` \| `morph` \| `render` \| `payment` \| `broadcast` \| `instagram` \| `content` \| `monitoring` \| `analytics` \| `webhook` \| `welcome` \| `crm` | | `TRIGGER` | `str` | `event` \| `cron` | | `EVENT` | `str` | canonical event name; `""` for a cron function | | `LEGACY_EVENTS` | `[N]str` | event names the code still listens to (multi-trigger); `[0]str = []` for a cron function | diff --git a/specs/functions/crm-proactive-sweep.t27 b/specs/functions/crm-proactive-sweep.t27 new file mode 100644 index 0000000000..cd1bb27033 --- /dev/null +++ b/specs/functions/crm-proactive-sweep.t27 @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/functions/crm-proactive-sweep.t27 -- function crm-proactive-sweep (Inngest, 999-multibots-telegraf) +; Source of truth for the function card on t27.ai (#/functions?function=crm-proactive-sweep). +; The registration was read from 999-multibots-telegraf:src/inngest_app/functions/crm/crmProactiveSweep.ts#L41 (main @fb8bee15, steps as of PR sweep-per-seller-steps). +; ID is the canonical id; this function was born spec-first on 2026-09-12 and has no legacy id. +; STEPS lists step.run names in source order; a ${...} in a name is a template the code +; expands per item. Values here are read from the source, not inferred from a name; what +; could not be read says so in NOTE. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module fn_crm_proactive_sweep; + +pub const KIND : str = "function"; +pub const ID : str = "crm-proactive-sweep"; +pub const LEGACY_ID : str = ""; +pub const NAME : str = "CRM: proactive seller sweep"; +pub const REPO : str = "999-multibots-telegraf"; +; file:line of inngest.createFunction( in the repo named in REPO +pub const SERVICE : str = "src/inngest_app/functions/crm/crmProactiveSweep.ts:41"; +pub const DOMAIN : str = "crm"; +; "event" or "cron"; an event function has EVENT (+ LEGACY_EVENTS), a cron function has CRON +pub const TRIGGER : str = "cron"; +pub const EVENT : str = ""; +pub const LEGACY_EVENTS : [0]str = []; +pub const CRON : str = "*/30 * * * *"; +pub const TZ : str = "UTC"; +pub const SUMMARY_EN : str = "Every 30 minutes resolves the connected sellers (owner first) and runs one proactive CRM tick per seller, each in its own step, so a seller's stalled clients get a card in the seller's Telegram; one run at a time, no retries."; +; step.run names in source order; sweep-${owner} runs once per resolved seller, sequentially +pub const STEPS : [2]str = ["resolve-sellers", "sweep-${owner}"]; +; retries declared on the function; when the code declares none, the Inngest JS SDK v3 default (4) is written and NOTE says so +pub const RETRIES : u8 = 0; +; "admin-telegram" (onFailure handler messages the admin chat), "log" (no handler) or "refund+notify" +pub const ON_FAILURE : str = "admin-telegram"; +; what a run touches outside the function, from a fixed vocabulary (see README) +pub const SIDE_EFFECTS : [3]str = ["messages-user", "paid-api", "db-write"]; +; the first step that stops a bad payload; "none" when the first step already acts, "unknown" when not probed +pub const GUARD : str = "none"; +; JSON payload of the 2026-09-09 safe probe and what it reached ("" / "skipped" when none was sent; "not-deployed" when the function is not on the probed build) +pub const SAFE_PROBE : str = "{}"; +pub const PROBE_RESULT : str = "not-deployed"; +pub const CONTROL : str = "spec+code"; +pub const NOTE : str = "Born 2026-09-12 when the proactive sweep left an in-process setInterval (CRM_SWEEP_DRIVER=timer restores it, CRM_PROACTIVE_MINUTES=0 disables both). Concurrency 1 and retries 0 because a tick may push a card to a seller; a retry or an overlap could push a second one. Safe mode (e2e_test=true) skips the run entirely; no carrier registered returns did=paused, not an error. 2026-09-17: three FAILED runs on 2026-09-16 (durations 15 s, 2m02s, 5m00s) all read 'HTTP 502 before the SDK responded' on the self-hosted Inngest dashboard -- until then the whole tick was one step.run(sweep), i.e. one HTTP request carrying every seller in turn, each allowed up to INGEST_TIMEOUT_MS=170 s; the Railway edge cut the request. Now resolve-sellers is a step and each seller is a sweep- step: one request per seller, finished sellers memoized, the trace names the seller a 502 landed on. Not verified: which seller each 502 hit, and whether the two short failures were a redeploy under the request. Test: 999-multibots-telegraf src/__tests__/services/crmProactive.sweepSteps.test.ts."; From be73fa5f6fd8d2bb69632e56c7420403b56cf35c Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Sat, 3 Oct 2026 03:19:16 +0700 Subject: [PATCH 2/3] specs/functions: crm-proactive-sweep anchor at crmProactiveSweep.ts:101 (999 main @7d48227) The sweep-per-seller change landed on 999-multibots-telegraf main and the createFunction call now sits at line 101, not 41. STEPS (resolve-sellers, sweep-${owner}), RETRIES 0 and concurrency 1 re-read on that revision. Refs #4112 Co-Authored-By: Claude Opus 5.5 --- specs/functions/crm-proactive-sweep.t27 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/functions/crm-proactive-sweep.t27 b/specs/functions/crm-proactive-sweep.t27 index cd1bb27033..c56bcdc000 100644 --- a/specs/functions/crm-proactive-sweep.t27 +++ b/specs/functions/crm-proactive-sweep.t27 @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 ; specs/functions/crm-proactive-sweep.t27 -- function crm-proactive-sweep (Inngest, 999-multibots-telegraf) ; Source of truth for the function card on t27.ai (#/functions?function=crm-proactive-sweep). -; The registration was read from 999-multibots-telegraf:src/inngest_app/functions/crm/crmProactiveSweep.ts#L41 (main @fb8bee15, steps as of PR sweep-per-seller-steps). +; The registration was read from 999-multibots-telegraf:src/inngest_app/functions/crm/crmProactiveSweep.ts#L101 (main @7d48227). ; ID is the canonical id; this function was born spec-first on 2026-09-12 and has no legacy id. ; STEPS lists step.run names in source order; a ${...} in a name is a template the code ; expands per item. Values here are read from the source, not inferred from a name; what @@ -16,7 +16,7 @@ pub const LEGACY_ID : str = ""; pub const NAME : str = "CRM: proactive seller sweep"; pub const REPO : str = "999-multibots-telegraf"; ; file:line of inngest.createFunction( in the repo named in REPO -pub const SERVICE : str = "src/inngest_app/functions/crm/crmProactiveSweep.ts:41"; +pub const SERVICE : str = "src/inngest_app/functions/crm/crmProactiveSweep.ts:101"; pub const DOMAIN : str = "crm"; ; "event" or "cron"; an event function has EVENT (+ LEGACY_EVENTS), a cron function has CRON pub const TRIGGER : str = "cron"; From ffe0f67c55dce2abfb4c4360ddb1a45949f77c02 Mon Sep 17 00:00:00 2001 From: Dmitrii Vasilev Date: Sat, 3 Oct 2026 03:50:24 +0700 Subject: [PATCH 3/3] docs(now): re-date the sweep-spec entry into the review window The NOW sync gate reads the entry date from the filename and accepts only yesterday..tomorrow UTC. The entry was dated 2026-09-17; it is moved to 2026-10-03 with its heading, and a line says it was re-dated for re-review. Refs #4112 Co-Authored-By: Claude Opus 5.5 --- ....md => 2026-10-03-the-sweep-gets-one-step-per-seller.md} | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) rename docs/now/{2026-09-17-the-sweep-gets-one-step-per-seller.md => 2026-10-03-the-sweep-gets-one-step-per-seller.md} (88%) diff --git a/docs/now/2026-09-17-the-sweep-gets-one-step-per-seller.md b/docs/now/2026-10-03-the-sweep-gets-one-step-per-seller.md similarity index 88% rename from docs/now/2026-09-17-the-sweep-gets-one-step-per-seller.md rename to docs/now/2026-10-03-the-sweep-gets-one-step-per-seller.md index 4cdbebfc3f..53c09edc54 100644 --- a/docs/now/2026-09-17-the-sweep-gets-one-step-per-seller.md +++ b/docs/now/2026-10-03-the-sweep-gets-one-step-per-seller.md @@ -1,4 +1,4 @@ -# NOW -- The sweep gets one step per seller (2026-09-17) +# NOW -- The sweep gets one step per seller (2026-10-03) ## What was read @@ -22,3 +22,7 @@ ## Where the code goes - 999-multibots-telegraf, branch `sweep-per-seller-steps`: `crmProactiveSweep.ts`, `functions.manifest.json`, test `crmProactive.sweepSteps.test.ts` citing this spec, lesson `docs/inngest/lessons/2026-09-17-one-http-request-per-seller.md`. + +## Re-dated + +- Re-dated on 2026-10-03 for re-review: this entry was first written on 2026-09-17, and its content is otherwise unchanged.