Prospect Intelligence Platform

A safety-first Phase 8 design/implementation boundary for manual, evidence-led prospect qualification, controlled source ingestion, bounded website scanning, and domain intelligence review. Phase 8 website scanning is a conservative observation workflow: it never submits forms, executes JavaScript, follows unsafe protocols, or authorizes outreach. Automated outreach is disabled, and no live source may be enabled without explicit approval.

Included

  • Dependency-free Python/SQLite API under apps/api.
  • Tenant-scoped business detail APIs with child intelligence/evidence records, provenance fields, notes, pipeline state, and audit history.
  • Server-side normalization, conservative website classification, exact deduplication, versioned scoring, and suppression checks. Phase 6 documents the SA phone/location canonical forms and the review-only fuzzy-match contract.
  • Bounded list pagination and server-side filters so a tenant cannot request an unbounded prospect collection.
  • Responsive static dashboard under apps/web with authenticated explorer filters, paginated results, detail review, manual intake, notes/pipeline context, evidence provenance, and browser-only CSV preview.
  • Docker Compose runtime with non-root containers, read-only filesystems, health checks, and a named SQLite data volume.
  • Browser authentication with server-side sessions and an optional first-run admin bootstrap.
  • Phase 4 MVP job monitor and SQLite-backed job/event schema/API surface, with the production limitations documented below.
  • Phase 5 source-ingestion contract: an approved source registry owns adapter terms, rate limits, retention, and health/circuit policy; CSV and manual reference adapters are the safe initial adapters.
  • Discovery queries are recorded as bounded, auditable intent and dry-run plans. Recording a query does not perform network discovery or imply that results exist.

Current workflow and Phase 4/5 boundary

  1. A permitted workspace member manually creates or reviews a prospect.
  2. The business detail response is the aggregate record for that tenant; related intelligence/evidence rows are returned only through the tenant-scoped detail surface.
  3. Each manually entered intelligence item should retain its source/provenance (for example, source label or URL, observed value, and captured/verified time). Missing provenance is a data-quality limitation, not permission to infer facts.
  4. Members use the pipeline state and notes to coordinate human review. A state change or note is an application event and is included in the record's audit/activity history where exposed by the API.
  5. Suppression remains a hard safety boundary. Suppressed or unreviewed records must not be treated as eligible for contact.

The API applies the organization/tenant boundary server-side to list, detail, child-record, notes, pipeline, and audit reads and writes. Clients must use the returned pagination metadata and follow next/previous links or tokens rather than assuming that one response contains the whole tenant dataset. See apps/api/README.md for the route contract and limits.

Phase 4 jobs/live logging contract

The planned asynchronous contract is: create one tenant-scoped job, return a stable job identifier, and move it through queuedrunning → a terminal state (succeeded, failed, cancelled). Each accepted request should carry an idempotency key whose scope and request fingerprint prevent duplicate jobs while allowing a safe replay of the original result. A job should persist append-only events with a monotonically increasing per-job sequence number, timestamp, level/type, safe message, and job/tenant identifiers.

Clients should poll a tenant-scoped job status/events endpoint using after_sequence (or an equivalent cursor), with bounded backoff and terminal-state handling. SSE is a planned low-latency delivery option, not a current implementation; polling remains the compatibility fallback. Cancellation and retry must be explicit, authorized controls: cancellation is cooperative and may finish as cancelled or report that the job is already terminal; retry creates a new attempt while retaining the original job/idempotency lineage and must not duplicate side effects.

The current MVP has SQLite job/event persistence, job status/list/detail and event APIs, cancellation/retry controls, and a browser job monitor that polls while work is active. SSE is not implemented; it remains a future delivery optimization over the persisted cursor. There is no Redis/Celery worker: the current in-process worker is suitable only for development/pilot use and must not be treated as durable, horizontally scalable execution.

Run locally

cd apps/api
python3 -m unittest discover -v
python3 app/main.py --host 127.0.0.1 --port 8000 --db /tmp/prospects.db

Serve the UI separately:

cd apps/web
python3 -m http.server 8080

Open http://127.0.0.1:8080. Set window.API_BASE in the browser console to http://127.0.0.1:8000 when testing the authenticated API locally, then sign in with the configured workspace credentials.

API smoke calls

curl http://127.0.0.1:8000/api/v1/health/live
curl 'http://127.0.0.1:8000/api/v1/businesses?page=1&page_size=25&pipeline_stage=new'
curl http://127.0.0.1:8000/api/v1/businesses/1
curl -X POST http://127.0.0.1:8000/api/v1/businesses \
  -H 'content-type: application/json' \
  -d '{"name":"Example Plumbing","website":"https://example.invalid","email":"info@example.invalid","phone":"+27 21 555 0100"}'

The protected calls require the authenticated session cookie. Exact child-record, notes, pipeline, and audit routes are documented in apps/api/README.md and are never cross-tenant addressable by changing an ID.

Compose

cp .env.example .env
docker compose config --quiet
docker compose up --build -d
curl -fsS http://localhost:8000/api/v1/health/live
curl -fsS http://localhost:8080/healthz
docker compose down

Compose passes the optional BOOTSTRAP_ADMIN_EMAIL and BOOTSTRAP_ADMIN_PASSWORD values to the API. Set both in an untracked .env only when provisioning a fresh instance, then remove them and rotate the password after the bootstrap admin is created. No credentials belong in this repository.

Authenticated browser requests use a server-side session cookie; login creates a session and logout invalidates it. The liveness endpoints (GET /api/v1/health/live and GET /healthz) intentionally remain unauthenticated so Docker, ingress, and monitoring health checks can use them. Authentication is not a substitute for tenant/authorization checks: protected routes must enforce the session and organization boundary server-side.

Phase 5 source boundary and remaining limitations

Phase 5 defines a source adapter contract and registry; Phase 8 adds a bounded website-observation adapter, but it does not implement general network discovery, enrichment scheduling, or a live external-source adapter. A source adapter must declare its identity, terms owner, permitted purpose, rate limits, retention class, query/result schema, dry-run behavior, and health/circuit controls. CSV and manual reference adapters may be used for operator-supplied data; they must preserve source attribution and raw source records, and must not silently turn preview data into outreach or verified facts.

A discovery query is a tenant-scoped, bounded, auditable request that can be validated and dry-run without contacting a source. Any live source requires explicit product/legal/security approval, a registered adapter, and an operational enablement decision; absent all three, execution must fail closed. Circuit-open, rate-limit, terms, or approval failures must produce a safe non-live result. Raw source records are retained only under the approved retention class and must exclude secrets and unnecessary personal data.

SQLite, the in-process worker, and the named local volume are suitable for the pilot only; production migration, durable queue/worker leases, event retention/backup, SSE delivery, and tested backup/restore remain unfinished. Redis and Celery are not implemented. The development password fallback is PBKDF2 rather than production Argon2id. Before production, complete the gates in docs/SECURITY.md and docs/OPERATIONS.md, including MFA, TLS, CSRF protection, source approval and terms review, rate limiting, circuit monitoring, tenant-scoped job/event authorization, idempotent side-effect handling, durable raw-source/audit retention, SSRF-safe fetching if a future scanner is approved, and tested backups/restores.

Phase 6 normalization and deduplication boundary

Normalization is deterministic and versioned. For South African data, phone values are stripped to digits, local 10-digit 0 forms and 00 27 forms are converted to canonical +27..., and unknown international numbers retain their explicit country code; presentation punctuation must not create a second identity. Locations derive whitespace/case/diacritic-folded province, city, and suburb fields. A normalized value is not proof that the underlying observation is correct.

Exact keys (for example, canonical domain, email, or phone) may identify duplicate candidates. Fuzzy matching is deterministic and suggestion-only: the same inputs and normalization version produce the same candidate, score, and reason. A suggested match must never merge automatically. Use the documented thresholds: >=0.90 is a strong suggestion, 0.750.8999 is a review suggestion, and <0.75 is not surfaced as a suggestion. A human with permission must explicitly confirm each merge.

Every confirmed merge must create a tenant-scoped, immutable-enough merge snapshot before mutation, recording the surviving and absorbed IDs, normalized comparison inputs, score/reasons, acting user, timestamp, and schema/normalization versions. The operation must be reversible from that snapshot. It must preserve or re-parent every child, evidence item, provenance/source-record link, note, pipeline/audit history, and original source identity; conflicts remain visible for human resolution rather than being silently overwritten. Cross-tenant candidates are never comparable or mergeable, and each suggestion, confirmation, rejection, reversal, and preservation/conflict decision belongs in the audit trail.

The MVP now exposes deterministic match suggestions at GET /api/v1/businesses/{id}/matches, explicit merge confirmation in the web review dialog, tenant-scoped merge history, and POST /api/v1/merge-history/{id}/reverse. The implementation remains a pilot boundary: hardening is still needed for a dedicated merge permission, stronger server-side confirmation semantics, full snapshot conflict handling, and production-grade rollback guarantees. Do not describe a normalized or suggested match as verified identity, discovery, enrichment, or outreach authorization.

Phase 7 domain intelligence boundary

Domain intelligence is an observation and review aid, not proof of business identity, control of a domain, or availability. A domain normalizer may derive a lowercase ASCII/Unicode comparison form and a registrable domain using a versioned Public Suffix List (PSL). The PSL is an input with update/version drift: unknown, private, malformed, single-label, localhost, and IP-literal values must remain unresolved rather than guessed. A subdomain is not automatically a separate candidate, and a public suffix itself is never a registrable domain.

DNS status is explicit: not_checked, pending, resolved, nxdomain, no_data, timeout, servfail, blocked, and error describe the check outcome, not a business conclusion. MX, NS, and TXT observations may be absent, partial, truncated, stale, resolver-dependent, or blocked; no record is not proof that mail, delegation, ownership, or a business relationship is absent. Store the resolver/source, observed time, TTL where supplied, and uncertainty/error metadata. Caches must be bounded and keyed by normalized query/type/class plus resolver policy, honor an observed TTL without extending authority, and expose freshness/staleness; cached data must never be presented as a fresh check.

Association confidence is separate from DNS status and from duplicate score. It must be derived from explainable, tenant-scoped evidence (for example, an operator citation, an exact business-domain observation, or corroborating DNS facts), retain the algorithm/version and uncertainty reasons, and remain suggestion-only. Candidate generation must reject cross-tenant records, public-suffix-only values, malformed or IP-only inputs, and suppressed/merged targets as applicable; it must not auto-attach a domain or infer ownership from a shared, parked, wildcard, sibling-subdomain, homograph, or merely resolvable domain. Every candidate needs human review, provenance, and an auditable accept/reject decision.

The platform must not claim that a domain is available, unregistered, or safe to acquire without an explicitly authorized availability provider registered with current product/legal/security approval, terms, tenant scope, rate limits, retention, and operational enablement. DNS nxdomain or no_data is not an availability result. Provider outages, rate limits, stale responses, conflicting results, and unknown status must remain unknown/unavailable, fail closed, and never trigger purchase, outreach, or automated follow-up.

Phase 7 remains a documentation/contract boundary in this MVP: there is no live DNS resolver, PSL-backed enrichment worker, cache service, or availability provider in Compose. Production work still includes selecting and versioning the PSL, implementing bounded DNS resolution and TTL-aware cache invalidation, defining MX/NS/TXT parsing and uncertainty retention, adding association review/permission/audit tests, and completing an approved availability-provider integration with SSRF/network egress controls, monitoring, retention, and incident/rollback procedures.

Phase 8 website scanning boundary

Website scanning is a bounded, tenant-scoped observation—not a crawler, browser, verifier, or outreach mechanism. A scan may fetch only http and https URLs after strict parsing and normalization. It must reject credentials, non-web schemes (file:, ftp:, gopher:, data:, javascript:, and similar), malformed hosts, localhost, IP literals where policy disallows them, and targets in loopback, private, link-local, multicast, reserved, or cloud-metadata ranges. DNS is resolved immediately before connection and the destination is revalidated at connection time; every redirect is limited, normalized, and revalidated for protocol, hostname, DNS, and IP range before it is followed. DNS answers must not be trusted from the initial validation alone (including rebinding changes).

Each scan enforces hard budgets: total wall-clock/request time, response bytes, body bytes retained, redirect count, and page/link crawl count and depth. Budgets apply across redirects and discovered links, with bounded concurrency, retries, and response decompression; a limit, timeout, DNS error, unsupported content type, or partial fetch produces an explicit incomplete/unknown outcome rather than an empty result. The scanner fetches HTML and other explicitly allowed small resources only; it does not submit forms, send credentials, execute JavaScript, load browser plugins, or perform arbitrary subresource requests.

Classifications are conservative and explainable. unknown, blocked, timeout, partial, and error remain distinct from a positive observation. A page can be classified only from bounded fetched content and must retain URL, redirect chain, response metadata, observed time, scanner/policy version, limits, and uncertainty reasons. A detected contact form, script, tracking tag, or business phrase is an observation—not proof of ownership, consent, deliverability, safety, or permission to contact.

Scan history is tenant-scoped and append-oriented. Results and cache entries are keyed by normalized URL plus scanner/policy/version inputs, bounded by size and retention, and expose observed_at, freshness/expiry, and whether a result came from cache. A cache hit is never represented as a fresh scan; policy, DNS, or scanner-version changes require revalidation/invalidation. History must not leak response bodies, secrets, cookies, authorization headers, or unnecessary personal data across tenants.

The website scanner remains a pilot boundary. Compose does not provide a production egress proxy, durable scan queue, distributed crawl coordinator, hardened DNS resolver, or compliance-grade result store. Production still requires independent SSRF testing (including DNS rebinding and redirect chains), egress/network policy, resource isolation, durable retention/deletion, authenticated scan-history authorization, rate limits and abuse controls, observability, and a reviewed policy for content types, robots/terms, caching, and incident response. Scans must never trigger acquisition, verification, enrichment, or outreach automatically.

Phase 9 public official-site contact extraction boundary

Phase 9 adds a passive, suggestion-only contact-observation workflow. When explicitly enabled, extraction may inspect bounded HTML from the business's approved/public official-site origin and its same-site contact/about pages; it is not general web search, crawling, enrichment, identity verification, or outreach. Only public page content and explicitly permitted mailto:/visible contact values may be considered. Do not submit forms, authenticate, bypass access controls, probe SMTP, send test messages, or contact a person or organization.

Every candidate contact must retain provenance: source URL and page location/context, extraction method, observed time, scanner/extractor and policy versions, and the exact uncertainty/reason code. Confidence is an explainable review signal, not deliverability, consent, ownership, or permission to contact. Classify role addresses separately from person addresses and classify free-mail domains separately from business-domain addresses; neither classification is proof of identity. Syntax validation is only a parse result. MX/DNS status is independently uncertain (not_checked, resolved, nxdomain, no_data, timeout, servfail, blocked, or error), and no MX result may be presented as deliverability.

False-positive exclusions must reject or quarantine values from asset URLs, image/file names, scripts/styles, example/test/placeholder domains, documentation text, tracking addresses, and malformed or unsupported schemes. Apply tenant-scoped suppressions before a candidate is persisted, returned, exported, or queued for review; suppressed values remain do-not-contact and suppression always wins over confidence, role, syntax, MX, pipeline, or verification state. Extraction is bounded by per-request and aggregate page/URL, byte, time, redirect, candidate, and concurrency limits. Store only the minimum contact value and lineage required for review, apply a documented retention/deletion class, and redact secrets and unnecessary personal data from logs and audit events.

Phase 9 does not authorize automated outreach. There is no SMTP probing, SMTP banner/VRFY/EXPN check, email validation message, send endpoint, campaign queue, or follow-up action. An extracted address is an observation requiring human review and explicit policy authorization before any separate future contact workflow.

Phase 10 configurable scoring boundary

Phase 10 separates fit score, priority band, and contact eligibility. A score is a deterministic ranking signal; it never authorizes contact. Rules are represented by a named, versioned rule set with explicit weights, thresholds, band definitions, eligibility gates, freshness policy, and suppression behavior. The active rule-set identifier/version is stored with each result so a historical score can be explained without silently applying today's policy.

A reproducible calculation uses the tenant-scoped business snapshot, normalized values, eligible evidence observations, rule-set/version, algorithm/version, and calculation time/freshness inputs. Explanations must retain the contributing factors, normalized inputs or evidence references, weights/points, exclusions, uncertainty reasons, and the final band. Do not accept a client-submitted score, band, eligibility flag, or rule version as authoritative.

Priority bands are policy labels (for example, high/medium/low or an explicitly configured equivalent) and must be derived from the versioned thresholds. Eligibility is evaluated separately and fail-closed: suppression/do-not-contact, stale or expired required evidence, unresolved/uncertain required signals, missing policy prerequisites, and authorization/tenant failures can make a prospect ineligible regardless of score. suppressed always wins and must remain visible; stale and uncertain observations must not be silently treated as absent or positive.

Recalculation is an explicit, tenant-scoped operation. It must snapshot the input/rule versions, record before/after score, band, eligibility, explanation, actor/job, timestamp, and reason in the audit trail, and be idempotent or safely repeatable. A policy/rule change must not rewrite history without an auditable recalculation; partial or failed recalculation must report its incomplete state rather than presenting mixed results as current.

Phase 10 remains a pilot boundary unless the runtime exposes all of the above controls end to end. Production work includes administrative rule-set lifecycle/approval, immutable calculation inputs, deterministic rounding/tie-breaking, scheduled recalculation with leases, retention and export semantics for explanations/audit, and regression tests proving suppression, stale, uncertain, and cross-tenant isolation behavior. See the API, security, and operations contracts for the authoritative safeguards.

Phase 11 dashboard and review workflow boundary

Phase 11 defines the operator-facing review workflow around saved filters, a tenant-scoped review queue, and deliberately bounded bulk actions. A saved filter is a named, server-persisted filter definition (including search, score/status/pipeline/eligibility criteria, sort, and page-size preferences) owned by one tenant. Save, load, rename, and delete operations must authorize against the authenticated tenant; filter definitions are configuration, not authorization, and must not contain secrets or unbounded query instructions.

The review queue is a derived view of the current tenant's matching businesses. Queue counts and rows must use the same filter predicate, exclude records the caller cannot read, and make suppression and merge state explicit. Suppressed/do-not-contact records are never eligible for contact or bulk contact action; merged/non-active records are not merge-eligible. Match suggestions remain review-only: a score cannot auto-merge, and every merge requires an explicit permitted human confirmation and a reversible, tenant-scoped snapshot.

Bulk actions must be bounded by a server-enforced maximum, an explicit set of tenant-scoped record IDs or an immutable filter snapshot, and a preview/confirmation step. The API must re-check authorization, suppression, merge eligibility, and current record state at execution time, use an idempotency key or equivalent safe retry control, and return per-record outcomes rather than claiming that the whole selection succeeded. A filtered count is not permission to act on an unbounded result set, and bulk actions must never imply outreach or bypass review.

Dashboard counts are clickable only when their scope and query semantics are clear. A click must carry the exact tenant-scoped predicate that produced the count; it must not silently broaden to all records. Labels must distinguish the current page from the full matching set, and loading, stale, unavailable, and error states must not be rendered as zero. Every saved-filter mutation, queue decision, bulk preview/confirmation/result, suppression decision, and merge/reversal must emit an audit event with tenant, actor, action, timestamp, filter/selection snapshot, bounded counts, per-item outcomes, and relevant policy/version identifiers. Audit details must be redacted and tenant-scoped.

The Phase 11 slice now provides tenant-scoped saved-filter storage/listing, a bounded review-queue read, clickable dashboard filter metadata, and a bounded bulk review route (POST /api/v1/businesses/bulk-review) for verify/reject/assign. The current implementation is still pilot-grade: saved-filter update/delete routes are not wired, the queue does not yet expose full matching-set counts or filter snapshots, dashboard click metadata is not a complete predicate contract, bulk actions lack preview/idempotency/per-record outcomes, and audit coverage is aggregate for bulk operations. Production work requires those hardening items plus regression tests for suppression precedence, merge eligibility, cross-tenant IDs, stale counts, and partial bulk failure.

Phase 12 CRM pipeline, interactions, outcomes, reporting, and suppression center

Phase 12 adds the CRM coordination contract around a tenant-scoped pipeline, an append-oriented interaction timeline, normalized interaction outcomes, bounded reporting, and a suppression center. These features are review and record-keeping tools; they do not turn the platform into an outreach system. Every read, write, export, report, and background operation must carry the authenticated organization_id scope, and a child ID, report ID, filter, or aggregate count is never authorization.

Pipeline state and transition rules

The canonical lifecycle is newcontactedqualifiedproposalnegotiationwon or lost. The API exposes these configured stages; any future paused/disqualified state must be explicitly added to the tenant's stage configuration and may be reopened only by an authorized human with a reason. A transition must name the target state, actor, timestamp, and reason/source; the API validates transitions server-side and records the before/after state in the audit trail. Repeating the current state is idempotent, not a new transition. Direct jumps, client-supplied history, edits to historical transitions, and transitions for merged/inactive records are rejected. Reopening lost, paused, or disqualified creates a new transition and does not rewrite history.

An interaction may suggest a state change, but it never changes pipeline state implicitly. A state change and its related interaction/outcome are separate auditable events, and a failed or partial write must not leave a fabricated outcome. won/lost require an explicit outcome and reason; won is not proof of payment or fulfillment. Suppression/do-not-contact overrides every pipeline state and makes contact eligibility false.

Interactions and outcome taxonomy

Interactions are append-only, tenant-scoped records of an operator-observed event. The record should retain the business/contact reference when known, channel (note, phone, email, meeting, or other), occurred time, actor, bounded redacted summary, provenance, and correlation/idempotency key. The current product does not send or validate communication: an interaction records what an operator says happened, not what the platform performed.

Outcomes are normalized and mutually explicit: connected, no_answer, left_message, meeting_booked, meeting_held, qualified, disqualified, won, lost, and other. other is an explicit catch-all, not proof of success or failure; missing/unknown data must not be silently converted to a negative outcome. disqualified and lost require an explicit reason under the configured workflow. do_not_contact is a separate safety state, immediately creates or confirms tenant suppression, and cannot be downgraded by a later positive outcome. Free-text notes supplement but do not replace the taxonomy; corrections append a new record rather than mutating history.

Reporting, audit, tenant scope, and retention

Reports use an explicit as_of time, tenant scope, timezone, date interval, and inclusion policy. Pipeline reports count the latest effective state per active business; interaction reports count events by occurred_at (not ingestion time); outcome reports count normalized outcomes and may include a separate unknown bucket. Suppressed, merged, deleted, and inactive records must be labeled and excluded from contact-eligible totals; they must not silently disappear from safety/audit counts. Page counts, matching-set counts, and distinct-business counts are different metrics and must be named. Late-arriving or corrected interactions preserve original and corrected timestamps and are never double-counted without an explicit correction policy.

Audit events cover pipeline transitions, interaction/outcome creation or correction, suppression changes, report/export requests, and report results. Store tenant, actor, target, action, timestamps, safe reason, bounded filter/as-of snapshot or hash, policy/version, correlation/idempotency ID, and per-item outcomes where applicable. Redact full contact values and free text unless required for the approved purpose. Retain CRM records, suppression decisions, interaction provenance, report snapshots, and audit events according to the approved tenant/data-retention schedule; deletion or legal-hold behavior must be explicit and auditable. A report cache is tenant-keyed, bounded, and labeled with its as_of/freshness; it is never a live authorization decision.

Suppression center and remaining outreach limitations

Suppression is a tenant-scoped deny list for email, domain, phone, and other approved identifiers. Matching is normalized server-side and must run before persistence, display, export, report inclusion as eligible, queueing, or any future action. Suppression wins over pipeline state, outcome, score, verification, cached data, and operator intent. The center must show the source, reason, actor, created/updated time, scope, and effective status; removal or expiry requires explicit authorization, reason, audit, and re-evaluation. Existing records remain visible as Do not contact and are not silently deleted. No Phase 12 route may send email/SMS, probe SMTP, validate an address by message, create a campaign, schedule follow-up delivery, or imply consent/deliverability. Any future outreach requires separate product, legal, security, and operational approval and must remain disabled by default.

Phase 12 remains pilot-grade until transition validation, immutable interaction/outcome history, suppression precedence, report definitions/timezones, retention/deletion jobs, export controls, idempotent writes, and cross-tenant regression tests are exercised end to end. The current Compose stack still has no durable CRM worker, scheduler, delivery provider, or outreach capability.

Verification

python3 -m unittest discover -v -s apps/api/tests -t apps/api
python3 -m compileall -q apps/api apps/web
git diff --check
docker compose config --quiet

Phase 13 optional AI assistance boundary

Phase 13 adds optional, human-reviewed AI assistance for summarization, classification, and evidence-oriented suggestions. AI is a drafting aid, not a source of truth, verifier, identity resolver, score/eligibility authority, or CRM actor. It is disabled unless an explicitly configured provider and tenant-scoped policy permit the requested operation. A provider failure, timeout, quota/rate limit, approval expiry, missing evidence, or ambiguous result fails closed to unknown/unavailable; it must never be represented as a successful empty result.

Provider configuration is optional and deny-by-default. A deployment may configure a primary provider and an optional fallback, but each provider must have an approved purpose/capability, tenant scope, data-processing/retention terms, region/egress policy, model/version, timeout/token budget, rate limit, cost ceiling, and operational enablement. Fallback is permitted only to another pre-approved provider for the same purpose and data class; it must not broaden tenant scope, retention, prompt data, or authority. No provider credentials belong in source, Compose files, logs, or committed .env files. The current Compose stack does not provision an AI provider; production enablement remains gated configuration work.

AI requests must minimize data before transmission: send only the fields and evidence excerpts needed for the approved task, redact secrets and unnecessary personal/contact data, avoid raw page bodies and credentials, and record a redacted request/policy fingerprint rather than a prompt containing sensitive data. Every generated suggestion must cite the tenant-scoped evidence IDs/source references and preserve evidence hash, citation, observed time, provider/model/version, policy version, and uncertainty. Hashes identify the exact evidence snapshot for reproducibility; they do not prove that the source is true. Missing, conflicting, stale, suppressed, or low-quality evidence must remain visible and must not be filled with invented facts.

AI output is an untrusted draft. It requires an authorized human approval/rejection (and an explicit reason for material changes) before it can become a stored claim, score input, pipeline update, contact decision, export, or any other consequential record. Approval must re-check tenant scope, suppression, evidence freshness, policy/version, and the unchanged evidence hash; stale or changed inputs require re-review. AI cannot create or edit CRM interactions/outcomes as if communication occurred, send messages, create campaigns, schedule follow-ups, contact prospects, merge records, acquire domains, or perform autonomous outreach. Suppressed/do-not-contact records remain visible for safety review and are never made eligible by an AI result.

Phase 13 is pilot-only. Before production, implement provider allowlisting and secret management, data-processing agreements, prompt/output redaction tests, evidence hash/citation verification, approval and rollback semantics, immutable audit coverage, tenant-isolation tests, retention/deletion/legal-hold jobs, cost/rate monitoring, incident disablement, and evaluation for hallucination, prompt injection, bias, and stale/conflicting evidence. See the API, web, security, and operations contracts for the authoritative limitations.

See apps/api/README.md, apps/web/README.md, docs/SECURITY.md, and docs/OPERATIONS.md for details.

S
Description
No description provided
Readme
429 KiB
Languages
Python 75.7%
HTML 11.1%
JavaScript 11.1%
Shell 1.6%
Dockerfile 0.4%