Repository navigation
feat(enrichment): add opt-in Precleared compliance manifest - #125
Open
gabrielaoimevbore-beep wants to merge 1 commit into
Open
gabrielaoimevbore-beep wants to merge 1 commit into
gabrielaoimevbore-beep wants to merge 1 commit into
Conversation
Add a declarative provider manifest, compliance/precleared.yaml, that asks Precleared whether a cold or marketing email may be sent to one recipient. It returns the verdict, its confidence, the deciding rule and its source, the next step and signed receipt metadata (receipt id and verify URL, check time and rules version). The result is decision support, not a legal guarantee. Opt-in and inert by default: - Bearer auth from PRECLEARED_API_KEY. Without a key, Prepare refuses and no request is made, so Precleared's keyless free tier is never used. - secrets.WorkspaceOwned makes the PRECLEARED_* settings resolve like Credential: the workspace's own value only, unless ALLOW_GLOBAL_CREDENTIAL_FALLBACK. Workspaces save them through the existing workspace-integrations route (new "precleared" integration card). - Not in any default waterfall: it runs only in a column that names it. Decision inputs are the lead's email and company plus the workspace's declared sender country, address provenance and pitch. Unset values are sent blank, which Precleared reads as not given; no consent or country fact is inferred. providerrun never serves or stores compliance verdicts in the shared enrichment cache, which is keyed per company or record. cost_per_lookup is the worst case for spend estimates. The 25 second timeout and the engine's 1 MiB response cap bound each call; error bodies are not parsed. Fixtures cover success, consent needed with blank context, uncertain, invalid input, auth, quota, rate limit, server error, malformed response, network failure and timeout, with no network or paid requests in CI. Refs debpalash#117 Signed-off-by: Gabe AO <support@precleared.dev> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FQdvgNwwnTYBHUGD2dPwFh
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.
First PR from #117: the Precleared manifest as opt-in enrichment for the current Go backend. The send gate will follow as a separate proposal against
internal/outreachsendandinternal/destdeliver, as you suggested.Disclosure: I build Precleared. The code was written with Claude (Anthropic's AI) at my direction. Anthropic's terms assign me any rights it has in that output, and I know of no licence or other restriction on it.
How this meets the checklist in #117
Manifest and registration
apps/backend-go/internal/manifest/builtin/manifests/compliance/precleared.yaml, loaded with the other built-ins (precleared_compliance, capabilitycompliance).wbregistryso it appears in the column provider picker (/api/workbooks/meta/providers). The recordedmeta/providers*fixtures are updated.preclearedcard in the settings integrations registry, so a workspace can save its own key and context through the existingPUT /api/settings/workspace-integrations/{id}route. The recordedintegrations/*list fixtures are updated.TestEmbeddedManifestsMatchLegacygets a smallgoOnlyallowlist for this Go-only manifest, so the local legacy parity check still passes.Explicit BYOK key and workspace opt-in
PRECLEARED_API_KEY. With no key,manifest.Preparerefuses withprecleared_compliance: missing PRECLEARED_API_KEYand nothing is sent, so the keyless free tier is never used. This is covered by a test.secrets.Resolver. A newsecrets.WorkspaceOwnedset makes the fourPRECLEARED_*keys resolve likeResolver.Credential: only the workspace's own value counts unlessALLOW_GLOBAL_CREDENTIAL_FALLBACK=true. That is the same policy CRM and outreach credentials follow, and it means no operator-wide key enrols a workspace.Decision inputs, nothing invented
recipient_emailemailcompany_namecompanysender_countryPRECLEARED_SENDER_COUNTRYaddress_sourcePRECLEARED_ADDRESS_SOURCEpitchPRECLEARED_PITCHaddress_sourcebecomesunknown, the cautious value.What it returns
complianceholds the verdict (ok_to_email,consent_needed,blockedorunknown).compliance_confidence,compliance_rule,compliance_rule_source,compliance_reason,compliance_noteandcompliance_next_step.compliance_footersays what the footer must include, andcompliance_recipient_countrynames the country whose rules applied.compliance_receipt_id,compliance_receipt_url(public verify endpoint),compliance_checked_atandcompliance_rules_version.compliance_disclaimercarries the decision-support notice.unknownis a successful but uncertain answer, documented as "do not send yet".Bounded calls, cost and fixtures
25 second manifest timeout. The engine's 1 MiB response cap applies, and
http_4xx/http_5xxbodies are never parsed, so payment links in a 402 never reach a lead.cost_per_lookup: 0.03is the declared worst case for spend estimates. It stays BYOK: Precleared bills the workspace's key, and a 400, 401, 402 or 429 runs no check.Fixtures in
internal/manifest/builtin/testdata/precleared/are replayed in process. They cover:unknown)No network or paid request is made in CI. They use
example.co.uk-style data only.Security-sensitive parts (per CONTRIBUTING)
secrets.Resolver.Getgains theWorkspaceOwnedcheck, placed besideGlobalOnly. It affects only the fourPRECLEARED_*keys.POST https://precleared.dev/api/v1/check?src=opengtmsends the lead's email and company name and the workspace's declared context. The URL is fixed, not templated, and goes through the normal egress guard. Credential values are scrubbed from errors as for every manifest.providerrunno longer reads or writes the shared enrichment cache for declarative providers of thecompliancecapability. That cache is keyed per company or record, so it could otherwise serve one recipient's verdict to another at the same company, or a stale one. Other providers keep the cache, and a test covers both sides.Notes
docs/providers/precleared.mdcovers setup (workspace and single-tenant), inputs, outputs, cost, errors and behaviour..env.examplegets two commented lines.workbook.Deps.Runner == nil) does not carryauth.extra_env(runplan.manifestDoc,credentials.ManifestAllow). There the context would go out blank, which Precleared treats cautiously. The worker and API paths useproviderrun, which reads it. I left that as is to keep this PR focused.make testrun is left to CI here.PR template
Refs #117
🤖 Generated with Claude Code
https://claude.ai/code/session_01FQdvgNwwnTYBHUGD2dPwFh