Filter weak local campaign source units - #227
lumenstech wants to merge 54 commits into
Conversation
release: promote main
…es--release chore: release release
release: promote main
release: promote main
release: promote main
release: promote main
release: promote main
release: promote main
release: promote main
release: v1.8.1
release: v1.8.2
release: v1.9.0
release: v1.10.0
release: v1.11.0
release: v1.12.0
release: v1.13.0
release: v1.14.0
release: v1.15.0
release: v1.15.1
release: v1.15.2
release: v1.15.3
|
Someone is attempting to deploy a commit to the Comp AI - PoC Team on Vercel. A member of the Team first needs to authorize it. |
|
Retargeted this onto
Nothing is wrong with your branch. If the diff now shows commits that are already on git fetch origin main
git rebase origin/main
git push --force-with-lease |
There was a problem hiding this comment.
40 issues found across 34 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/agent/agent/cli/local-campaign-worker.ts">
<violation number="1" location="apps/agent/agent/cli/local-campaign-worker.ts:23">
P1: When an option is absent, `argument()` returns the command instead of `undefined`. Normal `run`, `list`, and `export` invocations therefore fail while resolving omitted options. Return `undefined` when `index < 0`.</violation>
<violation number="2" location="apps/agent/agent/cli/local-campaign-worker.ts:68">
P1: CLI overrides bypass the campaign schema's Ollama and source-unit maxima. A command such as `--max-total-ollama-calls=100000` removes the configured call guardrail. Parse the merged campaign with `localCampaignSchema` before running.</violation>
</file>
<file name="apps/agent/agent/lib/local-lead-extraction.ts">
<violation number="1" location="apps/agent/agent/lib/local-lead-extraction.ts:105">
P1: When the model fabricates an evidence excerpt that is absent from the page, `extractLocalLead` still accepts and stages the lead. Pass the source text into validation and reject excerpts not found in it.</violation>
<violation number="2" location="apps/agent/agent/lib/local-lead-extraction.ts:136">
P1: The DNS preflight does not pin the address used by `fetch`, so DNS rebinding can bypass the local-address check. Use a resolver or HTTP dispatcher that validates and pins the connected address for the request.</violation>
<violation number="3" location="apps/agent/agent/lib/local-lead-extraction.ts:137">
P2: Every IPv6 source URL fails DNS validation because Node includes brackets in `parsed.hostname`, but `dns.lookup` expects the unbracketed address. Strip the brackets before lookup while retaining the original URL for `fetch`.</violation>
<violation number="4" location="apps/agent/agent/lib/local-lead-extraction.ts:154">
P1: A public source can redirect to `http://127.0.0.1/...`, and the default `fetch` follows that redirect without another DNS safety check. Disable automatic redirects or validate every `Location` target before following it.</violation>
</file>
<file name="packages/db/prisma/schema.prisma">
<violation number="1" location="packages/db/prisma/schema.prisma:1754">
P1: Mappings are global even though `canonicalId` points to business-unit-scoped records. A mapping in one business unit overwrites or marks the same source key as mapped in another; scope mappings by `businessUnitId` and every lookup.</violation>
<violation number="2" location="packages/db/prisma/schema.prisma:1782">
P2: Campaign members can target nonexistent canonical records because `companyId` and `personId` have no foreign keys. Add optional relations to `CanonicalCompany` and `CanonicalPerson`, then enforce the allowed target combinations.</violation>
</file>
<file name="apps/agent/agent/lib/local-campaign-staging.ts">
<violation number="1" location="apps/agent/agent/lib/local-campaign-staging.ts:26">
P2: On Windows or when the repository path contains spaces, `new URL(import.meta.url).pathname` is not a filesystem path. Use `fileURLToPath(import.meta.url)` before calling `dirname`.</violation>
<violation number="2" location="apps/agent/agent/lib/local-campaign-staging.ts:69">
P1: Every lead from the same source domain becomes `duplicate`, even when its company, contact, and content differ. Remove the standalone domain key; source-unit pages contain multiple legitimate companies.</violation>
</file>
<file name="apps/agent/agent/lib/local-ollama.ts">
<violation number="1" location="apps/agent/agent/lib/local-ollama.ts:55">
P1: When Ollama sends headers but stalls while streaming the body, `fetchImpl` resolves and this `finally` clears the timer before `response.json()` runs. Keep the abort timer active through status and body parsing, or race the entire fetch-and-parse operation, so a generation cannot exceed `per_ollama_call_timeout_ms` indefinitely.</violation>
</file>
<file name="apps/api/src/ingest/signal-qualification.service.ts">
<violation number="1" location="apps/api/src/ingest/signal-qualification.service.ts:129">
P1: When two promotion requests for the same signal run concurrently, both can pass `opportunityMapping` and create separate opportunities and deals before the mapping upsert resolves the race. Serialize or idempotently create promotion state under a transaction with a database-enforced uniqueness strategy, rather than relying on the final mapping upsert.</violation>
<violation number="2" location="apps/api/src/ingest/signal-qualification.service.ts:149">
P1: When `createDeal` validation or a later write fails, `promote` leaves the canonical opportunity behind because it persists it before validating the visible-deal path. Validate all prerequisites before writing, and wrap the promotion writes in an atomic transaction so failed promotions cannot create orphaned or partially updated records.</violation>
</file>
<file name="apps/api/src/ingest/ingest.service.ts">
<violation number="1" location="apps/api/src/ingest/ingest.service.ts:96">
P2: When a caller supplies `entity` or `signal_score` inside `payload` and omits the optional top-level field, these assignments replace the supplied value with `null`. Preserve payload values unless the corresponding top-level input was explicitly provided.</violation>
<violation number="2" location="apps/api/src/ingest/ingest.service.ts:139">
P1: When an accepted payload contains a nonnumeric score string, PostgreSQL throws during the inbox query before returning any rows. Guard the casts with numeric validation, or normalize and validate score values at ingest, in every score expression.</violation>
</file>
<file name="apps/app/app/(app)/[slug]/intelligence/intelligence-inbox.tsx">
<violation number="1" location="apps/app/app/(app)/[slug]/intelligence/intelligence-inbox.tsx:216">
P1: When a reviewer opens another signal, `SignalReviewSheet` reuses the previous signal's score fields and owner. Key the sheet by `sourceRecordId` or reset both states when `row` changes.</violation>
<violation number="2" location="apps/app/app/(app)/[slug]/intelligence/intelligence-inbox.tsx:217">
P1: After resolving or qualifying a signal, the open sheet still shows the old company and score, leaving promotion disabled or incorrectly enabled. Derive the sheet row from the refreshed `rows` by `sourceRecordId` and close it when the selected row leaves the current view.</violation>
</file>
<file name="apps/agent/agent/lib/local-campaign-source-units.ts">
<violation number="1" location="apps/agent/agent/lib/local-campaign-source-units.ts:146">
P2: When a page contains only event-level text and its own event URL, `hasUsefulPageEvidence` treats that URL as company evidence and the runner sends the whole page to Ollama. Ignore the parent event URL for this check or require company-specific evidence before allowing page-level fallback.</violation>
<violation number="2" location="apps/agent/agent/lib/local-campaign-source-units.ts:255">
P2: When the same company appears in a row and an anchor with different surrounding text, the differing `content_hash` makes this composite key unique despite matching website or company identity. Use the first available stable identity key, falling back to the hash only when neither website nor company is available, so duplicate units do not consume separate Ollama calls.</violation>
<violation number="3" location="apps/agent/agent/lib/local-campaign-source-units.ts:284">
P1: When a normal table or list row exceeds 120 characters, `candidateCompanyName` sees the flattened row as one oversized segment and returns `null`. Preserve block or cell boundaries, or extract the company name before applying the segment-length limit, so useful named-company rows are not rejected as `weak_website_only` or `insufficient_company_evidence`.</violation>
<violation number="4" location="apps/agent/agent/lib/local-campaign-source-units.ts:429">
P2: When a company-labeled utility anchor uses `javascript:`, `tel:`, or `data:`, `normalizeWebsite` returns it as `candidate_website` and the rejection logic counts it as evidence. Accept only `http:` and `https:` URLs before passing the value into website and evidence checks.</violation>
</file>
<file name="packages/db/prisma/migrations/20260902184536_sequencenow_crm_foundation/migration.sql">
<violation number="1" location="packages/db/prisma/migrations/20260902184536_sequencenow_crm_foundation/migration.sql:11">
P1: On a fresh deployment, this migration leaves `business_unit` empty, so `IngestService.signal()` rejects every project as unknown. Seed the required business units or add a provisioning path before enabling ingestion.</violation>
<violation number="2" location="packages/db/prisma/migrations/20260902184536_sequencenow_crm_foundation/migration.sql:234">
P1: When two business units use the same source identity, this global index makes the later ingest overwrite the first unit's `businessUnitId` and payload. Include `businessUnitId` in the source identity and update the lookup and conflict target accordingly.</violation>
<violation number="3" location="packages/db/prisma/migrations/20260902184536_sequencenow_crm_foundation/migration.sql:249">
P2: Because `companyId` and `personId` are nullable, PostgreSQL allows duplicate company-only or person-only members despite this unique index. Use `NULLS NOT DISTINCT` or equivalent partial unique indexes for the intended deduplication.</violation>
<violation number="4" location="packages/db/prisma/migrations/20260902184536_sequencenow_crm_foundation/migration.sql:291">
P1: These independent foreign keys allow an opportunity in one business unit to reference a company in another. Enforce matching `businessUnitId` values with composite tenant-scoped keys.</violation>
</file>
<file name="apps/api/src/ingest/ingest.contracts.ts">
<violation number="1" location="apps/api/src/ingest/ingest.contracts.ts:30">
P1: This accepts `javascript:` and `data:` URLs. Clicking a shared signal can execute attacker-controlled content. Restrict `sourceUrl` to HTTP and HTTPS URLs.</violation>
<violation number="2" location="apps/api/src/ingest/ingest.contracts.ts:104">
P2: When a caller sends `domain: ""`, `resolveCompany` ignores the signal domain and can create a duplicate company without a domain. Reject blank domains with `.min(1)`.</violation>
<violation number="3" location="apps/api/src/ingest/ingest.contracts.ts:155">
P2: When promotion creates a deal, `amountUsd` bypasses the CRM amount ceiling and can create an out-of-range amount or fail during persistence. Apply the shared dollar cap to this input and the payload fallback before `deals.create`.</violation>
</file>
<file name="apps/agent/agent/lib/local-campaign-cache.ts">
<violation number="1" location="apps/agent/agent/lib/local-campaign-cache.ts:30">
P2: When `pages.jsonl` contains one truncated or invalid JSON line, `readCachedPage` throws before checking the requested URL, causing every seed to fail as a fetch error. Skip malformed cache lines and refetch that URL instead of treating cache corruption as a page-fetch failure.</violation>
<violation number="2" location="apps/agent/agent/lib/local-campaign-cache.ts:43">
P1: A seed hangs indefinitely when its server stalls after response headers. Keep the fetch deadline active while reading the body.</violation>
</file>
<file name="apps/agent/agent/lib/local-campaign-schema.ts">
<violation number="1" location="apps/agent/agent/lib/local-campaign-schema.ts:13">
P2: When a campaign declares only `search`, the schema reports it valid but the runner has no search path, so the run can silently process zero seeds. Reject `search` until the runner implements it, or wire this source type to an actual search input.</violation>
<violation number="2" location="apps/agent/agent/lib/local-campaign-schema.ts:29">
P2: When `required_fields` contains an unknown lead field, validation succeeds and the runner marks that requirement as satisfied because `undefined !== null`. Restrict `required_fields` to fields produced by the local lead schema.</violation>
</file>
<file name="apps/agent/agent/lib/local-campaign-runner.ts">
<violation number="1" location="apps/agent/agent/lib/local-campaign-runner.ts:461">
P2: When a fetch or extraction takes longer than `perSeedTimeoutMs`, this check is reached only after the operation has already completed, and the lead can still be staged. Enforce the deadline around each awaited stage and mark the seed timed out without processing its result.</violation>
<violation number="2" location="apps/agent/agent/lib/local-campaign-runner.ts:474">
P2: When schema repair is needed, one extraction invokes Ollama twice but this counter records one call, so `max_total_ollama_calls`, per-seed limits, progress, and `ollama_calls` undercount actual requests. Count each generation attempt and enforce the budget before retries.</violation>
</file>
<file name="apps/api/src/ingest/ingest.module.ts">
<violation number="1" location="apps/api/src/ingest/ingest.module.ts:9">
P1: Because `IngestModule` does not import `TrpcModule`, its `@Router` cannot resolve the `AuthMiddleware` attached by `@UseMiddlewares`, so the ingest procedures will fail to register or the application will fail during router initialization. Import `TrpcModule` directly in this module.</violation>
</file>
<file name="apps/agent/agent/lib/local-campaign-extraction.ts">
<violation number="1" location="apps/agent/agent/lib/local-campaign-extraction.ts:154">
P2: When a model puts a non-null scalar outside `evidence_excerpt`, the extractor fails without its one repair attempt. Include scalar-evidence validation errors in `needsSchemaRepair` so this path can be repaired consistently.</violation>
</file>
<file name="apps/agent/agent/lib/local-lead-staging.ts">
<violation number="1" location="apps/agent/agent/lib/local-lead-staging.ts:17">
P2: Symlinked `var/local-leads` components bypass this guard and redirect `readFile` and `appendFile` outside the staging directory. Resolve existing components with `realpath` or reject symlinks before writing.</violation>
</file>
<file name="apps/agent/campaigns/partwall-2027.json">
<violation number="1" location="apps/agent/campaigns/partwall-2027.json:4">
P2: The campaign declares customer, geography, event-window, and source-type constraints, but the runner never applies them. Out-of-scope companies can therefore still become staged leads. Apply these constraints during extraction or filtering, or remove the misleading fields.</violation>
</file>
<file name="apps/agent/agent/lib/local-campaign-lead-schema.ts">
<violation number="1" location="apps/agent/agent/lib/local-campaign-lead-schema.ts:6">
P2: When a table or list source unit contains a bare `www.…` website, the extractor preserves that value, but this `.url()` check rejects it because it is not an absolute URL. Normalize bare domains before validation or use the same non-URL website contract as the existing local lead schema, otherwise valid company rows can be dropped after the repair retry.</violation>
</file>
<file name="apps/agent/agent/lib/local-campaign-scoring.ts">
<violation number="1" location="apps/agent/agent/lib/local-campaign-scoring.ts:21">
P2: When the model puts a campaign keyword or exclusion only in `project_fit_reason`, this scorer changes `fit_score` without source evidence. Match only source-backed fields so generated rationale cannot create or remove campaign fit.</violation>
</file>
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
| return ( | ||
| args | ||
| .find((value) => value.startsWith(`${flag}=`)) | ||
| ?.slice(flag.length + 1) ?? args[index + 1] |
There was a problem hiding this comment.
P1: When an option is absent, argument() returns the command instead of undefined. Normal run, list, and export invocations therefore fail while resolving omitted options. Return undefined when index < 0.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/agent/agent/cli/local-campaign-worker.ts, line 23:
<comment>When an option is absent, `argument()` returns the command instead of `undefined`. Normal `run`, `list`, and `export` invocations therefore fail while resolving omitted options. Return `undefined` when `index < 0`.</comment>
<file context>
@@ -0,0 +1,126 @@
+ return (
+ args
+ .find((value) => value.startsWith(`${flag}=`))
+ ?.slice(flag.length + 1) ?? args[index + 1]
+ );
+}
</file context>
| let response: Response; | ||
| try { | ||
| response = await Promise.race([ | ||
| fetchImpl(url, { |
There was a problem hiding this comment.
P1: A public source can redirect to http://127.0.0.1/..., and the default fetch follows that redirect without another DNS safety check. Disable automatic redirects or validate every Location target before following it.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/agent/agent/lib/local-lead-extraction.ts, line 154:
<comment>A public source can redirect to `http://127.0.0.1/...`, and the default `fetch` follows that redirect without another DNS safety check. Disable automatic redirects or validate every `Location` target before following it.</comment>
<file context>
@@ -0,0 +1,301 @@
+ let response: Response;
+ try {
+ response = await Promise.race([
+ fetchImpl(url, {
+ signal: controller.signal,
+ headers: { accept: "text/html,text/plain;q=0.9" },
</file context>
| ].join("\n"); | ||
|
|
||
| const result = await client.generate({ model: LOCAL_LEAD_MODEL, prompt }); | ||
| const lead = parseLocalLeadResponse(result.text, sourceUrl); |
There was a problem hiding this comment.
P1: When the model fabricates an evidence excerpt that is absent from the page, extractLocalLead still accepts and stages the lead. Pass the source text into validation and reject excerpts not found in it.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/agent/agent/lib/local-lead-extraction.ts, line 105:
<comment>When the model fabricates an evidence excerpt that is absent from the page, `extractLocalLead` still accepts and stages the lead. Pass the source text into validation and reject excerpts not found in it.</comment>
<file context>
@@ -0,0 +1,301 @@
+ ].join("\n");
+
+ const result = await client.generate({ model: LOCAL_LEAD_MODEL, prompt });
+ const lead = parseLocalLeadResponse(result.text, sourceUrl);
+ const contentHash = createHash("sha256").update(text, "utf8").digest("hex");
+
</file context>
| createdAt DateTime @default(now()) | ||
| updatedAt DateTime @updatedAt | ||
|
|
||
| @@unique([sourceSystem, sourceType, sourceId, canonicalType]) |
There was a problem hiding this comment.
P1: Mappings are global even though canonicalId points to business-unit-scoped records. A mapping in one business unit overwrites or marks the same source key as mapped in another; scope mappings by businessUnitId and every lookup.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/db/prisma/schema.prisma, line 1754:
<comment>Mappings are global even though `canonicalId` points to business-unit-scoped records. A mapping in one business unit overwrites or marks the same source key as mapped in another; scope mappings by `businessUnitId` and every lookup.</comment>
<file context>
@@ -1583,3 +1592,249 @@ model Apikey {
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ @@unique([sourceSystem, sourceType, sourceId, canonicalType])
+ @@index([canonicalType, canonicalId])
+ @@map("record_mapping")
</file context>
|
|
||
| function dedupeKeys(lead: LocalStagedLead): string[] { | ||
| return [ | ||
| `domain:${normalizeDomain(lead.source_domain)}`, |
There was a problem hiding this comment.
P1: Every lead from the same source domain becomes duplicate, even when its company, contact, and content differ. Remove the standalone domain key; source-unit pages contain multiple legitimate companies.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/agent/agent/lib/local-campaign-staging.ts, line 69:
<comment>Every lead from the same source domain becomes `duplicate`, even when its company, contact, and content differ. Remove the standalone domain key; source-unit pages contain multiple legitimate companies.</comment>
<file context>
@@ -0,0 +1,118 @@
+
+function dedupeKeys(lead: LocalStagedLead): string[] {
+ return [
+ `domain:${normalizeDomain(lead.source_domain)}`,
+ lead.company_name ? `company:${normalizeText(lead.company_name)}` : null,
+ lead.email ? `email:${normalizeText(lead.email)}` : null,
</file context>
| export const localCampaignLeadSchema = z | ||
| .object({ | ||
| company_name: z.string().trim().min(1).nullable(), | ||
| website: z.string().trim().url().nullable(), |
There was a problem hiding this comment.
P2: When a table or list source unit contains a bare www.… website, the extractor preserves that value, but this .url() check rejects it because it is not an absolute URL. Normalize bare domains before validation or use the same non-URL website contract as the existing local lead schema, otherwise valid company rows can be dropped after the repair retry.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/agent/agent/lib/local-campaign-lead-schema.ts, line 6:
<comment>When a table or list source unit contains a bare `www.…` website, the extractor preserves that value, but this `.url()` check rejects it because it is not an absolute URL. Normalize bare domains before validation or use the same non-URL website contract as the existing local lead schema, otherwise valid company rows can be dropped after the repair retry.</comment>
<file context>
@@ -0,0 +1,29 @@
+export const localCampaignLeadSchema = z
+ .object({
+ company_name: z.string().trim().min(1).nullable(),
+ website: z.string().trim().url().nullable(),
+ contact_name: z.string().trim().min(1).nullable(),
+ role: z.string().trim().min(1).nullable(),
</file context>
| website: z.string().trim().url().nullable(), | |
| website: z.string().trim().min(1).nullable(), |
|
|
||
| export function hasUsefulPageEvidence(text: string): boolean { | ||
| const cleaned = cleanText(text); | ||
| return looksCompanyLevel(cleaned) && !isOnlyEventLevel(cleaned); |
There was a problem hiding this comment.
P2: When a page contains only event-level text and its own event URL, hasUsefulPageEvidence treats that URL as company evidence and the runner sends the whole page to Ollama. Ignore the parent event URL for this check or require company-specific evidence before allowing page-level fallback.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/agent/agent/lib/local-campaign-source-units.ts, line 146:
<comment>When a page contains only event-level text and its own event URL, `hasUsefulPageEvidence` treats that URL as company evidence and the runner sends the whole page to Ollama. Ignore the parent event URL for this check or require company-specific evidence before allowing page-level fallback.</comment>
<file context>
@@ -0,0 +1,460 @@
+
+export function hasUsefulPageEvidence(text: string): boolean {
+ const cleaned = cleanText(text);
+ return looksCompanyLevel(cleaned) && !isOnlyEventLevel(cleaned);
+}
+
</file context>
| lead.company_name, | ||
| lead.website, | ||
| lead.location, | ||
| lead.project_fit_reason, |
There was a problem hiding this comment.
P2: When the model puts a campaign keyword or exclusion only in project_fit_reason, this scorer changes fit_score without source evidence. Match only source-backed fields so generated rationale cannot create or remove campaign fit.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/agent/agent/lib/local-campaign-scoring.ts, line 21:
<comment>When the model puts a campaign keyword or exclusion only in `project_fit_reason`, this scorer changes `fit_score` without source evidence. Match only source-backed fields so generated rationale cannot create or remove campaign fit.</comment>
<file context>
@@ -0,0 +1,91 @@
+ lead.company_name,
+ lead.website,
+ lead.location,
+ lead.project_fit_reason,
+ lead.evidence_excerpt,
+ ]
</file context>
| export const resolveSignalCompanyInput = signalSourceRecordInput.extend({ | ||
| companyId: z.string().trim().min(1).nullable().optional(), | ||
| companyName: z.string().trim().min(1).max(320).nullable().optional(), | ||
| domain: z.string().trim().max(320).nullable().optional(), |
There was a problem hiding this comment.
P2: When a caller sends domain: "", resolveCompany ignores the signal domain and can create a duplicate company without a domain. Reject blank domains with .min(1).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/api/src/ingest/ingest.contracts.ts, line 104:
<comment>When a caller sends `domain: ""`, `resolveCompany` ignores the signal domain and can create a duplicate company without a domain. Reject blank domains with `.min(1)`.</comment>
<file context>
@@ -0,0 +1,186 @@
+export const resolveSignalCompanyInput = signalSourceRecordInput.extend({
+ companyId: z.string().trim().min(1).nullable().optional(),
+ companyName: z.string().trim().min(1).max(320).nullable().optional(),
+ domain: z.string().trim().max(320).nullable().optional(),
+ createIfMissing: z.boolean().default(false),
+ queueResearch: z.boolean().default(true),
</file context>
| domain: z.string().trim().max(320).nullable().optional(), | |
| domain: z.string().trim().min(1).max(320).nullable().optional(), |
| } | ||
| for (const line of raw.split("\n")) { | ||
| if (!line.trim()) continue; | ||
| const row = JSON.parse(line) as CachedPage; |
There was a problem hiding this comment.
P2: When pages.jsonl contains one truncated or invalid JSON line, readCachedPage throws before checking the requested URL, causing every seed to fail as a fetch error. Skip malformed cache lines and refetch that URL instead of treating cache corruption as a page-fetch failure.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/agent/agent/lib/local-campaign-cache.ts, line 30:
<comment>When `pages.jsonl` contains one truncated or invalid JSON line, `readCachedPage` throws before checking the requested URL, causing every seed to fail as a fetch error. Skip malformed cache lines and refetch that URL instead of treating cache corruption as a page-fetch failure.</comment>
<file context>
@@ -0,0 +1,55 @@
+ }
+ for (const line of raw.split("\n")) {
+ if (!line.trim()) continue;
+ const row = JSON.parse(line) as CachedPage;
+ if (row.url === url) return row;
+ }
</file context>
Summary
Pilot Result
Compared with pilot-005 on the same 24-seed PartWall set:
Safety
Tests
Next
After this, stop adding more local runner PRs for now. Run real lead batches and tune campaigns based on output quality.
Summary by cubic
Filters weak local campaign source units before they are sent to Ollama, reducing false-positive leads and Ollama calls. Adds a local campaign runner, a signal ingest API, and an Intelligence inbox for reviewing incoming signals.
Highlights
var/local-leads; no CRM, Neon, or launch-agent changes.ingestendpoints for signal submission, inbox, company resolution, and qualification.Written for commit 8186392. Summary will update on new commits.