Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions docs/now/2026-10-03-the-sweep-gets-one-step-per-seller.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# NOW -- The sweep gets one step per seller (2026-10-03)

## 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`.

## 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.
2 changes: 1 addition & 1 deletion specs/functions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
42 changes: 42 additions & 0 deletions specs/functions/crm-proactive-sweep.t27
Original file line number Diff line number Diff line change
@@ -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#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
; 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: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";
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-<id> 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.";
Loading