Close #113–#116: lead-finder fully declarative (full sweep, no code) - #118
Merged
Conversation
… 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>
Closed
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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):
item($param:"item.field") (child of #94) #114pipeline.ts resolveInputValue:{$param:'item.field'}reads a field of the current forEach item viareadPath;{$param:'item'}still returns the whole item. Backward compatible.http.ts getPath: type-predicate segmentaddressComponents[types~=locality].longText— select the array element whose key contains the token, then continue. Inherited by map/responseMap/geocode.arr[]$ref grammar for grid fan-out (child of #94) #113 newflattenstep{items, depth?, path?}— concatenates the array-of-arrays a grid forEach produces (path:'data'lifts each cell's envelope sub-array first).enrichstep{items, tool, input, as, concurrency?}— runs a tool per item (concurrency-capped, via runRegistryTool), merges each result underas. The general 'call a connector per record' primitive → unlocks 'OR unreachable' AND [connectors] Web-search connector (Google Custom Search / SerpAPI) — enrichment #99 socials/email.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) → sinkleads. 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:
tscclean · 904 api tests pass (+15 net, GAP flips replaced in place, 0 regressions) · biome clean.🤖 Generated with Claude Code