Skip to content

Close #113–#116: lead-finder fully declarative (full sweep, no code) - #118

Merged
serge-ivo merged 1 commit into
mainfrom
feat/94-close-gaps-full-sweep
Aug 1, 2026
Merged

Close #113–#116: lead-finder fully declarative (full sweep, no code)#118
serge-ivo merged 1 commit into
mainfrom
feat/94-close-gaps-full-sweep

Conversation

@serge-ivo

Copy link
Copy Markdown
Contributor

Closes #113. Closes #114. Closes #115. Closes #116. Completes epic #94.

Closes the four capstone gaps as general, reusable primitives, then upgrades the declarative lead-finder from spine-only to the FULL sweep — proving this class of agent is buildable with zero code.

Primitives (each = the issue's recommended option):

Full-sweep lead-finder (lead-finder.json): geocode → fan_out(grid) → http_request searchNearby per cell (forEach + item.lat/lng) → flatten → map(derive + geo via #116) → enrich(http_reachable on websiteUri) → filter(no-website OR unreachable) → dedupe_upsert(place_id) → sink leads. The 4 GAP tests flip to assert-it-works; the e2e test drives a multi-cell mock (no-website / dead-site / live-site + real addressComponents + a duplicate) and asserts flatten→per-place, geo populated, both no-website & unreachable survive, live excluded, dedupe, per-record audit.

Verdict: the lead-finder class is now 100% buildable by config. No residual gap. (Minor note: a no-website record's enrich→http_reachable gets an empty url and returns a fail-string, harmless — the websiteUri-missing clause catches it first.)

Verify: tsc clean · 904 api tests pass (+15 net, GAP flips replaced in place, 0 regressions) · biome clean.

🤖 Generated with Claude Code

… full sweep

Adds the four runner/step-library primitives the #94 capstone flagged, then
rewrites the lead-finder reference pipeline from "spine only" to the FULL sweep
(grid + geo + reachability), proving this agent class is 100% buildable by config.

- #114 pipeline.ts resolveInputValue: $param:"item.<path>" reads a dotted field
  off the forEach item (whole-item $param:"item" unchanged; backward compatible).
- #116 connectors/http.ts getPath: type-predicate array select
  arr[key~=token].sub — pick the element whose key contains the token, then
  continue the sub-path (populates geo from Google's typed addressComponents).
- #113 steps.ts flatten step {items, depth?, path?}: concatenate array-of-arrays;
  path lifts a sub-array (e.g. "data") out of each result envelope first.
- #115 steps.ts enrich step {items, tool, input, as, concurrency?}: run a tool
  per record (concurrency-capped, {$item:"path"} templates) and merge each result
  under `as` on a copy — the "call a connector per record and merge" primitive
  (unlocks the "OR unreachable" filter half and #99 socials/email).

lead-finder.json: geocode → fan_out(grid) → http_request searchNearby per cell
(forEach, item.lat/item.lng) → flatten → map(geo via type-predicate) →
enrich(http_reachable) → filter(no-website OR unreachable) → dedupe_upsert → leads.
lead-finder.test.ts: the four GAP tests flip to assert-it-works; the end-to-end
test drives the full multi-cell sweep. README: gaps marked done, step map updated.

Unit tests added: pipeline.test.ts (#114), http.test.ts (#116 getPath),
steps.test.ts (flatten + enrich). api tsc clean; full api suite green (904);
whole repo green (1305); biome clean. Running lead-finder agent untouched.

Closes #113
Closes #114
Closes #115
Closes #116

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment