From 363a2a5e884460a6185176d53d2a29760d0f18e1 Mon Sep 17 00:00:00 2001 From: chitcommit <208086304+chitcommit@users.noreply.github.com> Date: Mon, 6 Apr 2026 12:28:15 +0000 Subject: [PATCH 1/4] feat: add governance compliance cron, MCP tools, and govClient integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wires ChittyCommand as the orchestrator for governance/compliance monitoring: - Job types: add sos_status, recorder_filings, assessor_check to ScrapeJobType union and all validation arrays (jobs, MCP, sync) - govClient: new integration client for ChittyGov API (compliance calendar, verify filing, list monitors) - Cron Phase 11: syncGovernanceCompliance() — pulls upcoming filings from ChittyGov (60-day window), upserts into cc_obligations as category='governance', enqueues verification scrapes for active monitors - MCP tools: query_compliance_calendar and verify_compliance_filing proxying to ChittyGov - Env: add CHITTYGOV_URL binding - Sync routes: sos_status/recorder_filings/assessor_check as valid manual trigger sources Co-Authored-By: Claude Opus 4.6 (1M context) --- src/index.ts | 1 + src/lib/cron.ts | 131 +++++++++++++++++++++++++++++++++++++- src/lib/integrations.ts | 87 +++++++++++++++++++++++++ src/lib/job-dispatcher.ts | 5 +- src/routes/jobs.ts | 2 +- src/routes/mcp.ts | 60 +++++++++++++++-- src/routes/sync.ts | 6 +- 7 files changed, 284 insertions(+), 8 deletions(-) diff --git a/src/index.ts b/src/index.ts index b2a0446..becdc82 100644 --- a/src/index.ts +++ b/src/index.ts @@ -53,6 +53,7 @@ export type Env = { CHITTYASSETS_URL?: string; CHITTYSCRAPE_URL?: string; CHITTYROUTER_URL?: string; + CHITTYGOV_URL?: string; CHITTYAGENT_SCRAPE_URL?: string; CHITTYREGISTER_URL?: string; CHITTYCHAT_DATA_API?: string; diff --git a/src/lib/cron.ts b/src/lib/cron.ts index 95c94dd..68816ca 100644 --- a/src/lib/cron.ts +++ b/src/lib/cron.ts @@ -1,6 +1,6 @@ import type { NeonQueryFunction } from '@neondatabase/serverless'; import type { Env } from '../index'; -import { plaidClient, financeClient, mercuryClient, scrapeClient, routerClient } from './integrations'; +import { plaidClient, financeClient, mercuryClient, scrapeClient, routerClient, govClient } from './integrations'; import { runTriage } from './triage'; import { matchTransactions } from './matcher'; import { generateProjections } from './projections'; @@ -135,6 +135,19 @@ export async function runCronSync( } catch (err) { console.error('[cron:dispute_reconcile] failed:', err); } + + // Phase 11: Governance compliance check + // Pulls upcoming deadlines from ChittyGov, upserts into cc_obligations, + // and enqueues verification scrapes for monitors with scrapers. + try { + const govSynced = await syncGovernanceCompliance(env, sql); + if (govSynced > 0) { + recordsSynced += govSynced; + console.log(`[cron:governance] synced ${govSynced} compliance filings`); + } + } catch (err) { + console.error('[cron:governance] failed:', err); + } } if (source === 'utility_scrape') { @@ -856,3 +869,119 @@ export async function syncEmailParsedBills(env: Env, sql: NeonQueryFunction): Promise { + const gov = govClient(env); + if (!gov) { + console.warn('[governance] ChittyGov not configured — skipping'); + return 0; + } + + let synced = 0; + + // Pull upcoming filings within 60 days + const calendarResult = await gov.getComplianceCalendar({ status: 'upcoming,due_soon,overdue', days: 60 }); + if (!calendarResult?.filings) { + console.warn('[governance] No filings returned from ChittyGov'); + return 0; + } + + for (const filing of calendarResult.filings) { + // Upsert into cc_obligations as governance category + const payee = `${filing.jurisdiction} — ${filing.filingType.replace(/_/g, ' ')}`; + const amount = filing.fee ? Number(filing.fee) : 0; + + try { + const [existing] = await sql` + SELECT id FROM cc_obligations + WHERE category = 'governance' + AND metadata->>'filing_id' = ${filing.filingId} + LIMIT 1 + `; + + if (existing) { + await sql` + UPDATE cc_obligations + SET status = ${filing.status === 'overdue' ? 'overdue' : 'pending'}, + due_date = ${filing.dueDate}, + amount_due = CASE WHEN ${amount} > 0 THEN ${amount} ELSE amount_due END, + metadata = jsonb_set( + COALESCE(metadata, '{}'::jsonb), + '{last_gov_sync}', + ${JSON.stringify({ syncedAt: new Date().toISOString(), daysUntil: filing.daysUntil, latePenalty: filing.latePenalty })}::jsonb + ), + updated_at = NOW() + WHERE id = ${existing.id} + `; + } else { + await sql` + INSERT INTO cc_obligations (category, subcategory, payee, amount_due, due_date, recurrence, status, late_fee, metadata) + VALUES ( + 'governance', + ${filing.filingType}, + ${payee}, + ${amount}, + ${filing.dueDate}, + ${filing.filingType === 'annual_report' ? 'yearly' : filing.filingType === 'tax_estimate' ? 'quarterly' : null}, + ${filing.status === 'overdue' ? 'overdue' : 'pending'}, + ${filing.latePenalty ? 0 : null}, + ${JSON.stringify({ + filing_id: filing.filingId, + jurisdiction: filing.jurisdiction, + entity_name: filing.entityName, + authority_url: filing.authorityUrl, + source: 'chittygov_sync', + })}::jsonb + ) + `; + } + synced++; + } catch (dbErr) { + console.error(`[governance] DB error for filing ${filing.filingId}:`, dbErr); + } + } + + // Enqueue verification scrapes for active monitors + try { + const monitorsResult = await gov.getMonitors('active'); + if (monitorsResult?.monitors) { + const chittyId = await env.COMMAND_KV.get('default:chitty_id') || undefined; + for (const monitor of monitorsResult.monitors) { + if (!monitor.scraperId) continue; + + // Map monitor types to scrape job types + const jobTypeMap: Record = { + sos_status: 'sos_status', + registered_agent: 'portal_scrape', + recorder_filings: 'recorder_filings', + assessment_status: 'assessor_check', + }; + const jobType = jobTypeMap[monitor.monitorType]; + if (!jobType) continue; + + try { + await enqueueJob(sql, jobType, { + scraper_id: monitor.scraperId, + monitor_id: monitor.monitorId, + ...((monitor.scrapeInput as Record) || {}), + }, { + chittyId, + cronSource: 'governance', + }, env); + } catch (err) { + console.error(`[governance] enqueue ${monitor.monitorId} failed:`, err); + } + } + } + } catch (err) { + console.error('[governance] monitor enqueue failed:', err); + } + + return synced; +} diff --git a/src/lib/integrations.ts b/src/lib/integrations.ts index e4fa537..55d76de 100644 --- a/src/lib/integrations.ts +++ b/src/lib/integrations.ts @@ -995,3 +995,90 @@ export function notionClient(env: Env) { }, }; } + +// ── ChittyGov ───────────────────────────────────────────── +// Corporate governance: compliance calendar, filing deadlines, monitors + +export interface ComplianceFiling { + filingId: string; + entityId: number; + entityName?: string; + filingType: string; + jurisdiction: string; + dueDate: string; + status: string; + daysUntil: number; + fee?: string; + latePenalty?: string; + authorityUrl?: string; +} + +export interface ComplianceMonitor { + monitorId: string; + entityId?: number; + entityName?: string; + monitorType: string; + scraperId?: string; + scrapeInput?: Record; + checkFrequency: string; + lastCheckedAt?: string; + status: string; +} + +export function govClient(env: Env) { + const govUrl = env.CHITTYGOV_URL; + if (!govUrl) return null; + + const headers: Record = { + 'Content-Type': 'application/json', + 'X-Source-Service': 'chittycommand', + }; + + return { + getComplianceCalendar: async (params?: { status?: string; days?: number; entityId?: string }): Promise<{ filings: ComplianceFiling[]; total: number } | null> => { + try { + const qs = new URLSearchParams(); + if (params?.status) qs.set('status', params.status); + if (params?.days) qs.set('days', String(params.days)); + if (params?.entityId) qs.set('entity_id', params.entityId); + const url = `${govUrl}/api/compliance/calendar${qs.toString() ? `?${qs}` : ''}`; + const res = await fetch(url, { headers, signal: AbortSignal.timeout(10000) }); + if (!res.ok) return null; + return await res.json() as { filings: ComplianceFiling[]; total: number }; + } catch (err) { + console.error('[gov] getComplianceCalendar error:', err); + return null; + } + }, + + verifyFiling: async (filingId: string, data?: { source?: string; data?: Record }): Promise => { + try { + const res = await fetch(`${govUrl}/api/compliance/verify/${encodeURIComponent(filingId)}`, { + method: 'POST', + headers, + body: JSON.stringify(data || {}), + signal: AbortSignal.timeout(10000), + }); + return res.ok; + } catch (err) { + console.error('[gov] verifyFiling error:', err); + return false; + } + }, + + getMonitors: async (status?: string): Promise<{ monitors: ComplianceMonitor[]; total: number } | null> => { + try { + const qs = status ? `?status=${status}` : ''; + const res = await fetch(`${govUrl}/api/compliance/monitors${qs}`, { + headers, + signal: AbortSignal.timeout(10000), + }); + if (!res.ok) return null; + return await res.json() as { monitors: ComplianceMonitor[]; total: number }; + } catch (err) { + console.error('[gov] getMonitors error:', err); + return null; + } + }, + }; +} diff --git a/src/lib/job-dispatcher.ts b/src/lib/job-dispatcher.ts index 375c5a5..13d273a 100644 --- a/src/lib/job-dispatcher.ts +++ b/src/lib/job-dispatcher.ts @@ -7,7 +7,10 @@ export type ScrapeJobType = | 'court_docket' | 'cook_county_tax' | 'mr_cooper' - | 'portal_scrape'; + | 'portal_scrape' + | 'sos_status' + | 'recorder_filings' + | 'assessor_check'; export type ScrapeJobStatus = | 'queued' diff --git a/src/routes/jobs.ts b/src/routes/jobs.ts index 2e3bbd2..da646c7 100644 --- a/src/routes/jobs.ts +++ b/src/routes/jobs.ts @@ -65,7 +65,7 @@ jobRoutes.post('/jobs', async (c) => { return c.json({ error: 'job_type and target are required' }, 400); } - const validTypes: ScrapeJobType[] = ['court_docket', 'cook_county_tax', 'mr_cooper', 'portal_scrape']; + const validTypes: ScrapeJobType[] = ['court_docket', 'cook_county_tax', 'mr_cooper', 'portal_scrape', 'sos_status', 'recorder_filings', 'assessor_check']; if (!validTypes.includes(body.job_type)) { return c.json({ error: `Invalid job_type. Must be one of: ${validTypes.join(', ')}` }, 400); } diff --git a/src/routes/mcp.ts b/src/routes/mcp.ts index 31b3bee..c377e03 100644 --- a/src/routes/mcp.ts +++ b/src/routes/mcp.ts @@ -5,7 +5,7 @@ import { getDb, typedRows } from '../lib/db'; import type { NeonQueryFunction } from '@neondatabase/serverless'; import { listJobs, getJobStatus, retryJob, getDeadLetters, enqueueJob } from '../lib/job-dispatcher'; import type { ScrapeJobType, ScrapeJobStatus } from '../lib/job-dispatcher'; -import { evidenceClient, ledgerClient } from '../lib/integrations'; +import { evidenceClient, ledgerClient, govClient } from '../lib/integrations'; /** * MCP (Model Context Protocol) server for ChittyCommand. @@ -302,7 +302,7 @@ const TOOLS = [ type: 'object' as const, properties: { status: { type: 'string', description: 'Filter by status', enum: ['queued', 'running', 'completed', 'failed', 'retrying', 'dead_letter'] }, - type: { type: 'string', description: 'Filter by job type', enum: ['court_docket', 'cook_county_tax', 'mr_cooper', 'portal_scrape'] }, + type: { type: 'string', description: 'Filter by job type', enum: ['court_docket', 'cook_county_tax', 'mr_cooper', 'portal_scrape', 'sos_status', 'recorder_filings', 'assessor_check'] }, chitty_id: { type: 'string', description: 'Filter by ChittyID' }, limit: { type: 'number', description: 'Max results (default 20)' }, }, @@ -348,13 +348,39 @@ const TOOLS = [ inputSchema: { type: 'object' as const, properties: { - job_type: { type: 'string', description: 'Type of scrape', enum: ['court_docket', 'cook_county_tax', 'mr_cooper', 'portal_scrape'] }, + job_type: { type: 'string', description: 'Type of scrape', enum: ['court_docket', 'cook_county_tax', 'mr_cooper', 'portal_scrape', 'sos_status', 'recorder_filings', 'assessor_check'] }, target: { type: 'object', description: 'Target parameters (e.g. {case_number}, {pin}, {property}, {portal})' }, chitty_id: { type: 'string', description: 'Optional ChittyID to bind results to' }, }, required: ['job_type', 'target'], }, }, + // ── Governance Compliance ────────────────────────────────── + { + name: 'query_compliance_calendar', + description: 'Get upcoming governance/compliance filing deadlines from ChittyGov. Returns LLC annual reports, tax estimates, and other filings with days until due.', + inputSchema: { + type: 'object' as const, + properties: { + status: { type: 'string', description: 'Filter by status (upcoming, due_soon, overdue, verified). Comma-separated for multiple.' }, + days: { type: 'number', description: 'Only show filings due within this many days (default: 90)' }, + entity_id: { type: 'string', description: 'Filter by entity ID (e.g. ENT-JAV)' }, + }, + required: [] as string[], + }, + }, + { + name: 'verify_compliance_filing', + description: 'Mark a governance filing as verified (filed/completed). Triggers recurrence roll-forward for recurring filings.', + inputSchema: { + type: 'object' as const, + properties: { + filing_id: { type: 'string', description: 'Filing ID (e.g. FIL-JAV-annual_report-2026)' }, + source: { type: 'string', description: 'Verification source (scrape, email, manual)' }, + }, + required: ['filing_id'], + }, + }, { name: 'synthesize_case_facts', description: 'Auto-pull verified facts from ChittyEvidence for a case and synthesize them into a structured litigation summary. Returns categorized facts with [GIVEN]/[DERIVED]/[UNKNOWN] tags.', @@ -1161,7 +1187,7 @@ async function executeTool(env: Env, sql: NeonQueryFunction, toolN const target = args.target as Record; if (!jobType || !target) throw new Error('Missing arguments: job_type, target'); - const validTypes: ScrapeJobType[] = ['court_docket', 'cook_county_tax', 'mr_cooper', 'portal_scrape']; + const validTypes: ScrapeJobType[] = ['court_docket', 'cook_county_tax', 'mr_cooper', 'portal_scrape', 'sos_status', 'recorder_filings', 'assessor_check']; if (!validTypes.includes(jobType)) throw new Error(`Invalid job_type. Must be one of: ${validTypes.join(', ')}`); const jobId = await enqueueJob(sql, jobType, target, { @@ -1171,6 +1197,32 @@ async function executeTool(env: Env, sql: NeonQueryFunction, toolN return { ok: true, id: jobId, status: 'queued' }; } + // ── Governance Compliance Tool Handlers ──────────────── + case 'query_compliance_calendar': { + const gov = govClient(env); + if (!gov) return { error: 'ChittyGov not configured (CHITTYGOV_URL missing)' }; + const result = await gov.getComplianceCalendar({ + status: args.status ? String(args.status) : undefined, + days: args.days ? Number(args.days) : 90, + entityId: args.entity_id ? String(args.entity_id) : undefined, + }); + if (!result) return { error: 'Failed to fetch compliance calendar from ChittyGov' }; + return result; + } + + case 'verify_compliance_filing': { + const filingId = String(args.filing_id || '').trim(); + if (!filingId) throw new Error('Missing argument: filing_id'); + const gov = govClient(env); + if (!gov) return { error: 'ChittyGov not configured (CHITTYGOV_URL missing)' }; + const ok = await gov.verifyFiling(filingId, { + source: args.source ? String(args.source) : 'mcp', + }); + return ok + ? { ok: true, filingId, message: 'Filing marked as verified' } + : { error: 'Failed to verify filing', filingId }; + } + case 'synthesize_case_facts': { const caseId = String(args.case_id || '').trim(); if (!caseId) throw new Error('Missing argument: case_id'); diff --git a/src/routes/sync.ts b/src/routes/sync.ts index 7eacebd..8fa11b2 100644 --- a/src/routes/sync.ts +++ b/src/routes/sync.ts @@ -2,7 +2,7 @@ import { Hono } from 'hono'; import type { Env } from '../index'; import { getDb } from '../lib/db'; import { matchTransactions } from '../lib/matcher'; -import { syncMercury, syncPlaid, syncFinance, syncCourtDocket, syncMrCooper, syncCookCountyTax, syncPortal } from '../lib/cron'; +import { syncMercury, syncPlaid, syncFinance, syncCourtDocket, syncMrCooper, syncCookCountyTax, syncPortal, syncGovernanceCompliance } from '../lib/cron'; export const syncRoutes = new Hono<{ Bindings: Env }>(); @@ -25,6 +25,7 @@ syncRoutes.post('/trigger/:source', async (c) => { 'mercury', 'plaid', 'chittyfinance', 'wave', 'stripe', 'turbotenant', 'chittyrental', 'court_docket', 'mr_cooper', 'cook_county_tax', + 'sos_status', 'recorder_filings', 'assessor_check', 'comed', 'peoples_gas', 'xfinity', 'citi', 'home_depot', 'lowes', ]; @@ -52,6 +53,9 @@ syncRoutes.post('/trigger/:source', async (c) => { court_docket: () => syncCourtDocket(c.env, sql), mr_cooper: () => syncMrCooper(c.env, sql), cook_county_tax: () => syncCookCountyTax(c.env, sql), + sos_status: () => syncGovernanceCompliance(c.env, sql), + recorder_filings: () => syncGovernanceCompliance(c.env, sql), + assessor_check: () => syncGovernanceCompliance(c.env, sql), }; // Resolve aliases and portal sources to their dispatcher From e4e3f712d7131c54ac12cfdbc160497c15a995a2 Mon Sep 17 00:00:00 2001 From: chitcommit <208086304+chitcommit@users.noreply.github.com> Date: Mon, 6 Apr 2026 13:08:29 +0000 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20harden=20govClient=20=E2=80=94=20aut?= =?UTF-8?q?h=20header,=20error=20logging,=20latePenalty,=20failed=20counte?= =?UTF-8?q?r?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses review findings on PR #76: - Add CHITTYGOV_TOKEN env + Bearer auth header (was missing entirely) - Log HTTP status + response body on non-ok responses (was returning null silently) - Track failed filing count and log summary on partial failures - Fix inverted latePenalty logic (was writing 0 instead of actual penalty) - Add Array.isArray guard on filings response + NaN guard on fee/penalty - Warn explicitly when getMonitors returns null instead of silent skip - Isolate KV read failure with own try-catch and clear label Co-Authored-By: Claude Opus 4.6 (1M context) --- src/index.ts | 1 + src/lib/cron.ts | 28 ++++++++++++++++++++++------ src/lib/integrations.ts | 19 +++++++++++++++++-- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/index.ts b/src/index.ts index becdc82..242c36c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,6 +54,7 @@ export type Env = { CHITTYSCRAPE_URL?: string; CHITTYROUTER_URL?: string; CHITTYGOV_URL?: string; + CHITTYGOV_TOKEN?: string; CHITTYAGENT_SCRAPE_URL?: string; CHITTYREGISTER_URL?: string; CHITTYCHAT_DATA_API?: string; diff --git a/src/lib/cron.ts b/src/lib/cron.ts index 68816ca..482a17f 100644 --- a/src/lib/cron.ts +++ b/src/lib/cron.ts @@ -884,10 +884,11 @@ export async function syncGovernanceCompliance(env: Env, sql: NeonQueryFunction< } let synced = 0; + let failed = 0; // Pull upcoming filings within 60 days const calendarResult = await gov.getComplianceCalendar({ status: 'upcoming,due_soon,overdue', days: 60 }); - if (!calendarResult?.filings) { + if (!calendarResult?.filings || !Array.isArray(calendarResult.filings)) { console.warn('[governance] No filings returned from ChittyGov'); return 0; } @@ -895,7 +896,10 @@ export async function syncGovernanceCompliance(env: Env, sql: NeonQueryFunction< for (const filing of calendarResult.filings) { // Upsert into cc_obligations as governance category const payee = `${filing.jurisdiction} — ${filing.filingType.replace(/_/g, ' ')}`; - const amount = filing.fee ? Number(filing.fee) : 0; + const rawAmount = filing.fee ? Number(filing.fee) : 0; + const amount = isNaN(rawAmount) ? 0 : rawAmount; + const rawPenalty = filing.latePenalty ? Number(filing.latePenalty) : null; + const latePenalty = rawPenalty !== null && !isNaN(rawPenalty) ? rawPenalty : null; try { const [existing] = await sql` @@ -930,7 +934,7 @@ export async function syncGovernanceCompliance(env: Env, sql: NeonQueryFunction< ${filing.dueDate}, ${filing.filingType === 'annual_report' ? 'yearly' : filing.filingType === 'tax_estimate' ? 'quarterly' : null}, ${filing.status === 'overdue' ? 'overdue' : 'pending'}, - ${filing.latePenalty ? 0 : null}, + ${latePenalty}, ${JSON.stringify({ filing_id: filing.filingId, jurisdiction: filing.jurisdiction, @@ -943,15 +947,27 @@ export async function syncGovernanceCompliance(env: Env, sql: NeonQueryFunction< } synced++; } catch (dbErr) { + failed++; console.error(`[governance] DB error for filing ${filing.filingId}:`, dbErr); } } + if (failed > 0) { + console.warn(`[governance] synced ${synced}, failed ${failed} of ${calendarResult.filings.length} filings`); + } + // Enqueue verification scrapes for active monitors try { const monitorsResult = await gov.getMonitors('active'); - if (monitorsResult?.monitors) { - const chittyId = await env.COMMAND_KV.get('default:chitty_id') || undefined; + if (!monitorsResult?.monitors) { + console.warn('[governance] Failed to fetch monitors from ChittyGov — skipping enqueue'); + } else { + let chittyId: string | undefined; + try { + chittyId = await env.COMMAND_KV.get('default:chitty_id') || undefined; + } catch (kvErr) { + console.error('[governance] KV read for chitty_id failed:', kvErr); + } for (const monitor of monitorsResult.monitors) { if (!monitor.scraperId) continue; @@ -980,7 +996,7 @@ export async function syncGovernanceCompliance(env: Env, sql: NeonQueryFunction< } } } catch (err) { - console.error('[governance] monitor enqueue failed:', err); + console.error('[governance] monitor fetch/enqueue failed:', err); } return synced; diff --git a/src/lib/integrations.ts b/src/lib/integrations.ts index 55d76de..2639037 100644 --- a/src/lib/integrations.ts +++ b/src/lib/integrations.ts @@ -1033,6 +1033,9 @@ export function govClient(env: Env) { 'Content-Type': 'application/json', 'X-Source-Service': 'chittycommand', }; + if (env.CHITTYGOV_TOKEN) { + headers['Authorization'] = `Bearer ${env.CHITTYGOV_TOKEN}`; + } return { getComplianceCalendar: async (params?: { status?: string; days?: number; entityId?: string }): Promise<{ filings: ComplianceFiling[]; total: number } | null> => { @@ -1043,7 +1046,11 @@ export function govClient(env: Env) { if (params?.entityId) qs.set('entity_id', params.entityId); const url = `${govUrl}/api/compliance/calendar${qs.toString() ? `?${qs}` : ''}`; const res = await fetch(url, { headers, signal: AbortSignal.timeout(10000) }); - if (!res.ok) return null; + if (!res.ok) { + const body = await res.text().catch(() => ''); + console.error(`[gov] getComplianceCalendar failed: ${res.status} — ${body.slice(0, 500)}`); + return null; + } return await res.json() as { filings: ComplianceFiling[]; total: number }; } catch (err) { console.error('[gov] getComplianceCalendar error:', err); @@ -1059,6 +1066,10 @@ export function govClient(env: Env) { body: JSON.stringify(data || {}), signal: AbortSignal.timeout(10000), }); + if (!res.ok) { + const body = await res.text().catch(() => ''); + console.error(`[gov] verifyFiling failed: ${res.status} — ${body.slice(0, 500)}`); + } return res.ok; } catch (err) { console.error('[gov] verifyFiling error:', err); @@ -1073,7 +1084,11 @@ export function govClient(env: Env) { headers, signal: AbortSignal.timeout(10000), }); - if (!res.ok) return null; + if (!res.ok) { + const body = await res.text().catch(() => ''); + console.error(`[gov] getMonitors failed: ${res.status} — ${body.slice(0, 500)}`); + return null; + } return await res.json() as { monitors: ComplianceMonitor[]; total: number }; } catch (err) { console.error('[gov] getMonitors error:', err); From 9d6c80f25435444f471dbd2caa6af8aeb6e9ecf8 Mon Sep 17 00:00:00 2001 From: chitcommit <208086304+chitcommit@users.noreply.github.com> Date: Mon, 6 Apr 2026 15:49:25 +0000 Subject: [PATCH 3/4] chore: add CHITTYGOV_URL to wrangler vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sets gov.chitty.cc service URL in [vars] alongside other service URLs. Token (CHITTYGOV_TOKEN) will be provisioned via ChittyConnect when ChittyGov adds auth — govClient already handles it conditionally. Co-Authored-By: Claude Opus 4.6 (1M context) --- wrangler.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/wrangler.toml b/wrangler.toml index 70e6ed1..50888c4 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -22,6 +22,7 @@ CHITTYSCRAPE_URL = "https://scrape.chitty.cc" CHITTYROUTER_URL = "https://router.chitty.cc" CHITTYAGENT_SCRAPE_URL = "https://chittyagent-scrape.ccorp.workers.dev" CHITTYEVIDENCE_URL = "https://evidence.chitty.cc" +CHITTYGOV_URL = "https://gov.chitty.cc" # Optional: chittyregister for beacon heartbeats CHITTYREGISTER_URL = "https://register.chitty.cc" # Optional: chittychat data API for MCP tools From 774a82ce108efda7c9c1c8075e222d3f890f1f6a Mon Sep 17 00:00:00 2001 From: chitcommit <208086304+chitcommit@users.noreply.github.com> Date: Mon, 6 Apr 2026 22:31:51 +0000 Subject: [PATCH 4/4] Revert "chore: add CHITTYGOV_URL to wrangler vars" This reverts commit 9d6c80f25435444f471dbd2caa6af8aeb6e9ecf8. --- wrangler.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/wrangler.toml b/wrangler.toml index 50888c4..70e6ed1 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -22,7 +22,6 @@ CHITTYSCRAPE_URL = "https://scrape.chitty.cc" CHITTYROUTER_URL = "https://router.chitty.cc" CHITTYAGENT_SCRAPE_URL = "https://chittyagent-scrape.ccorp.workers.dev" CHITTYEVIDENCE_URL = "https://evidence.chitty.cc" -CHITTYGOV_URL = "https://gov.chitty.cc" # Optional: chittyregister for beacon heartbeats CHITTYREGISTER_URL = "https://register.chitty.cc" # Optional: chittychat data API for MCP tools