Research engine
A SearchProvider port with an offline deterministic provider and an online Tavily provider; signals are deduplicated, scored and clustered into reports with evidence URLs.
Research, customer mining, CRM, email outreach and demand/supply matching as one operations personal agent. Research is read-only by default and runs offline without a key; email outreach is gated by the zeus kernel and nothing is ever sent without human sign-off. Signals are scored and deduplicated, every lead and match is traceable, and nothing unbuilt ever fakes success.
D2 email HELD 423 → APPROVED → mail.sendD3 batch HELD 423 → APPROVED → mail.sendkernel down 503 DECISION_UNAVAILABLE · fail-closedtavily search ONLINE · real search with keysmtp mail FAIL-CLOSED · off mode 503lead.promoted source=signal score=89 idempotent=hitdraft.sent decision=D2 messageId=msg_1match.created score=90 reasons=category+priceFive stations from market research to a matched buyer/supplier pair. Search and mail sit behind ports, and the one external action — email outreach — pauses at a zeus decision gate until a human approves. Nothing jumps the gate.
A read-only SearchProvider gathers mentions, normalizes, deduplicates by fingerprint and scores them with a deterministic local scorer — no model, no key required for the offline loop.
Need, pain and comparison signals cluster into demand insights that stay pending_review until a human confirms or rejects them.
High-intent signals promote into leads idempotently, inherit their score and evidence, and move through a validated new→qualified→contacted→won/lost state machine with a follow-up watch.
A draft goes to zeus for a D2 decision and returns held. The SMTP mail port fires only after human approval — no sign-off, zero outbound.
Buyer demands and supplier offers are matched across category, quantity, price, deadline and region into an explainable 0–100 score, then routed into a contact draft for outreach.
Each module owns one part of the growth loop and talks to the rest through explicit ports. Search, mail and decision are contracts, so a new search provider, another mail channel or the zeus kernel all plug in without rewriting the domain.
A SearchProvider port with an offline deterministic provider and an online Tavily provider; signals are deduplicated, scored and clustered into reports with evidence URLs.
Contacts, leads, a validated state machine, follow-up timeline and a watch for due re-engagement — every transition lands in lead_events.
Campaigns and drafts with a submit→zeus→approve→send flow. Bounces suppress, replies reflow into follow-ups, and open tracking is off by default.
Buyer demands × supplier offers scored across five dimensions into an explainable match, with an introduction builder that feeds outreach.
A DecisionPort hands outbound actions to zeus over HTTP via zeus-client. There is no local grading; an unreachable zeus returns 503.
A MailPort with an SMTP adapter (STARTTLS or implicit TLS). Off mode fails closed; smtp without host/from returns 422.
Classifies a signal as buyer demand or supplier offer and extracts category, quantity, price, MOQ, region and email with rules — never hard-filling a missing field.
A pure five-dimension scorer (category 40 / quantity 20 / price 20 / deadline 10 / region 10) with per-dimension reasons.
SQLite locally and PostgreSQL in production; six paired migrations across sixteen tables, enforced by check:migrations.
Growth OS reaches real people by email and touches real market data. It would rather refuse and return a precise error than perform something it cannot authorize. These four guarantees are enforced by the code and its tests.
Missing search credentials return 422 CREDENTIAL_MISSING; an unreachable zeus returns 503 DECISION_UNAVAILABLE; mail in off mode returns 503. No outbound ever happens without authorization.
The repo never grades locally. Every outbound email goes to the decision kernel over HTTP — single send D2, batch or high-impact D3 — and waits for human approval.
Offline search is an explicitly labeled replay, never a silent stand-in for a real provider; and no sign-off means zero outbound, verified by a strong negative test.
Signals deduplicate by fingerprint, lead promotion is idempotent, and lead_events plus outreach_events record every transition and send, matching zeus decision records one to one.
HTTP v1 routes, a composition root, domain modules and three ports. Storage speaks both SQLite and PostgreSQL, with dialect confined to the storage layer and paired, numbered migrations.
Node ≥22, TypeScript strict (noUncheckedIndexedAccess, exactOptionalPropertyTypes), 84 Vitest cases across 14 files with the G1–G5 gates.
Bundled node:sqlite locally with zero dependencies, PostgreSQL in production; sqlite/pg migrations are paired by number and enforced by check:migrations.
SearchProvider (offline/online), DecisionPort (zeus-client), MailPort (SMTP).
research · crm · outreach · match · decision · mail · structurer · matcher · storage.
Loads .env, injects the search provider, DecisionPort and MailPort at assembly; domain modules depend only on ports.
Research, CRM, outreach and match routes with one unified error envelope { error: { code, message, retryable, details? } }.
No live credentials required: offline search replays recorded fixtures, a fake-zeus decision path and the G1–G5 test suites prove the loop, and SQLite is the default with PostgreSQL ready for production.
# clone + install
$ git clone https://github.com/bayernjf/growth-os.git growth-os
$ cd growth-os && npm install
# migrate the local SQLite store
$ npm run migrate:up
# start — http://127.0.0.1:8787
$ npm startG1–G5 milestone artifact. Live search needs a Tavily key; email needs SMTP and a running zeus.