Files
MarketingTool/apps/web/README.md
T

155 lines
26 KiB
Markdown
Raw Normal View History

2026-09-03 11:15:54 +02:00
# ProspectOS web — Phase 9 boundary
2026-09-02 17:38:50 +02:00
2026-09-03 11:07:34 +02:00
Self-contained static frontend for the Prospect Platform API. There is no bundler or runtime dependency: serve this directory with any static HTTP server. The UI supports a manual, tenant-scoped review workflow, Phase 4 MVP job monitor, and Phase 8 scan-result/history presentation when supplied by the API. The browser does not fetch targets, submit forms, execute scan JavaScript, or send outreach; SSRF controls and budgets are server-side.
2026-09-02 17:38:50 +02:00
## Configure and run
The public runtime configuration is loaded from `config.js` before `app.js`. It contains no credentials and may safely be replaced during deployment:
2026-09-02 17:38:50 +02:00
```js
window.__PROSPECT_CONFIG__ = Object.freeze({ apiBase: 'https://api.example.invalid', assetVersion: 'phase-15' });
2026-09-02 17:38:50 +02:00
```
If `apiBase` is empty, the UI uses `window.API_BASE`, then `localStorage.prospect_api_base` when present, and otherwise targets the same origin. The dashboard requires an authenticated API session and shows the login screen until `/api/v1/auth/me` succeeds. Do not put tokens, passwords, or private keys in `config.js`.
`asset-manifest.json` records the public entrypoints, cache-busting version, and SHA-256 digests for release verification. The HTML references the static assets with the `phase-15` version query string; update those references and regenerate the manifest when changing the release version.
## Deployment readiness checks
Serve this directory from the intended static-server root, then run:
```sh
node scripts/smoke-deployment.mjs http://127.0.0.1:8080
```
The smoke script checks HTTP delivery for the manifest-listed assets, health/error pages, `healthz`, expected markers, and common hardcoded-secret patterns. It validates static readiness only; it does not deploy the app or prove API/production availability.
2026-09-02 17:38:50 +02:00
## Phase 3 UI contract
2026-09-02 17:38:50 +02:00
- The explorer requests tenant-scoped business pages from `GET /api/v1/businesses` and sends bounded pagination plus supported search/score/status filters to the API. Filtering is not a substitute for server-side authorization.
- Selecting a row loads the tenant-scoped detail view, including child intelligence/evidence records, provenance/source labels, confidence/freshness, current pipeline state, notes, and relevant audit/activity context when available.
- Add prospect, add intelligence, change pipeline state, and add note are explicit manual actions. The API records the acting user and applies permission, tenant, validation, deduplication, and suppression rules server-side.
- Evidence labels describe stored observations and their provenance. The UI must not present them as the result of automated discovery, DNS lookup, website crawling, or verification unless a future approved integration explicitly supplies that evidence.
- Review and suppressed states remain safety states. The UI shows outreach as unavailable; there is no send button, message composer, sender, or outreach endpoint.
- The CSV control is preview-only and local to the browser. Selecting a file does not persist rows or send them to the API.
The API remains the source of truth for tenant isolation, pagination bounds, filters, pipeline transitions, notes, audit records, and suppression. See `apps/api/README.md` for the route contract.
2026-09-02 17:38:50 +02:00
2026-09-03 08:46:22 +02:00
## Phase 6 normalization and deduplication UI contract
The UI may display the API's normalized SA phone and location values alongside the original observation, normalization version, and any ambiguity warning. It must not silently replace the source value or imply that a canonical form verifies identity. Duplicate candidates must show deterministic score, threshold band (`strong` `>=0.90`, `review` `0.750.8999`, or `none` `<0.75`), and explainable matching reasons.
Suggestions are review aids only. A merge flow must identify the surviving record, list all parents/children/evidence/notes/source records that will be preserved, show conflicts, and require an explicit human confirmation before calling an authorized API mutation. The UI must offer rejection and, where implemented, reversal using the merge snapshot; it must never auto-merge based on a score. Candidate, merge, snapshot, and audit data are tenant-scoped by the API, not by hidden UI state.
The current static MVP requests `/matches`, renders a **Human review required** list with confidence/reasons, asks for **Confirm merge**, and displays merge history with **Reverse merge** when the API marks it reversible. The API remains authoritative; these controls are not a substitute for server-side authorization. Existing normalization and match display remain suggestion-only; no merge happens without explicit operator confirmation.
2026-09-03 11:07:34 +02:00
## Phase 8 website-scanning UI contract
The UI may request a tenant-scoped scan through an authenticated API route when enabled and render the returned classification, status, redirect chain, observed time, scanner/policy version, cache freshness, applied budgets, and uncertainty/error reasons. The server permits only `http` and `https` targets. It must label cached data as cached/stale rather than “live,” keep `unknown`, `blocked`, `partial`, `timeout`, and `error` distinct from positive observations, and never turn a conservative classification into identity, ownership, consent, deliverability, or outreach permission.
The browser must not directly fetch arbitrary target URLs, follow redirects for scanning, submit forms, send cookies/credentials, execute target JavaScript, or expose response bodies unnecessarily. Scan history and cache controls are tenant-scoped API capabilities, not hidden client state. A result that is budget-limited or incomplete must remain visibly incomplete; no UI timer may imply that a scan completed.
2026-09-02 18:58:10 +02:00
## Phase 5 source UI contract
The web client may display registered source metadata, query mode, approval/terms state, rate-limit status, retention class, health, and circuit state returned by the API. It must label `dry_run` as a plan/validation result and distinguish operator-supplied CSV/manual references from independently verified evidence. It must not offer a live-source control unless the API reports explicit approval and operational enablement; client visibility is never an authorization control.
CSV and manual reference workflows must show source attribution, adapter/version, observed time, and any retention/redaction status. Raw source payloads should be hidden or minimized in the UI and remain tenant-scoped. A circuit-open or rate-limited source must be presented as unavailable/deferred, not as an empty discovery result. The current static client has no network discovery implementation; these are display and contract requirements for a future approved integration.
2026-09-02 18:12:21 +02:00
## Phase 4 job/live-log UI contract
A future job view should show `queued`, `running`, `succeeded`, `failed`, or `cancelled`, the current attempt, timestamps, safe error text, and a clear terminal state. It should display persisted events in sequence order, resume from the last cursor after refresh/reconnect, and tolerate duplicate events. Create/retry requests should send an idempotency key and show the returned job identity rather than starting duplicate work.
The preferred live path is SSE backed by the persisted event cursor; polling with bounded backoff is the required fallback and should be used for browsers/proxies that do not support SSE. Cancel is a cooperative action with an explicit pending/terminal result; retry is available only when the API authorizes it and must be presented as a new attempt/lineage. The UI must never infer progress from timers or claim work completed because a request was accepted.
The current client renders job status/counts, detail, structured errors, progress, and event timelines, and polls the jobs collection while queued/running work exists. It exposes authorized cancel/retry affordances based on the API response. There is no SSE client yet; polling is the current fallback and should remain available after SSE is introduced. The backend's SQLite/in-process worker is MVP-only. Do not add a Redis/Celery dependency by implication or label that worker production-ready.
2026-09-02 17:38:50 +02:00
## Browser verification
1. Start the API from `apps/api` with `python3 app/main.py`.
2. Serve this directory: `python3 -m http.server 8080 --directory apps/web`.
3. Open `http://127.0.0.1:8080`, with `window.API_BASE` set to `http://127.0.0.1:8000` using a tiny pre-load edit or browser devtools.
4. Sign in and confirm the header changes to **API connected**, tenant metrics populate, and the explorer renders a bounded page with search, score, status, and pagination controls.
5. Select a row and confirm the detail view keeps the business, child intelligence, evidence provenance, confidence/freshness, pipeline, notes, and audit context associated with that tenant.
6. Add or update only through the explicit manual controls. Confirm the refreshed detail/list state reflects the API response and that a viewer cannot mutate records.
7. Confirm review and suppressed rows show **Outreach unavailable** with the appropriate reason. Confirm there is no outreach/send endpoint or button.
8. Select a CSV and confirm a local, preview-only table appears without a network request or persistence.
9. Resize below 700px to verify the collapsible nav, stacked panels, and horizontally scrollable explorer/detail content.
A zero-dependency static smoke page (`smoke-test.html`) checks the key DOM contract in an iframe and reports pass/fail. Browser smoke coverage should treat pagination, detail child records, provenance rendering, pipeline/notes actions, and the disabled outreach state as separate checks.
2026-09-03 11:15:54 +02:00
## Phase 9 contact-observation UI contract
When the API supplies Phase 9 results, the UI may render contacts extracted from the approved/public official site and same-site contact/about pages. Display source URL/page context, extraction method, observed time, extractor/policy version, confidence and reasons, syntax status, role classification, free-mail classification, and independent MX/DNS status/freshness. Use explicit labels such as **Observation**, **Human review required**, and **Unknown**; never label a candidate verified, deliverable, owned, consented, or ready for outreach. The UI must show suppressed contacts as **Do not contact**, preserve the suppression reason, and never hide or override suppression through filters, refreshes, exports, or cached results.
Extraction is not a browser crawler. The browser must not fetch target pages directly, submit contact forms, execute target JavaScript, send credentials/cookies, probe SMTP, send validation messages, or expose an outreach/send control. Render bounded/partial/blocked/timeout/error results distinctly from an empty successful result, including candidate/page/byte/time limit reasons. Do not display false-positive candidates from assets, scripts/styles, example/test/placeholder values, tracking addresses, malformed schemes, or unrelated third-party pages.
The API remains authoritative for official-site scope, tenant isolation, suppression enforcement, limits, retention, provenance, and permissions. A cached extraction must show its observed time and freshness, never “live.” Candidate confidence, role/free-mail labels, syntax, and MX/DNS uncertainty are review metadata only and cannot enable a contact action.
2026-09-03 11:25:14 +02:00
## Phase 10 scoring UI contract
The UI may display server-provided `score`, `priority_band`, and `eligibility` as separate fields. It should show the active rule-set ID/version, calculated time/freshness, explanation factors with their points/weights, exclusions, and stale/uncertain reasons. A band is a triage label, not permission to contact; never derive or override these values solely in browser code.
Render eligibility independently and prominently: **Eligible**, **Ineligible**, or **Unknown/review required** must not be collapsed into a score band. Show suppression/do-not-contact as a hard, persistent state that wins over score, verification, pipeline, cached data, or refresh. Keep stale, expired, blocked, partial, and uncertain evidence visibly distinct from missing evidence and never present them as a positive or negative fact. There is no outreach control in this phase.
If the API exposes recalculation, the UI must show the requested rule-set/version, job/progress/partial state, actor/time, and before/after explanation or band changes; acceptance of a request is not completion. Recalculation history and audit details remain tenant-scoped server capabilities. Rule-set administration, activation, rollback, and eligibility policy are not client-side authorization controls.
Phase 10 remains a pilot display contract until the API supplies stable versioned rule metadata, reproducible input lineage, complete explanation payloads, explicit eligibility reasons, and audited recalculation results. Browser smoke coverage should include score/band disagreement with eligibility, suppression precedence, stale/uncertain rendering, version changes, partial recalculation, and cross-tenant non-disclosure.
2026-09-03 11:57:18 +02:00
## Phase 11 dashboard and review workflow UI contract
Phase 11 is the dashboard/review presentation contract for **saved filters**, a **review queue**, and bounded bulk actions. Saved filters must show a named, human-readable summary of the exact search/status/score/pipeline/eligibility predicate, sort, and page-size settings. Save/load/update/delete controls must reflect API authorization and tenant scope; the browser must not treat a filter ID, hidden field, or local-only copy as permission. If filters are shared, the UI must show that they are tenant-scoped and read-only or editable as returned by the API.
The review queue must make its scope visible: current filter name or predicate, matching-set versus current-page count, ordering, pagination/cursor state, and `has_more`. A row can be selected only from the current tenant-scoped result. Suppressed/do-not-contact state must remain prominent and disable contact-related actions; merged/non-active records are not merge-eligible. Merge suggestions remain **Human review required** and need an explicit confirmation dialog; no score or checkbox may auto-merge.
Bulk actions must show the bounded selection size and server maximum, provide a preview before confirmation, and report per-record success/skipped/failed outcomes. The UI must refresh or reconcile stale rows after execution, preserve suppression and eligibility reasons, and never describe a partial result as complete. A clickable count must navigate using the exact predicate that produced the count; distinguish full matching-set counts from page counts, and show loading/error/unavailable rather than zero. Count cards are navigation affordances, not authorization controls.
The UI must expose audit context for saved-filter changes, queue decisions, bulk preview/confirmation/execution, suppression/eligibility decisions, and merge/reversal: actor, time, bounded selection/filter snapshot, result totals, and safe reason/version metadata. It must not render secrets or unnecessary contact data. The API remains authoritative for tenant isolation, permissions, re-checks, idempotency, suppression precedence, and audit persistence.
The current Phase 11 client now renders saved-view controls, a review queue capped at 100 visible records, selectable rows, and explicit verify/reject bulk review actions. It also renders clickable dashboard metric cards. Current limitations are material: saved views can be created/loaded/deleted in the client but update is not exposed; queue selection is visible-row-only and the UI does not show a server maximum/preview/per-record outcomes; dashboard links use client filter shortcuts rather than a complete server predicate; and suppression/merge eligibility and audit results still depend on the API response. No bulk action sends outreach or auto-merges.
2026-09-03 12:07:58 +02:00
## Phase 12 CRM UI contract
The Phase 12 UI presents a tenant-scoped pipeline, append-only interaction timeline, normalized outcomes, bounded reporting, and a suppression center. It must show the exact tenant/filter/as-of/timezone scope of every view and distinguish page counts, matching-set counts, event counts, and distinct-business counts. Loading, stale, unavailable, and error states are not zero. The API is authoritative; a hidden field, report ID, saved filter, or visible row cannot grant access.
Pipeline controls display the configured stages (`new`, `contacted`, `qualified`, `proposal`, `negotiation`, `won`, `lost`) and require an explicit reason for `won`, `lost`, and any configured reopen action. The UI must not offer direct jumps, edit historical transitions, or advance a stage merely because an interaction was added. Interactions show channel, actor, occurred time, provenance, safe summary, and outcome. Corrections are visibly appended/superseding, not destructive edits. `other` is distinct from a success or failure claim.
The outcome vocabulary is `connected`, `no_answer`, `left_message`, `meeting_booked`, `meeting_held`, `qualified`, `disqualified`, `won`, `lost`, and `other`. `other` is explicit uncertainty/catch-all metadata, not proof of success or failure; the UI must not invent an outcome for missing data. `do_not_contact` is a separate persistent suppression state, not a deliverability or engagement outcome, and must disable contact-related controls.
The suppression center shows normalized identifier, source, reason, scope, actor, effective time, and audit context. It must apply to records before display/export/report eligibility and must never silently delete a suppressed record. Unsuppression/removal is an explicit authorized action with confirmation and reason. Report and export screens must show freshness, as-of, timezone, filter snapshot, retention class where applicable, and safe partial/per-record results; they must not imply deliverability or outreach permission.
There is no send button, message composer, SMTP probe, validation email, campaign, delivery scheduler, or automated follow-up in Phase 12. The browser never contacts a prospect. Suppression, pipeline, outcome, report, and audit controls are presentation layers over server enforcement. The UI remains pilot-grade until browser/API smoke coverage verifies transition rejection, append-only corrections, outcome taxonomy, suppression precedence, report semantics, retention states, and cross-tenant non-disclosure.
2026-09-03 12:27:15 +02:00
## Phase 14 draft-only outreach UI contract
Phase 14 adds preparation language only. The browser may display a server-provided outreach draft and its gate status, but it must never call a provider, send a message, schedule delivery, probe SMTP, send validation mail, create a campaign, or imply that draft creation or approval is delivery. Render a persistent **Draft only — human approval required** state and keep `AUTOMATED_OUTREACH_ENABLED=false` visible as the no-send default.
A draft view must show tenant scope, recipient/channel, provider ID/version when configured, consent/legal-basis status and jurisdiction/policy version, suppression/do-not-contact status, evidence citations/source references, exact evidence snapshot hash, observed/freshness times, uncertainty/conflict reasons, rate/cap status, approval actor/time/reason/expiry, and a safe content fingerprint or bounded redacted preview. Never display provider secrets, raw prompts, credentials, unnecessary personal data, or unsupported claims. Public availability, score, pipeline state, verification, and AI confidence are not consent, lawful basis, deliverability, or permission to contact.
Approval controls must be absent or disabled unless the API reports all gates passed and the authenticated user is authorized. Approval must be an explicit confirmation of the exact draft version and evidence hash, with a reason where required; edits, changed evidence/policy, stale data, suppression, expired approval, provider failure, or uncertain legal status must invalidate it and require re-review. Rejection and expiry must remain visible. Approval never creates a send control.
If a future side-effecting API is exposed, the browser must send a tenant-scoped idempotency key and show the original bounded result on exact replay, while presenting conflicting-key, cap, suppression, provider, and gate failures distinctly from success. Display audit context for draft creation, gate decisions, citations, approval/rejection/expiry, retries, cap denials, and any delivery result, with redaction. Never turn a count or visible row into authorization.
Phase 14 is not implemented as a live outreach workflow. The current static client has no draft composer, consent ledger, provider integration, approval API, send button, delivery status, bounce/complaint handling, or legal-policy engine. Production work requires API-backed draft/version persistence, jurisdiction-specific legal review, provider/DPA and secret-management controls, server-side gates, durable approvals/audit/idempotency, suppression re-checks, rate/cost caps, kill switch, retention/deletion/legal-hold behavior, and browser/API tests proving no outbound network activity.
## Remaining limitations
2026-09-02 17:38:50 +02:00
2026-09-03 11:15:54 +02:00
The static client has no client-side crawler, scanner, contact extractor, enrichment scheduler, outreach integration, availability provider, or SSE delivery. It can display server-provided Phase 9 observations, but production still requires server-side official-site scoping, SSRF/DNS-rebinding/redirect controls, hard extraction/page/byte/time/candidate budgets, durable history/cache isolation and retention/deletion, abuse/rate controls, suppression regression tests, and authenticated provenance/audit coverage. For domain intelligence, display registrable-domain/PSL version and unresolved reasons, DNS status and freshness/TTL (not “available”), independent MX/NS/TXT uncertainty, and association confidence with explainable evidence. Never auto-attach candidate domains or treat `nxdomain`/`no_data` as availability. CSV preview is capped for display and is not an import workflow.
2026-09-03 12:16:12 +02:00
## Phase 13 optional AI assistance UI contract
The UI may offer AI drafting only when the authenticated API reports an approved, enabled capability for the current tenant and task. Provider choice, fallback, prompt construction, redaction, budgets, tenant authorization, and suppression checks are server-side; the browser must never receive provider secrets or call an AI vendor directly. Show provider/model/version and `unknown`/`unavailable`, timeout, partial, stale, or policy-blocked states distinctly from an empty or successful result.
Every suggestion must display its evidence citations, tenant-scoped evidence IDs, exact evidence hash/snapshot identifier, observed time, uncertainty/conflict reasons, and policy/provider/model versions. A citation points to the evidence used; it is not proof that the source is correct, and an AI explanation is not an independently verified fact. Do not render unsupported or invented facts about names, roles, contact details, dates, outcomes, consent, deliverability, ownership, or other claims as facts. Preserve missing and conflicting evidence instead of filling gaps. Suppressed/do-not-contact records remain visible with the safety state and never become actionable because an AI suggestion is confident.
AI output must be visibly labeled **AI suggestion — human review required** and remain read-only until an authorized human explicitly approves it. Approval must show the proposed change, citations/hash, freshness, tenant scope, and safe reason; rejection and expiry must be available. The UI must require re-review when the evidence hash or policy version changes and must display partial/failed approval rather than implying persistence. Approval does not authorize contact or verification.
No Phase 13 control may send email/SMS, probe SMTP, create a campaign, schedule follow-up, alter pipeline/interactions/outcomes as if communication occurred, merge records, acquire a domain, or perform autonomous CRM/outreach actions. The browser must not hide or export suppressed data as eligible, and exports/reports must retain safe AI provenance and redaction labels where applicable. Production remains limited until browser/API tests cover citations and hash mismatch, redaction, fallback boundaries, approval/rejection, stale/conflicting evidence, suppression precedence, tenant non-disclosure, and no-autonomy controls; the current Compose stack has no configured AI provider.
## Phase 15 deployment and readiness
The web image is a portable static server: it runs as a non-root user, serves only the files copied into `/srv`, and exposes `/healthz`. Virtualmin is responsible for DNS, HTTPS certificates, reverse-proxy routing, firewall rules, and any access control around the site. Set the API base deliberately for the deployed origin; do not put credentials or provider secrets in HTML, JavaScript, local storage, image layers, or `.env` files. `CORS_ORIGINS` must exactly match the approved HTTPS origin rather than a broad wildcard.
`/healthz` is an unauthenticated process/liveness check. API `/api/v1/health/ready` checks SQLite readiness, but neither endpoint proves tenant authorization, backup validity, or external dependencies. Route traffic only after the web and API containers report `healthy`, the HTTPS proxy reaches the intended containers, and an authenticated browser/API smoke test succeeds. The browser must never be used to test or initiate outbound prospect/provider traffic; `AUTOMATED_OUTREACH_ENABLED=false` remains visible as the no-send default.
For releases, validate the exact static image and API image together, capture image digests and configuration revision, and retain the prior pair for rollback. If a schema/data migration is involved, the API owner must complete backup/restore and migration validation before the web image is promoted. The current client has no service-worker cache or migration logic; stale browser tabs must be refreshed after a release, and Virtualmin/CDN caching must not serve an old API contract indefinitely. SQLite, HTTP-only local Compose, lack of a readiness endpoint, and lack of a production asset/CDN pipeline are explicit limitations.