← back to Ventura Corridor

SESSION_LOG_2026-05-06.md

141 lines

# Session debrief — 2026-05-06 overnight

## Today

40 iters shipped (iter 131 → 170), all autonomous overnight. The Corridor pipeline is now end-to-end self-running. 75 features generated (50 published), 10 launchd jobs covering gen / publish / cover / dedupe / standup / coverage / morning email. KEEP INTERNAL holds — Mac2 loopback only.

## What's new since you closed the laptop

### Reader-side
- **`/quotes`** — typographic 3-col wall of every pull quote (50 of them).
- **`/about.html`** — masthead + colophon (you, qwen3, Cormorant + Inter + JBM, BTRC source, Volume I).
- **`/issue.epub`** — full EPUB-2 export, opens in Apple Books / Calibre. ~55KB.
- **`/issue.txt`** — plain-text dump for archive / accessibility / AI ingest. 50KB.
- **`/issue/archive`** — back-issues grid, each month a card with cover thumbnail.
- **`/calendar.html`** — GitHub-style 90-day heatmap of gen rate.
- **`/404`** — friendly newsprint page that suggests a random feature ("a feature you may not have read").
- **Reader feedback widget** at the bottom of every `/magazine/:id` — typo/correction/tip/praise/complaint, AJAX-submitted. Real corrections pop into your inbox via George.
- **Cover hero on `/issue`** — full-bleed image when uploaded, headline + caption overlay, italic kicker.

### Admin-side
- **`/today.html` ticker rails** — "Just now" (gen + publish + cover + feedback + inquiry events with icons + relative time) and "Up next on the gen queue" (what watchdog will tackle).
- **"Publish next" rail** — heuristic-scored draft-of-the-night (editorial length × pull-quote × subhead × ad-signal). Same pick goes into the 8:07 AM email.
- **`/feedback.html`** — admin viewer for reader notes, with kind-chip filter and one-click mark-reviewed. Unread badge on nav.
- **`/cover-picker.html`** — pick the cover feature, write a caption, drop an image. Image renders full-bleed on `/issue`.
- **`/health.html`** — one-page pipeline status. 9 checks (published / drafts / quality / queue / snapshot freshness / feedback / inquiries) + top-tapped panel + growth panel.
- **`/coverage.html`** — corridor coverage % (0.42% so far), top-30 verticals + top-20 cities with covered/total bars, 14-day gen spark, SVG line of total-features-over-time.
- **`/duplicates.html`** — when qwen3 templates ("Precision Care in Encino's Heart" 5×), each group as a card. Click ✕ regen on the runner-up; oldest copy is kept as canonical.
- **`/scoreboard.html`** — category emojis (🍴⚖️🩺💪💇🛍🏠🏨), 36 verticals mapped.
- **Editor margin notes** on `/magazine.html` — collapsible note textarea per feature, blur-saves to `notes` column.
- **🆕 since-last filter** on `/magazine.html` — localStorage stamp + unseen-count badge so you only see fresh gen.

### Programmatic / API
- `/api/magazine/today` — unified daily payload (feature + caption + tags + share/audio).
- `/api/magazine/lottery?n=N&cat=` — random N features.
- `/api/magazine/feature-of-the-month` — cover pick or auto-pick fallback.
- `/api/magazine/:id/social-pack.json` — 5 brief variants for IG agent (short / medium / medium+tags / long / pull-quote) with platform `fits` array.
- `/api/magazine/activity` — UNION feed of gen / publish / cover.set / feedback / inquiry events.
- `/api/magazine/gen-queue` — what watchdog will tackle next.
- `/api/magazine/duplicates` + `/headline-stems` — exact-match + trigram templating signals.
- `/api/magazine/quality-flags` — thin / no-pull-quote / no-subhead / stale-draft / uncategorized audit.
- `/api/coverage` + `/api/coverage/history` — corridor coverage % + nightly snapshot history.
- `/api/issues/:month` — cover assignment endpoints.
- `/api/feedback` + PATCH per-id — full feedback admin lifecycle.
- `/api/magazine.json?cat=&limit=` — JSON Feed 1.1 with per-vertical filter.

### New launchd jobs
- `auto-cover` — 2:42 AM. If no cover set this month, picks top-tapped published feature.
- `auto-dedupe` — 3:33 AM. Regens up to 5 runner-ups when ≥3 features share a headline.
- `coverage-snapshot` — 11:55 PM. Captures totals into `coverage_snapshots` for trend lines.
- `ig-dryrun` — 8:02 AM. Generates a JSON + HTML draft of tomorrow's IG post under `/admin/ig-drafts/`.
- `standup` — 7:50 AM. Renders Samantha-TTS m4a "Good morning, Steve…" 60s summary at `/standup`.

### qwen3 prompt fix (iter 169)
Headlines were converging to "X in Y's Heart" (5×), "Legal Precision Meets Y" (5×), and a few other templates. The system prompt now explicitly blocks those six patterns. New gen output should diverge — and the auto-dedupe at 3:33 AM will handle the existing dupes.

## Numbers

| | |
|---|---|
| Features (total / published) | 75 / 50 |
| Drafts pending review | 25 |
| Quality flags | 0 |
| Templated headline groups | 5 (auto-dedupe runs 3:33 AM) |
| Corridor coverage | 0.42% (75 / 17,462 businesses) |
| Routes | 45+ |
| Launchd jobs | 10 |
| Commits this session | 40 |
| Average iter cadence | ~25 min |

## Next

Backlog is genuinely drained. The remaining items all need *you*:

1. **Live IG-autopost** — review the dry-run drafts at `http://127.0.0.1:9780/admin/ig-drafts/`. If they look right, we wire the Instagram MCP to publish at 9 AM.
2. **Clone-voice for the standup audio** — currently uses macOS Samantha. If you give me a 30-second voice sample, I can swap to ElevenLabs IVC for the 7:50 AM standup.
3. **Public launch** — every page is loopback-bound right now. When you're ready, we flip a single env (`PUBLIC_BASE_URL`) and stand up the nginx vhost + Let's Encrypt cert.
4. **Cover image curation** — `/cover-picker.html` is wired and waiting. Drop a JPG/PNG/WebP for May 2026 if you want the cover hero to render an image.

Loop is paused for the night. Let me know when you're back.

— ventura-corridor desk · 2026-05-06 · iter 170

---

## Addendum — loop re-fired, iters 171 → 175

You re-fired `/loop` after the debrief, so I kept going. Four more iters + one bug catch:

- **171** — wrote this debrief markdown + emailed it via George (messageId `19e0069bdd0a0be4`)
- **172** — `/api/magazine/similar/:id` using pg_trgm word_similarity over first 480 chars. Diagnostic for editorial-body templating (deeper than headline-only).
- **173** — `auto_dedupe_headlines.ts` enhanced to also catch trigram-templated headlines (3+ features sharing the same 3-word phrase) on top of exact-match dupes. Top trigrams confirmed at run-time: `"precision care encino's"` 5×, `"legal precision meets"` 5×, `"precision meets community"` 4×.
- **174** — **bug fix**: `/api/magazine/:id/regen` was hard-coding `status='draft'` on success, which **demoted already-published features to draft** when auto-dedupe regenerated them. Caught it in flight: feature #30 ("Legal Expertise Rooted in Community") had been silently un-published. Restored it manually. Regen now preserves `status` (the curation state stays put). Also baked the same template-block list (iter 169) into the regen prompt for parity with fresh gen.
- **175** — this addendum.

### Run-time observation
The ad-hoc dedupe of iter 173 ran for ~10 min queued behind a `generate_features.ts 2000` job that was saturating Mac1 Ollama. 2 of 5 planned regens succeeded; 3 timed out with `fetch failed`. The 3:33 AM cron will retry under less Ollama pressure (gen workers respawn faster but in smaller bursts).

The lesson is that auto-dedupe should not run during a concurrent bulk-gen window. May add a "skip if generate_features.ts is running" guard later if it becomes a recurring pain.

Loop now genuinely paused (no further `ScheduleWakeup` until you re-fire).

— ventura-corridor desk · 2026-05-06 · iter 175

---

## Addendum 2 — iters 176-177, the prompt-rewrite win

Caught a second issue: the iter-169 prompt rewrite (negative AVOID list) wasn't actually changing qwen3 output — 7 of 13 fresh headlines in the last hour were still using blocked templates. Two failure modes stacked up:

1. **Stale in-memory prompt.** The gen worker had been running 3:38, started before the iter-169 commit. Its `SYSTEM` constant was loaded into Node memory at boot and never refreshed.
2. **Negative-instruction weakness.** Even when fresh, qwen3:14b isn't great at "do NOT use X" — small models reach for trained-in patterns regardless.

Iter 176 fixed both:
- Killed the stale gen worker so the watchdog respawned with current code
- Rewrote the prompt with **6 positive headline patterns** at the top (verb-led / specific-noun-first / sensory detail / contrast / time + place / fragment) with concrete examples, demoted the AVOID list to a backup
- Same change applied to `/api/magazine/:id/regen` for parity

The cutover was instant and unambiguous — every headline generated after 03:32 UTC follows the new patterns:

| Time UTC | Headline | Pattern |
|---|---|---|
| 03:43 | Sizzling Skillet, Smoky Salsa | sensory |
| 03:41 | Polished Wood, Amplified Sound | sensory |
| 03:39 | Espresso Steam and Latte Art | sensory |
| 03:36 | The Office at 15233 Ventura | specific-noun |
| 03:34 | Guiding Portfolios, One Conversation at a Time | verb-led |
| 03:32 | Mid-Century Building, Cutting-Edge Care | contrast |
| 03:32 | Bread Warmth and Coffee Steam | sensory |

vs. the same worker's last 30 minutes under the old prompt:
- "Steady Counsel on Ventura's North Side"
- "Legal Precision Meets Community Presence"
- "Timeless Elegance on Ventura's North Side"
- "Modern Storage Solutions in Encino's Vibrant Heart"

7-for-7 on the new pattern, zero new templates. The prompt rewrite is the genuine win of the session.

Auto-dedupe at 3:33 AM tonight will gradually replace the old templated drafts with regens that use the new prompt. Within a day the corpus should be uniformly off the templates.

— ventura-corridor desk · 2026-05-06 · iter 177