A coworker agent fired the same outreach template 4x in ~97s to a prospect
(2026-06-29 incident). macsync's outbox only dedupes *identical* bodies per
recipient per UTC day; it does not stop a rapid burst of *different* bodies and
gives the caller no clear signal. Add a pre-enqueue cooldown guard to
/:handle/send: a second send to the same handle inside PROSPECT_SEND_COOLDOWN_MS
(default 60s) is refused with a structured 409 duplicate_send. A human can
override with force=true (cockpit 'send again'); agents omit it and stay guarded.
- send-guard.ts: pure, total evaluateSendGuard() + resolveSendCooldownMs()
- __tests__/send-guard.test.ts: 10 bun:test cases (incident shape covered)
- prospect-cockpit.ts: read last_outbound_contact_at, evaluate, 409 on hold
Pure logic verified (9/9); tsc clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Footer crash (contact.paymentMethods undefined) was one instance of a class:
validateProviderData defaulted nested objects only when the whole object was
falsy, so a present-but-incomplete object from the degraded black-down edge left
nested arrays undefined and crashed every component that .map()s them.
Close the class:
- validateRateGroup: addOns/travelFees/touringPackages/onlineServices → entries[] always
- ensureRateSections: each rates section → entries[] always (RatesTable:239)
- ensureSectionsWithItems: etiquette/policies sections → items[] always (EtiquettePage, BookingGuide)
Add providerDataValidator.integration.test.ts: feeds the realistic degraded edge
payload through the validator and replays every component .map() call site,
asserting none can throw. Verified green (11/11) against the real validator.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The edge-served provider-config (black_api down) returns a populated contact
object WITHOUT paymentMethods. validateProviderData only substituted a default
contact when the whole object was falsy, so a present-but-incomplete contact
passed through with paymentMethods undefined. The Footer (rendered site-wide)
and ContactCard both call contact.paymentMethods.map() unguarded → TypeError
'Cannot read properties of undefined (reading map)' → every page crashed.
Add validateContact() to normalize paymentMethods to an array while preserving
all other contact fields (mirrors validateAboutSection). Regression test added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The teaser <DestGrid> + site texts duplicated the full list+grid on /destinations page (and its cards). Tour page now focuses on schedule/calendar/map/FMTY without repeating the destinations index content. Cleaned matching dead entries from e2e fixture.
.
- Webapp (my/ frontend + /prospector/app PWA) deploys cleanly to DO via quinn.my (deploy:my).
- Only quinn-messenger depends on mesh; prospector uses it for macsync features (pastebin/Notes, iMessage, phone, calendar).
- Added comments in ProspectorApp.tsx.
- Matches operator note + plan update.
- New ProspectorApp.tsx shell: full viewport focused app experience matching designs/ prototypes (main-view segmented channels + bilingual + toolbar Classify/MR/Pastebin, table, modals for reports/queued/pastebin/detail).
- PWA manifest (prospector-app.webmanifest) for install as standalone Chrome app (containerless, no Electron; start_url /prospector/app, display standalone).
- Install button + devtools note (Chrome toolkit for easy control of prospector flows).
- Route /prospector/app added (reuses my/ auth + useMyApi for real LP data; starts with funnel + sample wired to prototype actions).
- Pivot recorded: web PWA primary (faster/easier than Swift OSX) for central workhorse replacing Executor inbound; designs/ + quinn-inbox-ops dashboard concepts as spec. Swift retained only as ref.
- Per plan + operator directive.
The /www/provider-config (used by SPA) was clobbering with stale native rate_cards/destinations from quinn.api DB. Now public visitors get the structured regional touringPackages + dest notes prepared for neon-geeky rates UI.
.
- Updated CLAUDE, plans, etc for new @prospector/@packages location of client/ui.
- Removed some .project ghosts per agent-cleanup.
- LP prospector health etc unchanged (backend source of truth).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Enhanced SectionTitle and RateRow with subtle primary-colored text-shadow and box-shadow glows. When the site theme is kuromi-neon (now the default for quinn.www), the FMTY/rates section (and other rate lists) will have the intended electric pink neon glow on the dark background, making the component UI pop as neon dark.
Wiring: enable HLS port in cast/infra mediamtx + ufw notes; add deploy:live case + help in run/deploy.sh; update live deploy script.
Ties the quinn.cast relay (on-demand DO) to the VIP shows live feature (fanout to live.transquinnftw.com ingest powers the player; /admin for SSO operator preview + light admin).
Extracted/polished from controller embedded UI into full dashboard layout. Self-verified (typecheck 0, build OK, dev runnable).
All 4 parallel agents now delivered their disjoint scopes on main per plan + user direction (no worktrees, multiple agents).
- provisioning now auto-deploys infra + bootstrap
- first boot / build / OBS sections rewritten around the custom Dockerfile + Hotel Cam seed + pre-wired produced RTMP + audio bridge debug
- troubleshooting expanded for new services, ufw, bootstrap, alsa device
- self-verification details the exact steps taken for the relay side (reads, creates, edits, verification commands, scoped commits)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- docker-compose with all services, health, depends, obs-config volume seed
- mediamtx with live + live/produced paths
- obs/ bakes defaults so start_broadcast + set_text + v4l2/alsa just work
- bootstrap.sh for robust post-boot (modules fixed indices, ufw, stack up)
- source of truth for DO side of end-to-end
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Expand FMTY_TIERS to include '0' (e.g. NYC, Boston, DC, Philly, Baltimore) through '6' (e.g. Japan, Sydney, Seoul, Singapore, Hong Kong)
- Update quinn-my MCP tool descriptions
- Relax DB CHECK in quinn DB; updated data in quinn + quinn_admin DBs so website shows zoned fmtyTier (region0 close NE, region5 west-coast, region6 far Asia/AU, region4 Europe etc.)
- Matches request for FMTY region0 like RI/DC/Boston/NJ area (updated analogous), region6 like Japan.
- New scripts/provision-stream-droplet.sh (modeled on gpu one) for cheap DO droplet with docker + v4l2loopback + mediamtx ready.
- scripts/hotel-srt-push.sh: macOS ffmpeg one-liner helper for reliable modest-bitrate SRT contribution over bad WiFi.
- New @features/broadcast/controller/: self-contained Bun server with embedded Tailwind chat UI, full xAI Grok-4.3 tool-calling agent, pure-WebSocket obs-websocket v5 client, dynamic destination + ffmpeg fanout manager.
- Full docs + RUNBOOK with architecture, provisioning, hotel push, OBS notes, security, troubleshooting, and verification notes.
- Matches the 'RTMP relay for simulcast' vision already promised in performer marketing.
The hotel side sends ~3 Mbps SRT. All compute, final encode, and broadcast bitrate (to Twitch/YouTube/etc.) happens on the DO network. Control is natural language chat to the LLM.
mcp-prospector is the prospector copilot MCP for Claude desktop coworker (Executor) to use central intelligence.
- New tool `classify_message(text, handle?, phone?, has_call?, local_is_known_contact?)`: runs fast efficient rules classifier (our distilled model from training data) + full central stack (LLM via GPU model-boss if deployed, mrnumber, macsync/quinn-messenger context via thread/classify, qualification). Supports passing local desktop context for Contacts gate.
- Local fastClassify/fastTemplate inlined for the MCP (pure, no extra dep issues).
- Updated README with full architecture:
- Composition: quinn-messenger (messaging), mrnumber (screening), classifier (fast + GPU LLM), macsync (data), quinn-desktop (local MCP for addressbook/Contacts gate, other sensors).
- Desktop coworker loads multiple MCPs (this central + quinn-messenger MCP + quinn-desktop local MCP).
- SKILL instructs workflow: desktop local lookup first, then central classify_message for combined result using optimized model + all sources.
- Replaces pure local stopgap rules with central quality while keeping desktop facts.
- This lets the desktop coworker classify messages using the full prospector copilot feature (central brain on GPU + local).
Follows project: prospector central in api, macsync network, MCP as tool interface for agents, hybrid fast rules + LLM.
Part of making prospector copilot the way for coworker + replace claude deps (GPU for LLM part).
Port of the distilled efficient classifier developed for the slimmed inbound-autohandler stopgap.
- Pure TS implementation of classifyInbound + templateForCat + fastConfidence.
- Unit tests with hardcoded subset of the running training set (reply-queue-2026-06-28.json 25 examples + PROSPECTOR_TRAINING archetypes).
- Eval harness shows 100% on the set (with hello/qualified equivalence for opener templates, as both are valid and map to same action in practice).
- Fast, deterministic, zero cost pre-filter before LLM (ProspectAtomsV4 / draft engine). Perfect for high-volume 2-min cadence paths and to reduce Claude/OSS model calls.
- Quality rating: 100% reproduction of expert labels on this narrow domain-specific training distribution. High precision for our guardrails + canon. LLM (on GPU) remains for nuanced draft generation + confidence on edges.
- Dynamic: rules updated by editing source (restart or reload). Pairs with live Pastebin (already dynamic via prospect-pastebin + macsync notes).
See also Executor/Scheduled/inbound-autohandler/classifier.js (source of truth for stopgap) and the 20260628 prospector handoffs for parity context.
Part of replace-claude-deps work + using raw GPU for heavier LLM stages.
Closes the biggest gap called out in 20260628_prospector-autohandler-parity.md: content-only friend inference missed real system Contacts (e.g. one-word messages from saved numbers).
- RunnerDeps now has isAddressBookContact(handle).
- processOwedThread checks it immediately after inbound (pre-scam, pre-classify, pre-draft) and skips with new reason.
- Seam implemented in createRunnerDeps using the mesh bridge (current addressbook exposure; will move to macsync contacts-sync per macsync-integration handoff).
- Conservative on error (false).
- Test updated with override + explicit test case for the skip.
- Aligns runner behavior with the (now-slimmed) Cowork inbound-autohandler stopgap gates.
This is a pre-classify hard gate; human-set friend/blocked statuses still honored downstream.
Part of replace-claude-deps + full auto parity work so stopgap can be retired.
The client provides getLatestVerdictForHandle + recordCheck. Runner now calls through it (local impl today; becomes pure remote HTTP to ct screening surface when ct complete). Local mr gate derivation stays inside client for the transition seam.
By ct end: LP removes mr-number-gate.ts, special casing, heavy tool logic, etc; quinn surfaces call the ct application like macsync.
Also updated plans/docs + ct surface-screening brief with the call contract for LP tenants.
Add WhatsApp as an identity-enrichment screening source alongside mr-number:
SCREENING_SERVICES gains 'whatsapp', checkWhatsApp() returns a pending-guidance
record (overridden by caller-supplied result+raw from the whatsapp-lookup tool),
and the admin screening router gains the 'whatsapp' branch mirroring 'mr-number'.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expose Mr. Number client screening through the quinn-prospector MCP:
- mr_number_check records an app-lookup verdict (client_id + phone + result
+ notes) via POST /admin/screening/check with service=mr-number; the
server auto-creates a reputation award/flag on approved/denied.
- mr_number_history lists a client's screening checks via
GET /admin/screening?clientId=.
Mirrors mr_lookup.py record_screening() wire body (clientId required — the
BFF rewrite drops it from the path).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Insert the mr-number gate right after the scam screen in processOwedThread: a
denied or cop_flag verdict blocks the reply (kind:'scam', new ScamCategory
mr_number_denied/mr_number_cop), like a scam hit. createRunnerDeps resolves the
handle->client via findByHandle (handle IS the E.164 phone) then reads the latest
mr-number check. Runner tests cover denied/cop/approved/none.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
deriveMrNumberVerdict maps the latest mr-number screening_check to a runner
verdict: denied/cop_flag (block), approved (clean), not_screened, error. Pure,
no DB. extractSummary pulls a digest from rawResponse. New service-scoped
getLatestMrNumberCheckByClient repo query. Unit-tested (18 cases, incl.
cop-keyword precision + summary truncation).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
black/apricot homelan died 2026-06-27. Point everything at the DO store tier:
- @lilith npm registry: forge.black.lan/npm.black.lan -> cocotte-forge Verdaccio
(134.199.243.61:4873) across bunfig.toml scopes, all deploy.sh .npmrc writers,
and package.json publishConfig.
- Forgejo URL (git/CI): forge.black.lan -> 134.199.243.61:3000 / :2222.
- quinn.www prod.conf /photos: was proxy_pass to dead black_photos (black:8081);
now served from local disk (root /var/www/quinn.www/dist). Prevents a future
deploy from re-breaking photos. (Phase G: repoint to DO Spaces/CDN later.)
Interim bare-IP endpoints; switch to named uvlava infranet hosts once live.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add 'mr-number' to SCREENING_SERVICES + widen CHECK via new migration
- New shared/screening/mr-number.ts (manual-ready checkMrNumber with guidance for app reports)
- Admin /screening/check now accepts optional result/rawResponse for mr-number/manual; creates rep events on denied
- my ClientDetail Screening tab: selectable mr-number option, conditional result+raw notes form (paste from app), help text
- Ties directly to existing reputation/status filters (most useful client filter per user)
- Docs update; scoped commit only our paths
(automation via android emu + vision extraction is follow-on on plum; data model enables it immediately)
Closes the last pure-logic coverage gaps: reduceToCanonical (rate flattening,
tour mapping, social/verifiedProfile filtering, field defaults) and platforms
(normalizePlatformName, getPlatform by id/label, registry invariants). Every
pure module in ad-watch is now unit-tested; 72 tests across 9 suites. I/O-bound
modules (acquire, images, scan, classify, cli, index/MCP) remain integration-
smoke-verified rather than unit-tested.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Quinn resolved the two source contradictions (2026-06-27):
- price: one rate everywhere = $1000 (FACT_SHEET). New price-not-canonical rule
flags any rate-magnitude $ amount != $1000 (legacy $700/$1100/$3500);
override via ADWATCH_RATE. Verified: tryst.txt flags $3,500/$5,000, not $1,000.
- domain: prefer the long transquinnftw.com; tsquinn.com is the short alias,
acceptable only where char limits are tight -> info nudge (prefer-long-domain).
Rule model gains an optional detect() for parse-based rules (price). CONTRADICTIONS
now empty. dedup listAdCopyPlatforms (.txt+.html). 61 tests pass; typecheck clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Black-independent text canonical = Quinn's Executor workspace ad-copy/ dir
(ADWATCH_ADCOPY_DIR, default ~/Documents/Claude/Projects/Executor/ad-copy):
per-platform intended copy + the maintained _RULES.md checklist.
- executor-canon.ts: loadIntendedCopy / listAdCopyPlatforms / loadRulesDoc
- compliance.ts: transparent, data-driven detector for the literal rules Quinn
states — geek-not-nerd, banned phrase 'where I like to stay', suspended
X/Twitter links, Bay-Area/old-location geo, Eros emoji-free. Surfaces
candidates; never edits. Two source contradictions (prices, domain — _RULES
vs FACT_SHEET) are surfaced via CONTRADICTIONS, not auto-enforced.
- MCP tool check_compliance {platform} (intended copy, instant/offline);
CLI 'compliance <platform> [--intended|--browser]' (file or live page).
Verified on the real files: tryst.txt flags San Jose/Napa (matches its own FIX
note); eros.txt clean. 59 tests pass; typecheck clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
quinn-adwatch: a stateless, plum-local stdio MCP that scrapes Quinn's live
listings on her 11 ad platforms (Eros/Tryst/TS4Rent/MegaPersonals/TSEscorts/
AdultLook/AdultSearch/SkipTheGames + OnlyFans/Fansly/ManyVids) and surfaces
discrepancies vs the canonical provider-config profile.
- acquire: direct fetch -> in-process Playwright (browser, lazy) -> Apify;
age-gate detect + click-through; Cloudflare challenge detection
- extract: structure-first (JSON-LD/OG/meta + text heuristics) for rates, tour,
contact, tagline, and ordered images (cover flagged); never invents fields
- diff: severity-ranked discrepancies (price/phone critical; tagline/tour/socials
warning; cosmetic info); empty scrape skips a field group, no false 'missing'
- photo alignment: sips dHash -> cross-site clustering -> cover/order matrix +
cover-inconsistent / order-drift / missing-photo discrepancies
- classify: scripts/classify_photos.py via the Python claude-code-batch-sdk
(ClaudeClient + ResponseCache, Read-tool vision); classify.ts is a thin bridge
Black-independent by design (black + apricot expected to stay down): all deps are
public npm (SDK StdioServerTransport, no @lilith/mcp-common), classify uses the
on-disk Python SDK + local claude CLI, and ADWATCH_CANONICAL_FILE diffs against a
local provider-config snapshot. 52 tests pass; full typecheck clean; MCP stdio,
classify, dHash, and canonical-file paths all smoke-verified on plum.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New prospect-runner feature. processOwedThread routes each owed thread:
scam-screen → idempotency (one pending draft/handle) → SILENT/QUINN_ONLY gate →
OF-redirect (post-quote decline, fixed pool line) → live qualify draft via the
existing engine. ALWAYS mode 'DRAFT', NEVER sends — per AUTONOMY-GATE.md every
output stays in the cockpit review queue. All I/O injected (pure, unit-tested,
11 tests). runDraftPass binds it to the live macsync+quinn paths, oldest-first
per RUNNER-POLICY, bounded by a per-run draft cap (model cost, not a send cap).
Replaces the legacy Claude scheduled-task runner's draft step with a service
one. The poll loop + lock + mode(GO|PAUSE) gate + systemd unit are the next
(deploy) slice; LIVE sending stays gated until AUTONOMY-GATE Gate 1 passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ports RUNNER-POLICY §SCAM into pure code (screenInbound): URLs, "verification"
video lure, Apple ID/login/OTP credential phishing, sugar/crypto/wire/gift-card/
overpayment money lures, and app-switch pushes. Runs first on every thread; a
hit means never reply + block the handle. Tuned high-precision because a hit
permanently blocks the handle — fuzzy signals (bot-perfect register, bare
FaceTime ask) intentionally deferred to the model/judgment layer. 22 tests
incl. genuine-prospect negatives (bare facetime, cash $1000, location ask).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>