Skip to content

feat(leadgen): unify lead_profile + lead_sync into a single leadgen action#74

Open
eldonm wants to merge 6 commits into
mainfrom
feat/leadgen
Open

feat(leadgen): unify lead_profile + lead_sync into a single leadgen action#74
eldonm wants to merge 6 commits into
mainfrom
feat/leadgen

Conversation

@eldonm

@eldonm eldonm commented Jul 7, 2026

Copy link
Copy Markdown
Member

Consolidates the separate lead_profile and lead_sync_action packages (and their skills) into one cohesive jvagent/action/leadgen harness.

What's in it

  • Unified actionengine, spec, store, sync, hooks, validators, contract validation (_validate_contract), and a thin-harness pattern.
  • DocsREADME + docs/ (frontmatter-schema, thin-harness, extending, troubleshooting).
  • Exampleexamples/example_leadgen skill template + an example leadgen_agent.
  • CLIjvagent skill create-leadgen <agent> <skill> scaffolds a leadgen skill from the template.
  • Tests — capture, spec, sync-templates, thin-harness guards, validators.
  • Removes the retired lead_profile / lead_sync_action packages + skills (−2586 lines).

Test plan

  • pytest tests/action/leadgen — 15 passed.
  • pre-commit run clean (black, isort, flake8, mypy, detect-secrets).

Independent of the outstanding dependency PRs (#67, #71, #72).

🤖 Generated with Claude Code

eldonm pushed a commit that referenced this pull request Jul 7, 2026
…ck-yaml

- Regenerate package-lock.json cleanly (rm + npm install) so it's fully in sync
  — the incremental install left it missing a transitive dep, so `npm ci` (what
  CI runs) failed with EUSAGE. `npm ci` now installs clean.
- Exclude auto-generated pnpm-lock.yaml from check-yaml (mirrors #74): main's
  pnpm-lock carries a duplicate key from the #69 merge that trips the strict
  parser; lockfiles shouldn't be yaml-linted. jvchat uses npm ci, not pnpm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
eldonm added a commit that referenced this pull request Jul 7, 2026
* chore(jvchat): upgrade to React 19

Bump react / react-dom / @types/react / @types/react-dom to ^19 and regenerate
package-lock.json. Fixes the one React 19 breaking change in the tree: the
global `JSX` namespace was removed, so JsonViewer.tsx's `JSX.Element` return
types now import the type explicitly (`import type { JSX } from "react"`).

The @assistant-ui/*, radix-ui, @testing-library/react, lucide, etc. ecosystem
all dedupe cleanly onto react@19. jvchat build (tsc && vite build), lint
(eslint --max-warnings 0), and tests (vitest, 55 passing) are green under 19.

Supersedes dependabot #65 (partial bump — it missed @types/react-dom, causing the
install-time ERESOLVE). Closes #73.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(react-19): sync package-lock (npm ci); exclude pnpm-lock from check-yaml

- Regenerate package-lock.json cleanly (rm + npm install) so it's fully in sync
  — the incremental install left it missing a transitive dep, so `npm ci` (what
  CI runs) failed with EUSAGE. `npm ci` now installs clean.
- Exclude auto-generated pnpm-lock.yaml from check-yaml (mirrors #74): main's
  pnpm-lock carries a duplicate key from the #69 merge that trips the strict
  parser; lockfiles shouldn't be yaml-linted. jvchat uses npm ci, not pnpm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Eldon Marks <eldon.marks@v75inc.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Eldon Marks and others added 2 commits July 7, 2026 03:36
…ction

Consolidate the separate lead_profile and lead_sync_action packages (and their
skills) into one cohesive jvagent/action/leadgen harness: engine, spec, store,
sync, hooks, validators, contract validation, and a thin-harness pattern, with
README + docs (frontmatter-schema, thin-harness, extending, troubleshooting),
an example_leadgen skill template, an example leadgen_agent, and full tests.

Adds `jvagent skill create-leadgen` CLI to scaffold a leadgen skill from the
template. Removes the retired lead_profile / lead_sync_action packages and
skills (-2586 lines).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-lock from check-yaml

- Rename tests/action/leadgen/test_thin_harness_guards.py ->
  test_leadgen_thin_harness_guards.py: pytest imports test modules by basename
  (no __init__), so it collided with tests/action/interview/ of the same name
  and aborted full-suite collection ("import file mismatch"). Passed locally
  only because the leadgen dir was run in isolation.
- black/isort the leadgen test files (missed in the first pass — only source +
  cli were formatted).
- Exclude pnpm-lock.yaml from check-yaml: main's jvchat/pnpm-lock.yaml carries a
  duplicate key (@codemirror/state@6.7.0) from the #69 lockfile merge that trips
  the strict YAML parser; auto-generated lockfiles shouldn't be yaml-linted
  (detect-secrets already excludes it). Unblocks check-yaml across all PRs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Eldon Marks and others added 4 commits July 7, 2026 04:17
Refine leadgen sync.py and extend test_sync_templates.py; touch the example
product_inquiry_leads SKILL.md. pre-commit clean; leadgen tests (17) pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… intro coexistence

Adapt the leadgen reference agent into a multi-skill storefront and harden the
leadgen action.

Agent (examples/jvagent_app/.../leadgen_agent):
- Add contrib/storefront mock action (dummy FAQ + product catalog) and the
  product_faq / product_search skills, coexisting with leadgen on one
  orchestrator surface (gpt-4.1).
- Add jvagent/intro_interact_action opener; configure sync on the leadgen
  action in agent.yaml rather than the skill frontmatter.

Intro coexistence (jvagent/action/intro):
- Contribute the first-message greeting as a response-shaping *parameter* (HOW)
  instead of a *directive* (WHAT), so ReplyAction weaves it into the same reply
  as the substantive answer instead of emitting a second, disjoint paragraph.
  Fixes intro + orchestrator "responding in parallel".

Leadgen action (jvagent/action/leadgen):
- Sync digest dedup fix: compute_digest hashed internal keys, so the stored
  digest changed the hash and every capture re-synced (duplicate rows on
  append destinations). Exclude _-prefixed keys from the digest and from
  {profile_json} so internal state never re-syncs or leaks to a destination.
- Sync is destination-agnostic via the standard MCP interface; drop the
  google_sheets-specific framing from example and docs.
- Move sync config onto the action (sync_mode / sync_min_fields /
  sync_require_any / sync_destinations); a skill may still self-contain sync by
  declaring its own destinations.
- More aggressive contact capture: STANDING GOAL in the capture/retrieve tool
  descriptions plus a next_ask hint in tool results; value-tied gap-fill each
  turn, easing off once required fields are captured.

Tests: sync dispatch/dedup/resync/internal-key + action-sync merge, intro
parameter contract, storefront search. pre-commit + affected slices green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nce; drop stale docs

- Rewrite jvagent/action/intro/README.md: was stale (described a directive +
  PersonaAction + wrong weights + Jac comparison). Now documents the
  parameter-based first-message intro, weight -300, new_user gating, ReplyAction
  egress.
- Leadgen README/frontmatter-schema/troubleshooting: sync config lives on the
  action (agent.yaml) via sync_mode/min_fields/require_any/destinations, standard
  MCP interface (no Sheets-specific framing), proactive next_ask gap-fill,
  graceful "connector not configured" skip, digest/internal-key notes.
- Add examples/.../leadgen_agent/README.md documenting the storefront demo
  (leadgen + FAQ + product-search + intro + sync).
- actions-catalog: refresh intro (parameter/-300) and leadgen (destination-agnostic
  sync) entries.
- CHANGELOG: Added storefront demo; Changed generic sync + action-level config +
  aggressive capture + intro parameter; Fixed digest re-sync + internal-key leak.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
test_healthcheck_requires_directive used asyncio.get_event_loop().run_until_complete,
which raises "no current event loop" on CI's Python — passed locally by luck. Use
@pytest.mark.asyncio + await like the other tests.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant