feat(enrichment): stay active until enrichment done, self-report results#154
Conversation
Launching an enrichment (leadbay_enrich_titles) kicks off an async backend
job and returns immediately. The agent used to end its turn on that ack, so
the user had to reprompt ("is it done yet?") to see results.
Now the agent stays active in the same turn: it polls leadbay_bulk_enrich_status
until the job is done — all_done, OR progress plateaus (some contacts are
unresolvable and never flip, so all_done can stay false forever) — then reports
the resolved emails/phones + counts in-reply, without a reprompt and without
deferring to a scheduled re-check.
- Reverse the "Don't poll" rule in snippets/gates/notifications-inbox.md: split
by WHEN the job launched — previous-turn jobs still ride the _meta.notifications
push; a job you launched THIS turn gets polled to completion.
- enrich-titles.md.tmpl: add "AFTER LAUNCH — STAY ACTIVE UNTIL DONE" with the
plateau safety-valve; bulk-enrich-status.md.tmpl: poll-repeatedly contract +
completion-report section.
- enrich-titles.ts / enrich-contacts.ts / launch-bulk-enrichment.ts: update the
runtime message/next_action/hint strings to instruct active polling (no logic
change).
- WORKFLOWS.md: new Workflow 43 (single-turn, automatic stay-active — distinct
from WF34's multi-turn "wait for enrichment" prompt).
- New unit test proves all_done flips false->true across sequential polls.
- Eval harness: wire a BulkTracker + NotificationsInbox into live-mcp-server so
leadbay_bulk_enrich_status can actually be polled in evals.
Co-Authored-By: Claude <noreply@anthropic.com>
…ude_contacts)
Codex P1: the stay-active guidance tells the agent to stop on a plateau and
report the RESOLVED contacts, but bulk_enrich_status's notification fast path
gated the per-lead fan-out on `!inProgress && includeContacts` — so at a
plateau (in_progress:true forever, some contacts unresolvable) an
include_contacts read returned bare {lead_id} with no contact data, making the
report unfulfillable.
- Fan out on `includeContacts` alone, regardless of in_progress. The agent only
sets include_contacts on the read it reports from, so interim polls
(default false) stay cheap.
- Surface credits_remaining on any report read (terminal OR plateau), not only
terminal — same opt-in cost boundary.
- New tests: plateau report returns contacts + balance while in_progress;
interim poll stays cheap (one /notifications call, no fan-out, no /me).
Co-Authored-By: Claude <noreply@anthropic.com>
Codex P2: the shared notifications-inbox snippet told the agent to poll every status tool "until all_done or a done-count plateau", but only leadbay_bulk_enrich_status returns all_done. Qualify signals completion via an empty still_running[] (in_progress:false); import via status:"complete". The blanket all_done rule risked the agent over-polling or ignoring the real terminal response for qualify/import. Spell out each status tool's own terminal signal in the current-turn polling rule. Enrichment keeps its all_done/plateau semantics; qualify and import get their correct fields. Enrichment-specific templates (enrich-titles, bulk-enrich-status) were already correct and are unchanged. Co-Authored-By: Claude <noreply@anthropic.com>
Minor bump for the stay-active enrichment feature (product#3866). package.json + server.json (top-level, packages[].version, and the @leadbay/mcp@<major.minor> npx pins all track 0.24). Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fc1771102a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
fc17711 to
83d8f21
Compare
…nch, fallback counts, test mock - enrich-titles.ts: branch the launched-mode message/next_action on bulkRecord (real bulk_id) BEFORE notification_id. A notification_id can return with no tracker (legacy/OpenClaw raw launch), leaving bulk_id undefined — the agent must use the per-lead fallback, not poll a nonexistent bulk_id. [Codex #789] - enrich-titles.md.tmpl + notifications-inbox.md: a plateau is only real after ~90s–2min of SPACED polls, not the first few back-to-back reads (done sits flat while the backend spins up). [Codex #784] - bulk-enrich-status.md.tmpl: don't promise bulk_progress unconditionally — the legacy fan-out fallback returns overall_progress only; derive counts from it there. [Codex #793] - bulk-enrich-poll-until-done.test.ts: the terminal include_contacts test now mocks the per-lead contact GETs and asserts contacts come back, so a swallowed fetch error can't mask an empty result. [Codex #798] - (Codex #806 — contacts on plateau — already fixed in 4fef5e8.) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 59dcf16b43
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…plates + gate quota call Follow-ups after the prior round found the same fixes needed in sibling surfaces: - enrich-titles.md.tmpl: AFTER LAUNCH no longer promises bulk_progress unconditionally — mirrors the status-tool wording (fast path has it, legacy fan-out returns overall_progress only). [Codex] - bulk-enrich-status.md.tmpl: plateau rule now requires spaced polls over ~90s–2min elapsed, not "a few polls" — mirrors the launch guidance. [Codex] - notifications-inbox.md: the previous-turn "don't poll" rule now applies only to ambient/background completion; when the user explicitly asks for status or "wait for it to finish" (e.g. WF34 turn 3), the agent DOES poll. [Codex] - WORKFLOWS.md WF43: add leadbay_account_status to required_calls + required_order so the deterministic precheck enforces the quota refresh (not just the judge). [Codex] Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 449f98c314
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…p scope, eval store isolation - enrich-titles.ts: the no-BulkTracker (legacy/OpenClaw) fallback next_action now carries the same plateau/timeout escape as the bulk-id branch — stop once the done set stops growing over ~90s–2min, don't poll leadbay_get_contacts forever on unresolvable contacts. [Codex] - notifications-inbox.md: scope the stay-active poll loop — imports are the exception (a large wait_for_completion:false import returns a handle and resolves over minutes; the tool does one refresh pass), so don't block looping on import_status unless the user asked to wait or it's a quick import. [Codex] - live-mcp-server.ts (eval helper): use InMemoryBulkStore instead of the file-backed createDefaultBulkStore, so repeated eval sessions don't share ~/.leadbay/bulks.json and hit the 5-min idempotency window (which would reuse a prior record and skip the backend launch the eval is meant to exercise). [Codex] (required_order — kept: it IS enforced, by the /eval skill Phase 4, just not by the workflows.test.ts unit audit; replying to clarify.) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d53811c575
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…l-aware wording, tighten WF43 - enrich-titles.ts: already_launched (idempotent reuse) branch next_action now carries the spaced-plateau/elapsed escape — a reused bulk with unresolvable contacts can stay all_done:false forever. [Codex] - enrich-contacts.ts + launch-bulk-enrichment.ts: single-contact and granular bulk hints now bound the wait (~90s–2min spaced re-checks), then report the unresolved contact/remainder instead of polling get_contacts forever. [Codex] - enrich-titles.md.tmpl: plateau "didn't resolve" wording is now channel-aware (no phone / no email / no contact details / quota-hit), not hard-coded "no email found" — correct for phone-only and email+phone runs. [Codex] - notifications-inbox.md: qualify terminal signal = still_running empty on its own; require in_progress:false only when present (it's null on legacy/fallback reads, else the agent polls a completed job forever). [Codex] - WORKFLOWS.md WF43: require leadbay_pull_leads (first) + a success criterion to scope enrichment to the top 5 (picked leadIds / candidateCount:5), so a run can't use the default candidate set and overspend quota. [Codex] Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e1290adca4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…sion eval store - enrich-titles.md.tmpl + bulk-enrich-status.md.tmpl: the channel-aware plateau wording now checks phone_number (the field leadbay_get_contacts actually returns), not phone — a successful phone reveal was being mislabeled "no phone number found". [Codex] - live-mcp-server.ts (eval helper): swap the in-memory store for a PER-SESSION file store (LEADBAY_BULK_STORE_PATH, else a per-process path under ~/.leadbay). In-memory was lost between turns — the live runner spawns a fresh process per turn, so WF34 turn 3's bulk_enrich_status poll would BULK_NOT_FOUND the record turn 2 launched. A per-session file survives across the session's turns while staying isolated from other runs (fixes the round-3 idempotency-reuse concern without breaking multi-turn). [Codex] Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e04887a9b3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…us poll (WF43) The "polled REPEATEDLY (more than once)" success criterion was unconditional, so a correct same-turn run whose FIRST leadbay_bulk_enrich_status read already returns all_done (small / already-enriched / fast backend) would be judged as failing just for not making a redundant second poll. Make repeated polling conditional: one poll is correct when the first read is already all_done; otherwise keep polling while still in-progress. [Codex] Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0af173ad74
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…re path + filter report to titles - live-session-runner.ts: the round-5 per-session store fix was only half done — the helper honored LEADBAY_BULK_STORE_PATH but the runner never set it, so it fell back to the per-process path and multi-turn still broke. Now writeMcpConfig sets LEADBAY_BULK_STORE_PATH to ~/.leadbay/bulks.eval.<session_id>.json, stable across the session's per-turn server processes so WF34 turn 3 finds turn 2's bulk. [Codex] - enrich-titles.md.tmpl + bulk-enrich-status.md.tmpl: include_contacts returns each lead's FULL contact list (get_contacts fan-out), so the report guidance now says to FILTER to the enriched `titles` (match job_title) — don't attribute a pre-existing CFO/Sales email to a CEO/Owner/Manager run. [Codex] Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 56001a3187
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…ing lead_ids, bg-request, channel-scoped fallback - enrich-titles.ts already_launched: a reused record can be stuck status:pending (original launch hit launched_tracker_pending). Route pending reuse to the per-lead fallback (with lead_ids), not bulk_enrich_status/BULK_PENDING. [Codex] - enrich-titles.ts launched_tracker_pending: include lead_ids in the response so the agent has identifiers to poll per-lead (leadIds may have come from the wishlist default). [Codex] - enrich-titles.md.tmpl: honor an explicit "don't wait / background" request — hand back the bulk_id + let _meta.notifications deliver, instead of forcing the poll loop against user intent. Stay-active stays the default. [Codex] - bulk-enrich-status.ts: legacy fallback "done" is now channel-aware — a title-matching contact only counts as done when the REQUESTED channel resolved (record.email → email present; record.phone → phone_number present). Stops a phone-only run from reading all_done:true off a pre-existing email enrichment. New phone-only test + /users/me mock added to the title-scope test. [Codex] Co-Authored-By: Claude <noreply@anthropic.com>
…ol-result-in-enrhichment # Conflicts: # packages/mcp/server.json
|
Synced with main; @leadbay/mcp is |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 251438438d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…d_ids in no-tracker launch - enrich-titles.ts already_launched: only treat a reused pending record as STUCK when it's aged (>=60s). A fresh concurrent pending is the normal serialization point between the first call's findOrCreatePending and markLaunched — it flips to launched momentarily, so the second caller should poll bulk_enrich_status as usual, not be told the job is unhealable. Distinguish by seconds_since_original. [Codex] - enrich-titles.ts mode:"launched": include lead_ids in the response so the no-tracker branch (no bulk_id, caller may have used the wishlist default) gives the agent concrete identifiers to follow the job per-lead. [Codex] Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 423d99113c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…xception in gate, no-notif message - enrich-titles.ts: an aged (>=60s) pending reuse is ambiguous — pre-launch crash (no backend job) vs post-launch markLaunched failure (job ran). The record can't tell them apart, so reusing + "don't relaunch" risks polling for nonexistent work. Since re-launch is server-idempotent, retire the aged pending (markFailed) and fall through to a fresh launch. Fresh concurrent pending (<60s) still reuses. New aged-pending relaunch test (injectable clock). [Codex] - enrich-titles.ts: the launched message no longer promises _meta.notifications completion when notification_id is null — that path has nothing to correlate, so it tells the agent/user to poll leadbay_bulk_enrich_status again later instead. [Codex] - notifications-inbox.md: the shared gate now mirrors the background-request exception (and the no-notification-id caveat), so prompt surfaces using this snippet don't override an explicit "don't wait" with a forced poll loop. [Codex] Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: db3ad6f78d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…aveats, plateau quota, 0.24.0 changelog - enrich-titles.ts: the launched next_action now honors an explicit no-wait request (hand back the handle instead of forcing the poll loop) — the runtime string is the newest instruction the agent sees, so the prompt-only exception wasn't enough. [Codex] - enrich-titles.md.tmpl: the background exception now conditions the _meta.notifications promise on a non-null notification_id (nullable-backend path has nothing to correlate → tell the user to ask again later). [Codex] - server-instructions/quota-topup.md: refresh quota when the job is done — all_done OR a terminal plateau — not only all_done, so plateau completions still report refreshed quota. [Codex] - CHANGELOG.md: add the 0.24.0 section so the release workflow publishes real feature notes instead of the generic fallback. [Codex] Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7d0462b061
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…t hint + plateau in launch messages - enrich-contacts.ts: the single-contact hint now keys "done" off the REQUESTED channel's field (email → email present; phone → phone_number present), built dynamically from the requested channels — not contact.enrichment.done alone, which is already true for a contact previously enriched on the other channel (a phone reveal could be reported before phone_number lands). [Codex] - enrich-titles.ts: the mode:"launched" message strings (both notification and no-notification branches) now carry the plateau escape too — poll until all_done OR until overall_progress.done plateaus — matching next_action. The message is the immediate instruction the agent reads, so without it agents could poll all_done forever on unresolvable contacts. [Codex] Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0556afeaf0
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…el-aware "done" + no-wait everywhere The round-15 findings were the last string variants of two rules already fixed in code + other strings: (1) a contact is "done" only when the REQUESTED channel landed (email/phone_number), not contact.enrichment.done alone; (2) honor an explicit no-wait request. Round-15 fixes: - bulk-enrich-status.md.tmpl COMPLETION REPORT: derive counts from overall_progress + requested fields, not the enrichment.done flag. - enrich-titles.ts no-tracker message + next_action: channel-aware done. - launch-bulk-enrichment.ts hint: channel-aware done (dynamic from requested channels). - enrich-titles.ts already_launched next_action: honor no-wait. Consolidation sweep (so future rounds stop finding one variant at a time): grepped every agent-facing runtime string / template / snippet for "until all_done" without a plateau escape (none left) and "enrichment.done" without a channel caveat — fixed the last one (launched_tracker_pending next_action). Now every poll-path string states channel-aware done + plateau + no-wait identically. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 03448caa07
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…ta + granular-launch (wider sweep) - server-instructions/quota-topup.md: the single-contact quota-refresh trigger now keys off the REQUESTED channel landing (email/phone_number present), not enrichment.done alone — which is already true for a contact enriched on the other channel, so a phone reveal could otherwise trigger the refresh early. [Codex] - granular launch-bulk-enrichment.md.tmpl: same channel-aware qualifier on its "track via get_contacts" description. Widened last round's sweep to ALL promptforge sources (not just the enrichment tool files) — this quota snippet was the one it had missed. Remaining enrichment.done mentions are field descriptions / candidate semantics, not completion gates, and are correct as-is. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bcf9fc6702
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
… message (match next_action) The launched-mode `message` still opened with an unconditional "Do NOT end your turn here — poll now", conflicting with the no-wait exception already in next_action. Both are operator-facing, so a "kick it off, I'll check later" request saw contradictory guidance. Both message branches (notification and no-notification) now carry the same no-wait qualifier as next_action. [Codex] Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eee98084dc
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…o tool hints + gate lead-in Round 15/16 rewrote the enrich_contacts and launch_bulk_enrichment hints to add the channel-aware "done" rule but didn't carry the no-wait exception into them — so those freshly-edited hints still said "don't end your turn, poll ~90s–2min" with no background escape. Fixed both: - enrich-contacts.ts: no-wait EXCEPTION → hand back leadId + contactId, re-check later. - launch-bulk-enrichment.ts: no-wait EXCEPTION → say it launched, re-check via get_contacts later. - notifications-inbox.md: the gate's opening "stay active" line now names the two exceptions up front so the lead-in isn't contradicted by the detail below. Swept every "stay active / don't end your turn" agent-facing directive for the no-wait qualifier — all now carry it (or lead with the exceptions). Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ee8ca321dd
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…uard + tracker-pending title filter - enrich-titles.ts (REAL BUG): the aged-pending relaunch (round 12) did markFailed → re-reserve → unconditionally POST /launch. If a concurrent caller created the fresh reservation first, the re-reserve returns reused:true and both callers launched — a double paid launch for the same selection within the idempotency window. Now checks fresh.reused and defers to that reservation (returns already_launched, no second /launch). New test proves no /launch fires. - enrich-titles.ts: the launched_tracker_pending fallback next_action now tells the agent to filter per-lead results to the enriched titles (job_title match) — the last spot the title-filter rule hadn't reached. [Codex] Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1a1e23df59
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…title filter in no-tracker fallback Both are the message/next_action + branch-parity gaps: - already_launched reuse message: add the no-wait qualifier (its next_action had it since round 15; the message still said "poll now" unconditionally). Same for the concurrent-guard reuse message. - no-tracker fallback (message + next_action): add the title filter — count/report only contacts whose job_title matches the enriched titles (the tracker-pending and status paths already had it; this branch didn't). Matrix-audited every launch-path string in enrich-titles.ts against the 4 rules (no-wait / plateau / channel-aware done / title-filter); actionable directives now carry every applicable rule. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5287b41d31
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…unch, partial_failures plateau) + 2 no-wait variants Real bugs: - enrich-titles.ts: if markFailed throws, the aged pending row survives and the re-reserve returns THAT SAME row with reused:true — my round-19 concurrency guard then deferred to it, leaving a pre-launch-crash handle stuck 'pending' forever. Now the guard only defers on a GENUINELY NEW row (bulk_id !== staleBulkId); a same-id stale row relaunches (idempotent). New test. - bulk-enrich-status.md.tmpl + enrich-titles.ts next_action: don't treat a flat overall_progress.done as a plateau while partial_failures is present — that's a transient per-lead fetch error (e.g. 429), not an unresolvable contact; keep polling / respect retry_after. (Code already excludes partial_failures from all_done; this closes the agent-facing plateau guidance.) No-wait variants: - no-tracker fallback message + tracker-pending fallback next_action now honor an explicit no-wait request (hand back lead_ids instead of forcing the loop). Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ad5b1f6e86
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…fication fast path Codex round 22 (2 real findings, same class): on the fast path, include_contacts fans out to getContacts, which uses allSettled — a rejected endpoint (429) becomes [] instead of throwing, so a transient error was indistinguishable from "nothing resolved" and the plateau rule could report those leads as permanently unresolvable. - get-contacts.ts: additively expose _fetch_errors (endpoint + code + retry_after) when an endpoint rejects — ignored by existing callers, non-breaking. - bulk-enrich-status.ts: the fast-path fan-out now builds partial_failures from both thrown errors AND getContacts._fetch_errors, and surfaces partial_failures on the return — mirroring the legacy path, so the agent's plateau rule honors it. - New test: a 429 on a fast-path plateau contact read surfaces as partial_failures. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 155d52b459
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| ...(fastPartialFailures.length > 0 | ||
| ? { partial_failures: fastPartialFailures } | ||
| : {}), |
There was a problem hiding this comment.
Keep partial contact failures from ending polling
When a notification-backed job is terminal but the include_contacts:true fan-out hits a transient contact-read failure (for example a per-lead 429), this returns partial_failures while all_done still stays true because it only mirrors !inProgress. The stay-active guidance treats all_done:true as the terminal signal, so the agent can stop and report completion with missing contact data instead of retrying/respecting retry_after; the legacy path already excludes partial_failures from all_done, so the fast path should do the same or otherwise force a retry.
Useful? React with 👍 / 👎.
|
|
||
| - **Previous turn / before an MCP restart, and the user has NOT asked about it** — don't poll for it in the background. Simply continue the conversation; the next time you call any tool, the completed-work entry appears in `_meta.notifications` (also on `leadbay_account_status.notifications`). This is the ambient push path — leave it to do its job. **But if the user explicitly asks for status or to "wait for it to finish"** (e.g. a multi-turn flow where a job was launched in a prior turn and this turn says "wait for enrichment to finish, then …"), DO poll its status tool now until done, exactly as for a this-turn job below — the ambient push only surfaces *completed* work, so it can't answer a live "is it done / wait for it" request while the job is still running. | ||
| - **This turn (you just launched it)** — the DEFAULT is: do NOT end your turn on the "launched" ack; stay active and poll the job's status tool in a loop until it reports done, then report the finished result yourself, rather than spinning forever or deferring the result to a later turn. (Two exceptions, detailed below: the user explicitly asked NOT to wait / to run it in the background; or it's a large qualification/import that's async by design — in those cases hand back the handle instead of looping.) Each status tool has its OWN terminal signal — poll until: | ||
| - `leadbay_bulk_enrich_status` → `all_done:true` — OR `overall_progress.done` holds steady across several SPACED polls (~15–30s apart) over at least ~90s–2 min of elapsed time (some contacts are unresolvable, so `all_done` can stay false forever). Don't call a plateau from the first few back-to-back reads — early on `done` sits flat while the backend spins up. Once the plateau is real, report what resolved and name what didn't. |
There was a problem hiding this comment.
Exclude partial failures from shared plateau guidance
This shared terminal condition omits the partial_failures exception that leadbay_bulk_enrich_status now documents and returns. When a plateau/report read has transient per-lead fetch errors (for example a 429) and overall_progress.done stays flat, any prompt surface including this snippet can still tell the agent to stop and report those leads as unresolved instead of continuing to poll or honoring retry_after; mirror the status-tool guard here while partial_failures is present.
Useful? React with 👍 / 👎.
|
Codex reviews are irrelevant at this point |
What & why
Launching an enrichment (
leadbay_enrich_titles) kicks off an async backend job and returns immediately. The agent used to end its turn on that ack, so the user had to reprompt ("is it done yet?") to see results.Now the agent stays active in the same turn: it polls
leadbay_bulk_enrich_statusuntil the job is done, then reports the resolved emails/phones + counts in-reply, without a reprompt."Done" =
all_done, OR progress plateaus —totalcounts every matching contact and some (unresolvable titles, no findable email) never flip, soall_donecan stay false forever. Whenoverall_progress.donestops climbing across a few polls, the agent reports what resolved (naming the ones that didn't) instead of spinning forever or deferring to a scheduled re-check.Changes
snippets/gates/notifications-inbox.md— reverse the flat "Don't poll" rule, split by when the job launched: previous-turn jobs still ride the_meta.notificationspush; a job launched THIS turn gets polled to completion.enrich-titles.md.tmpl— new "AFTER LAUNCH — STAY ACTIVE UNTIL DONE" section with the plateau safety-valve.bulk-enrich-status.md.tmpl— poll-repeatedly contract + completion-report section.bulk-enrich-status.ts— the notification fast path now returns per-lead contacts on anyinclude_contactsread (not only when terminal), and surfacescredits_remainingon any report read. Without this, a plateau report (in_progress:trueforever) returned bare{lead_id}with no contact data — the report the guidance asks for was unfulfillable.enrich-titles.ts/enrich-contacts.ts/launch-bulk-enrichment.ts— runtimemessage/next_action/hintstrings updated to instruct active polling (string-only; no logic change).WORKFLOWS.md— new Workflow 43 (single-turn, automatic stay-active; distinct from WF34's multi-turn "wait for enrichment" prompt).bulk-enrich-poll-until-done.test.ts—all_doneflips false→true across sequential polls; a plateau read (in_progress:true+include_contacts) returns contacts + balance; an interim poll stays cheap (one/notificationscall, no fan-out).live-mcp-server.ts— wire aBulkTracker+NotificationsInboxsoleadbay_bulk_enrich_statuscan actually be polled in evals (previously errored "No BulkTracker configured", which blocked WF34's turn-3 poll too).Eval (live, latest run — Workflow 43, on the current head
155d52b4)Prompt: "Pull my current leads, then enrich the CEO, Owner and Manager emails on the top 5 — go ahead and spend, email channel — and give me the finished results in this same reply, don't make me ask again."
MM 5 / IA 5 / NF 5 / TSF 5 · invariants PASS (required + order + forbidden).
pull_leads → enrich_titles (launched, bulk_id, enrichable_contacts:3) → bulk_enrich_status ×5 (spaced ~20–30s, include_contacts on the report reads) → account_status. The agent launched the paid enrichment, stayed active and polled to settlement, then reported per-lead results in the same reply. This run exercised the plateau path directly:overall_progress.doneheld at 0/1 with nopartial_failuresacross the spaced polls, so the agent correctly treated the requested contacts reachingenrichment.done:trueas terminal (not spinning for a never-trueall_done, not misreading the flat count as a transient failure), delivered the one landed email (michael@p1padel.com) + named the four that didn't resolve, and refreshed quota viaaccount_status. No fabricated emails — the single reported email is the only enriched email in any tool result.Not covered: runs under the eval's fallback system prompt (not the shipped host routing). The clean
all_done:true-terminal branch (vs the plateau branch this run hit) is covered deterministically by the unit tests.Gates
pnpm prompts:build·pnpm -r build·pnpm -r test(components 47, core 556, promptforge 16, mcp 525) ·pnpm -r typecheck— all green. Budget/workflows/routing/version audits pass. GitHubbuild-and-testCI green.Closes https://github.com/leadbay/product/issues/3866