{"slug":"crazy-news-channel","total":122,"limit":100,"offset":0,"since":null,"commits":[{"hash":"774ae8f","date":"2026-09-26 16:48:45 -0700","author":"Steve Abrams","subject":"P24 Daily Edition: all of today's news + top 6 with Shadow Man editorial-ink cartoons (TK-12354)","body":"Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01SC2DKkJkSNenmFz2Eqnvpk"},{"hash":"7868d09","date":"2026-09-26 03:43:56 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-26T03:43:38 (5 data files) — cartoons/queue/2026-09-26.json cartoons/queue/20260926-server-room-thermostat-war.meta.json cartoons/shorts-ledger.json yolo/cartoon-batch-log.jsonl yolo/cartoon-shorts-log.jsonl","body":""},{"hash":"7eee427","date":"2026-09-26 03:19:52 -0700","author":"Steve Abrams","subject":"P24 daily cartoons 2026-09-26: 6 built + registered","body":""},{"hash":"b33a8cf","date":"2026-09-26 03:13:31 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-26T03:13:13 (3 data files) — cartoons/queue/20260926-bleacher-superstition-tower.meta.json cartoons/queue/20260926-festival-mascot-election.meta.json cartoons/queue/20260926-halftime-show-runaway.meta.json","body":""},{"hash":"688c4d1","date":"2026-09-26 02:43:06 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-26T02:42:47 (3 data files) — cartoons/queue/2026-09-26.json cartoons/queue/20260926-rainbow-permit-office.meta.json cartoons/queue/20260926-statue-selfie-line.meta.json","body":""},{"hash":"506aa9e","date":"2026-09-25 12:04:47 -0700","author":"Steve Abrams","subject":"index.html: correct stale comment — real-news stories can now carry a linked cartoon (TK-12247)","body":"Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_0174h5jsd9QqR6SbjXgvooua"},{"hash":"b098bf8","date":"2026-09-25 12:03:14 -0700","author":"Steve Abrams","subject":"snapshot before shadowman-articles merge: Steve's review-queue approval of senator-s-shadow-war (18:19Z, review-actions.jsonl)","body":"Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_0174h5jsd9QqR6SbjXgvooua"},{"hash":"b0563b7","date":"2026-09-25 12:03:14 -0700","author":"Steve Abrams","subject":"p24: attach 2 Shadow Man cartoons to their articles — Money Tank → Pakistan airstrikes, Party Arm-Wrestle → Cook Senate shifts (TK-12247)","body":"Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_0174h5jsd9QqR6SbjXgvooua"},{"hash":"afee2fe","date":"2026-09-25 11:43:10 -0700","author":"Steve","subject":"cartoon pages: share one page template between approve.py and the Shadow Man export (TK-12247)","body":"approve.py now renders cartoons/<date>-<slug>.html from daily-cartoons/cartoon-page.template.html;\noutput verified byte-identical to the previous inline f-string on a scratch copy.\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XizSgLiFuNbjY418z94Ewq"},{"hash":"95c7f77","date":"2026-09-25 11:24:21 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-25T11:23:40 (3 data files) — .claude/worktrees/agent-a08d7d38661546685 .claude/worktrees/agent-aa0bd89b9b6be4947 daily-cartoons/queue/2026-09-25/senator-s-shadow-war/meta.json","body":""},{"hash":"432e63e","date":"2026-09-25 11:16:19 -0700","author":"Steve","subject":"p24 review queue: article picker for cards with no story_id (TK-12243)","body":"Pre-article-pipeline queue items have no story_id, so Approve failed with\napprove.py's \"--story-id required\". Live cards without one now show a\n\"Link an article\" picker above Approve/Delete: up to 3 keyword-overlap\nsuggestions ($0, no LLM) + typeahead search over every extract_stories.mjs\narticle, grouped Real news / P24. Approve sends {date,slug,story_id};\nbulk Approve N refuses inline and lists the cards missing an article.\n\nServer: GET /api/stories; /api/approve accepts optional story_id, validates\nit against the extracted stories (unknown/malformed -> per-item error),\npersists story_id/title/url/source into meta.json before running\napprove.py --story-id. extract_stories.mjs honours P24_SITE_DIR.\nCSS: scoped rq-picker rules; overflow-x:clip on the queue page so the\nsticky bulk bar actually pins to the viewport. Static review.html markup\nunchanged (picker is live-only).\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XizSgLiFuNbjY418z94Ewq"},{"hash":"43607e2","date":"2026-09-25 11:12:29 -0700","author":"Steve","subject":"p24: publish 13 approved Shadow Man cartoons to the Cartoon Desk gallery (TK-12241)","body":"Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>"},{"hash":"c3b7dd0","date":"2026-09-25 11:07:45 -0700","author":"Steve","subject":"TK-12241: Shadow Man gallery page on the Cartoon Desk (approved pieces only)","body":"cartoons/shadow-man.html + shadow-man.js, linked from cartoons/index.html. Data comes from\ncartoons/shadow-man-data.js, written by the Inkwell export script, which exports ONLY pieces\napproved in the Inkwell curation UI (currently 0 -> page shows an empty state). Theme/era filters,\nsort, density slider (persisted), date+time chips, <dialog> viewer. Styling added to\nassets/style.css as PART 4 scoped under body.shadow-man-page; no inline styles. Not added to\nmanifest.js, the daily queue, or the Shorts pipeline.\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>"},{"hash":"8691d10","date":"2026-09-25 10:54:09 -0700","author":"Steve","subject":"p24 daily-cartoons: review-server with multi-select Approve/Delete (TK-12243)","body":"- daily-cartoons/review-server.mjs: zero-dep node:http server, 127.0.0.1:9947 (walks up if\n  taken; PORT env fixes it), Basic Auth. Renders the review page live, serves style.css + queue\n  media, POST /api/approve (approve.py via execFile, sequential), /api/delete (move to\n  queue/_trash, never rm), /api/restore; strict date/slug validation (traversal -> 400);\n  JSON-only + same-origin POSTs; every action appended to logs/review-actions.jsonl.\n- build_review.py: one template for static + live. Per-card Select checkbox and big full-width\n  Approve (green) / Delete (red) buttons below each card; sticky bulk bar (Approve N / Delete N /\n  Select all / Clear); in-page delete-confirm modal; Undo toast; Approved-check state, no double\n  approve. Static review.html renders controls disabled with \"open via review-server to act\".\n- common.py: P24_SITE_DIR now also relocates queue/review.html (tests never touch the real queue);\n  queue_dates() skips queue/_trash (generate.py uses it).\n- assets/style.css: scoped rq-* classes under body.cartoon-queue-page (one-stylesheet rule).\n- Verified with Playwright against a temp COPY: 46/46 checks, 0 console errors.\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XizSgLiFuNbjY418z94Ewq"},{"hash":"a357aa3","date":"2026-09-25 10:53:29 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-25T10:52:57 (1 data files) — .claude/worktrees/agent-a08d7d38661546685","body":""},{"hash":"9c5d41b","date":"2026-09-25 10:22:49 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-25T10:22:25 (2 data files) — .claude/worktrees/agent-a1b66befdca443faf .claude/worktrees/eyes-top-left","body":""},{"hash":"8ef5d98","date":"2026-09-25 10:20:35 -0700","author":"Steve Abrams","subject":"p24 daily-cartoons: docs for article-driven, still-image-by-default pipeline","body":"README.md rewritten for TK-12237: article selection rules (14-day queue + full\nmanifest.js exclusion, real-news-first-then-p24-fallback), the new $0.003/cartoon\nstill-image cost line (was $0.103 with video), --concepts-only and --video usage,\nand a note that the older multi-panel interactive-strip cartoons already in\ncartoons/manifest.js use a different DOM than scripts/cartoon-shorts/make-cartoon-shorts.mjs\nexpects (pre-existing gap, unrelated to this change). run-daily.sh comment updated\nto flag that --video must not be added there without an explicit Steve ask.\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XizSgLiFuNbjY418z94Ewq"},{"hash":"fb5ea85","date":"2026-09-25 10:20:28 -0700","author":"Steve Abrams","subject":"p24 daily-cartoons: review queue shows linked article, renders cleanly without clips","body":"build_review.py's card now shows the linked source article (title + real-news/p24\nsource chip), or a plain \"no linked article\" note for pre-TK-12237 queue items that\npredate story linking. The approve-command snippet shown per card now includes the\nrecorded --story-id so Steve can copy/paste it directly. review.html regenerated;\nverified 0 console/page errors via Playwright and 0 <style> blocks / style=\"\" attrs\n(only ../assets/style.css, per the one-stylesheet rule).\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XizSgLiFuNbjY418z94Ewq"},{"hash":"0b40f82","date":"2026-09-25 10:20:22 -0700","author":"Steve Abrams","subject":"p24 daily-cartoons: approve.py defaults --story-id from meta.json, links source article","body":"--story-id is no longer required on the command line — it defaults to the article\ngenerate.py recorded in meta.json (story_id) and is still validated against\nstories-data.js/real-news-data.js; pass --story-id to override. The generated\ncartoon page now also shows a \"Source: <headline>\" link to the linked article when\npresent. Works unchanged with or without clip.mp4 (poster-only renders as a still\n<img>, no style violations — only ../assets/style.css is linked).\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XizSgLiFuNbjY418z94Ewq"},{"hash":"b8bc8d0","date":"2026-09-25 10:20:16 -0700","author":"Steve Abrams","subject":"p24 daily-cartoons: generate cartoons from real articles, still image by default (TK-12237)","body":"generate.py now reads today's real-news-data.js (falling back to stories-data.js's\ninvented P24 stories) via a sandboxed extract_stories.mjs parser, picks one unused\narticle per cartoon (skipping story_ids cartooned in the last 14 days of the queue\nor already in cartoons/manifest.js, preferring newest real news), and asks local\nOllama for exactly one gag per article instead of inventing abstract concepts with\nno article input. Video generation (Replicate wan-2.2 i2v) is now opt-in via\n--video (default OFF, ~$0.003/cartoon instead of ~$0.103) per Steve: \"just an\nimage, not moving.\" Added --concepts-only/--dry-run for a $0 preview of picked\narticles + concepts with no Replicate calls.\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XizSgLiFuNbjY418z94Ewq"},{"hash":"dee75b4","date":"2026-09-25 09:52:09 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-25T09:51:23 (1 data files) — .claude/worktrees/agent-adeaa4f0116149a98","body":""},{"hash":"8d0d7a9","date":"2026-09-25 09:50:51 -0700","author":"Steve","subject":"p24: cover the fake artist signature the image model painted into 5 daily-queue posters (undo: git revert)","body":"Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>"},{"hash":"391456a","date":"2026-09-25 09:44:15 -0700","author":"Steve","subject":"snapshot before merging one-stylesheet + name-scrub branch","body":"Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>"},{"hash":"5743111","date":"2026-09-25 09:44:04 -0700","author":"Steve","subject":"p24: drop stale manifest entry for deleted filibuster-marathon page","body":"Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>"},{"hash":"04b770b","date":"2026-09-25 09:43:13 -0700","author":"Claude Opus 5.5","subject":"p24: scrub every mention of Paul Conrad / Conrad per Steve's standing rule","body":"Removes the name from all user-visible text and from the daily-cartoon\ngenerator prompts/templates so it can't come back in future output:\n\n- cartoons/reference/paul-conrad.js -> editorial-ink-reference.js: rewrites\n  the \"Masters\" bio+works reference into a generic \"Classic Editorial-Ink\n  Tradition\" style reference (no named individual, no catalogued/cited real\n  works) and updates cartoons/reference/index.html to match.\n- cartoons/20260924-conrad-filibuster-ink.html -> 20260924-filibuster-ink.html\n  and 20260924-conrad-filibuster-qwen25-7b.html -> 20260924-filibuster-qwen25-7b.html,\n  images/filibuster-marathon-conrad.jpg -> filibuster-marathon-ink.jpg, with\n  every reference updated (manifest.js ids/files/thumb/tags/style_reference,\n  index.html featured-cartoon links, cartoons/index.html, assets/style.css\n  comments, cta/cta3-video-results.json local test-evidence paths).\n- 3 other cartoon pages (come-in-and-get-me-meter, row-by-row-marshal,\n  gold-tier-treadmill) + their manifest.js entries: \"in the manner of Paul\n  Conrad\" -> \"in classic editorial-ink style\"; style_reference/tag values\n  paul-conrad / \"paul conrad style\" -> editorial-ink / \"classic editorial ink\".\n- daily-cartoons/generate.py STYLE_PRE (the actual generator prompt) and\n  approve.py's approved-page caption + style_reference/tags rephrased so no\n  future daily cartoon carries the name; daily-cartoons/README.md updated;\n  the 5 queued (not-yet-approved) meta.json image_prompt fields scrubbed and\n  daily-cartoons/review.html rebuilt from them via build_review.py.\n\nVerified: recursive case-insensitive grep for \"conrad\" across the whole\nworktree (incl. yolo/) returns zero hits; the one-stylesheet invariant\n(no <style>/style=\" in any tracked html) still holds; Playwright crawl of\nindex.html + cartoons/index.html + cartoons/reference/index.html + all 23\ncartoon pages returns HTTP 200, 0 console errors, 0 failed network requests,\nand no \"conrad\" in rendered text or HTML source on every page reachable from\nthe current manifest (one unrelated pre-existing 404 found and left alone:\nmanifest.js's \"filibuster-marathon\" entry points at a file removed by an\nearlier, unrelated commit — flagged for a separate fix, not in scope here).\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>"},{"hash":"010df2d","date":"2026-09-25 09:21:14 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-25T09:20:47 (1 data files) — .claude/worktrees/agent-adeaa4f0116149a98","body":""},{"hash":"3619366","date":"2026-09-25 09:16:24 -0700","author":"Steve","subject":"p24: fix cartoon-page generators to emit zero inline styles going forward","body":"daily-cartoons/approve.py (writes a new cartoons/<date>-<slug>.html for every\nnewly-approved cartoon) and daily-cartoons/build_review.py (writes the local\nadmin review queue) each emitted their own <style> block with a hardcoded\npalette (--cream/--ink/--mint, or --bg/--ink/--mint/--card/--dim + a\nprefers-color-scheme dark swap) and a Georgia font, on top of already\nlinking ../assets/style.css. Fixed both generators to link only the shared\nstylesheet and use new body classes (cartoon-page-simple, cartoon-queue-page)\nwhose rules now live in style.css, so every page these scripts produce from\nhere on stays on the one site-wide palette/font. Regenerated review.html\n(local-only queue, 5 items) to verify.\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>"},{"hash":"f3c1a2d","date":"2026-09-25 09:16:16 -0700","author":"Steve","subject":"p24: move all 22 cartoon-strip pages + the reference page onto the shared stylesheet","body":"Each of the 22 cartoons/2026*.html pages (plus cartoons/reference/index.html)\ncarried its own <style> block — 10 distinct copy-pasted/drifting variants —\non top of already linking ../assets/style.css, plus a per-page \"Dark mode\"\ntheme-toggle button+script on 17 of them. Some variants set a serif font\n(Georgia/'Times New Roman'/'Iowan Old Style'/Palatino) that didn't match the\nsite's sans-serif stack, so which page you clicked into changed both the\nfont and (via the toggle, or the mood-swap removed in the prior commit) the\ncolors.\n\nDeleted every inline <style> block and moved its rules into assets/style.css\nunder body classes scoped per layout family (cartoon-page[-wide], -frame,\n-diagram, -editorial, -reference) so none of it can leak onto index.html or\ncartoons/index.html. Removed the per-page theme toggles (button + JS) along\nwith the mismatched serif fonts. Functionality (panels, reveal/twist\nbuttons, sliders, status messages, the one true 3-state auto/dark/light\ntoggle on budget-pie-fight) is unchanged — only the styling mechanism moved.\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>"},{"hash":"455c6ee","date":"2026-09-25 09:06:05 -0700","author":"Steve","subject":"p24: move index.html's 9 inline style=\"...\" attrs into style.css classes","body":"cartoon-desk-link and the featured-editorial-cartoon block (panel/link/\nvideo/title/caption) carried inline styles, including a Georgia/'Times New\nRoman' font-family on the title+caption that didn't match the site's one\nsans-serif font stack. Replaced each with a class (.cartoon-desk-link,\n.featured-cartoon, .featured-cartoon-panel/-link/-eyebrow/-video/-title/\n-caption) defined once in style.css, and dropped the mismatched serif so\nthe whole page uses one font system. index.html now has zero style=\"...\"\nattributes.\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>"},{"hash":"d12e5a2","date":"2026-09-25 09:05:26 -0700","author":"Steve","subject":"p24: remove per-category mood palette swap and per-strip dark toggle from style.css","body":"Clicking a category filter set body[data-mood=\"politics|weather|...\"], and a\nwhole block redefined --bg/--panel/--accent/--text/etc per category, so every\nclick repainted the entire site's colors. Same problem for the per-strip\n\"Dark mode\" toggle, which swapped --bg/--panel/--text via html[data-theme].\nRemoved both blocks; kept only the single default (\"all\") mint-green palette\nin :root. JS still sets body[data-mood] on click (for the mood-readout text\nand the independent per-card --cat-accent dots) and pages still carry\ndata-theme, but nothing in style.css keys colors/fonts off either anymore.\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>"},{"hash":"e22509c","date":"2026-09-25 07:43:01 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-25T07:42:43 (4 data files) — daily-cartoons/queue/2026-09-25/bureaucrats-and-bubbles/meta.json daily-cartoons/queue/2026-09-25/infinite-scroll/meta.json daily-cartoons/queue/2026-09-25/senator-s-shadow-war/meta.json daily-cartoons/queue/2026-09-25/tech-tyrant/meta.json","body":""},{"hash":"688391d","date":"2026-09-25 04:08:01 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-25T04:07:37 (2 data files) — cartoons/queue/2026-09-25.json yolo/cartoon-batch-log.jsonl","body":""},{"hash":"523729e","date":"2026-09-25 02:36:35 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-25T02:36:15 (1 data files) — cartoons/queue/2026-09-25.json","body":""},{"hash":"9c016cc","date":"2026-09-24 15:55:11 -0700","author":"Steve Abrams","subject":"cartoon shorts: first 5 public Satire Shorts posted to All News Daily (TK-12191)","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"},{"hash":"e0ca704","date":"2026-09-24 15:54:02 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-24T15:53:39 (3 data files) — cartoons/shorts-ledger.json yolo/cartoon-shorts-log.jsonl yolo/tk12191-shorts-hook.patch","body":""},{"hash":"9abf345","date":"2026-09-24 15:52:49 -0700","author":"Steve Abrams","subject":"daily cartoon batch: post new cartoons as public Satire Shorts, max 5/day (TK-12191)","body":""},{"hash":"89ae98f","date":"2026-09-24 15:48:27 -0700","author":"Steve Abrams","subject":"cartoon shorts: transparent SATIRE badge (no white box), kicker below badge, caption tucked under image (TK-12191)","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"},{"hash":"ce98d61","date":"2026-09-24 15:43:02 -0700","author":"Steve Abrams","subject":"Add make-cartoon-shorts.mjs: turn each P24 cartoon into a vertical YouTube Short","body":"Playwright captures the 3-panel strip + Plot Twist state live from the cartoon\nHTML; ImageMagick composites branded 1080x1920 cards (opening SATIRE card,\nper-panel cards, plot-twist card, article card for the linked real story);\nffmpeg Ken-Burns + concat + mux produces out.mp4. Reuses the EXISTING\nallnewsdaily pipeline modules (tts-elevenlabs.mjs, upload-youtube.mjs,\nfit-caption.js) rather than copying them. Idempotent via\ncartoons/shorts-ledger.json; every run logs PASS/FAIL/SKIP rows to\nyolo/cartoon-shorts-log.jsonl and writes a fleet-health-rollup heartbeat to\n~/.claude/skills/p24-cartoon-shorts/data/latest.json.\n\nA cartoon whose story_id doesn't resolve to a real stories-data.js /\nreal-news-data.js entry is skipped (FAIL row) and never rendered/uploaded.\n\nTested (--no-upload): commission-turtles with VO ($0.087, 290 chars) and\nbudget-pie-fight silent, both 1080x1920 / 34.2s / <60s. Negative test\n(unresolvable story_id) confirmed FAIL row + no video, no ledger write.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>"},{"hash":"2d4bee0","date":"2026-09-24 15:37:06 -0700","author":"Steve Abrams","subject":"Add googly eyes (/eyes skill) pinned bottom-right above the ticker, pointer-events none","body":"Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01KLwoGhVdenyRTtTeMnGHSU"},{"hash":"bd41b0b","date":"2026-09-24 15:28:31 -0700","author":"Steve Abrams","subject":"P24 cartoon batch: build one at a time (no parallel rate-limit risk), start 5:15 ET (2:15 PT) to post by 6:30 ET, 15-min per-build timeout","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"},{"hash":"7b23cb1","date":"2026-09-24 15:24:27 -0700","author":"Steve Abrams","subject":"P24 cartoon batch: 6:00 ET start (3:00 PT), build all briefs in parallel to post by 6:30 ET; parent registers manifest/stories + commits (keeps story_id + tags rules)","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"},{"hash":"2a7fdfb","date":"2026-09-24 15:23:30 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-24T15:23:04 (2 data files) — scripts/com.steve.p24-cartoons-daily.plist yolo/cartoon-batch-log.jsonl","body":""},{"hash":"33a362f","date":"2026-09-24 15:15:07 -0700","author":"Steve Abrams","subject":"P24 daily-cartoons: Ollama concepts + flux-schnell poster + WAN 2.2 clip into local review queue, $1/day cost cap (TK-12153)","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VGPARcQepnFT49pzMk1ZE5"},{"hash":"dc17f16","date":"2026-09-24 15:09:43 -0700","author":"Steve Abrams","subject":"reschedule P24 daily cartoon batch: 9:15 AM → 3:30 AM","body":""},{"hash":"0f638f4","date":"2026-09-24 15:03:55 -0700","author":"Steve Abrams","subject":"cleanup: remove filibuster-marathon (duplicate of Cloture o'Clock) + test scratch files","body":""},{"hash":"e224274","date":"2026-09-24 15:01:03 -0700","author":"Steve Abrams","subject":"gitignore: cover shots*/ , _tmp-*/ , cartoons/_shot-*.png","body":"Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01LfkajczsrHUsBBKvhRwcCG"},{"hash":"b6a6016","date":"2026-09-24 14:55:58 -0700","author":"Steve Abrams","subject":"P24: every category page uses the same photo-card layout as the front page (retire newspaper-mode)","body":"Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01673i1FNbU9H28DomRKpHuk"},{"hash":"318cde7","date":"2026-09-24 14:05:00 -0700","author":"Steve Abrams","subject":"P24: add article tags on every post, with a clickable #tag= filter + tag cloud","body":"Adds a `tags` array (3-6 short, shared-vocabulary tags) to every entry in\nstories-data.js (44), real-news-data.js (42), cartoons/manifest.js (23),\nand the 4 inline FEATURES in index.html (109 posts total, 0 missing).\nCartoons inherit their linked story's tags plus \"editorial cartoon\" (+\n\"paul conrad style\" for the 5 Conrad-manner strips).\n\nUI: tag chips render on every story/feature card, in the article view\nunder the dek, and on every cartoon-gallery card (as a sibling of the\ncard's own <a>, not nested inside it). Clicking a chip filters to every\npost carrying that tag across all categories, shows a \"Tagged: X ✕\"\nindicator, and persists in the URL as #tag=<tag> (works under file://,\nunlike localStorage cross-page). A tag cloud (top tags with counts,\ntags used only once excluded) sits above the grid when no filter is\nactive.\n\nReal-news tagging is deterministic (scripts/lib/auto-tag-real-news.mjs:\ncategory + outlet + ~30 keyword rules, always 3-6 tags, no LLM/API) and\nis now called by fetch-real-news.mjs on every refresh, so a future run\ncan never write an untagged entry. build-cartoon-batch.sh's FAIL check\nis extended the same way it already enforces story_id, so a cartoon (or\nthe story it's paired with) built without tags fails the batch. Each\ndata file's header now states the tags rule.\n\nAlso fixes the last 6 P24 pages still carrying their own inline theme\nCSS instead of the shared assets/style.css from TK-12151's unification\n(cartoons/reference/index.html + the come-in-and-get-me-meter,\nrow-by-row-marshal, gold-tier-treadmill, conrad-filibuster-ink, and\nfilibuster-marathon strips) — same link+data-mood treatment as the\nother 18, so every page's background now matches its mood site-wide\n(verified: index.html and cartoons/reference/index.html compute the\nidentical \"all\"-mood background gradient).\n\nVerified: node --check on every edited JS file; a vm-context script\n(scripts/verify-tags.mjs) asserting 0 of 109 posts have missing/empty\ntags and printing vocabulary stats; a 30-check Playwright pass over\nfile:// (front page article + tag-click filtering, cartoon gallery +\ntag-click filtering, 390px mobile with 0 horizontal overflow, and the\n6 CSS-unified pages) — 0 console/page errors throughout.\n\nTK-12165\n\nCo-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01V29xaoBwcFwJyR4KC43ptn"},{"hash":"6765454","date":"2026-09-24 13:51:22 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-24T13:51:00 (1 data files) — cta/cta3-video-results.json","body":""},{"hash":"4c712f2","date":"2026-09-24 13:46:07 -0700","author":"Steve Abrams","subject":"Fix P24 cartoon video: pull <video> out of the <a> wrapper, clear ticker overlap","body":"CTA cross-browser test found clicks on the video were being swallowed by the\nenclosing anchor (Safari navigated away, Chrome silently did nothing), and the\nfixed bottom ticker covered the native control bar at normal window heights.\nVideo now sits outside any <a>; caption/title link separately. Added\nmargin-bottom to clear the ticker.\n\nCo-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VGPARcQepnFT49pzMk1ZE5"},{"hash":"7bf67a3","date":"2026-09-24 13:26:29 -0700","author":"Steve Abrams","subject":"Animated Filibuster Marathon on P24 front page (WAN 2.2 i2v, ~$0.20)","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VGPARcQepnFT49pzMk1ZE5"},{"hash":"6481269","date":"2026-09-24 13:04:18 -0700","author":"Steve Abrams","subject":"Unify P24 stylesheet: shared external CSS across front page, cartoon gallery, and all 18 strip pages","body":"Extracts the front page's 818-line inline <style> into assets/style.css\n(Part 1, verbatim) and links it from index.html, cartoons/index.html\n(Part 2: gallery-only grid/card/controls rules, deduped against Part 1),\nand all 18 cartoon strip pages (Part 3), replacing three drifting inline\nstylesheets with one shared file — still pure file:// static HTML, no\nserver/build step.\n\nThe 18 strips previously hand-rolled a separate \"parchment newspaper\"\nserif theme (--ink/--ink-dim/--rule/--seal, Georgia font, a per-file\nlight/dark toggle). Rather than rewrite each strip's SVG-drawing JS\n(several read these var() names directly at render time), the legacy\nnames are aliased onto the shared mint-green token system on the `body`\nselector (not `:root` — aliasing at :root freezes the substitution using\nhtml's own default-mood value instead of re-resolving per body[data-mood],\nverified by isolated repro) so every strip re-themes automatically,\nincluding a `data-mood=\"<category>\"` attribute per strip matching its\nmanifest.js section (13 politics, 1 each business/entertainment/scitech/\nsports/weather).\n\nAlso found and fixed: 17 of 18 strips explicitly set data-theme=\"light\"\non load by default (only one has real 3-state auto/dark/light logic), so\na literal parchment-color override on data-theme=\"light\" was permanently\nmasking every mood palette. Light is now a no-op (mood palette shows by\ndefault); the toggle's \"dark\" state layers a night-viewing neutral on\ntop of whichever mood is active, verified working via Playwright click.\n\nVerified with Chrome headless screenshots + Playwright (front page,\ngallery, and 3 varied strips: politics 3-panel, scitech, weather) —\ncorrect per-category theming, legible contrast, 0 console/page errors,\nclick-advance SVG interaction intact, dark-mode toggle intact.\n\nOut of scope (left untouched, in-flight from other concurrent agents):\ncartoons/manifest.js, cartoons/reference/*, and the newer cartoon files\nnot yet in the 18-file manifest (filibuster-marathon, come-in-and-get-me-\nmeter, conrad-filibuster-ink, gold-tier-treadmill, row-by-row-marshal) —\nthose will need the same link+data-mood treatment once their owning\ntickets land.\n\nTK-12151\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01V29xaoBwcFwJyR4KC43ptn"},{"hash":"c130b62","date":"2026-09-24 13:00:06 -0700","author":"Steve Abrams","subject":"Backfill story_id links for 9 orphaned cartoons + codify link-to-article rule","body":"9 cartoons shipped with story_id: null (no underlying article at all):\ncommission-turtles, budget-pie-fight, pork-barrel-train, quorum-call-hide-seek,\ncloture-oclock, lobbyist-revolving-door, conrad-filibuster-ink,\nconrad-filibuster-qwen25, filibuster-marathon.\n\nAdded 7 new stories-data.js entries (window.P24_EXTRA_STORIES, matching the\nexisting schema exactly): 6 distinct stories for the 6 non-filibuster\ncartoons, plus one shared story (pw-filibuster-holloway) for the 3\nPaul-Conrad/model-contest/plain filibuster variants, since they all depict\nthe same invented Delegate Holloway event. Re-pointed all 9 cartoons'\nstory_id fields in cartoons/manifest.js accordingly. Verified via a vm-context\nload of both files: all 23 cartoons now resolve to a real story (0 orphans,\n0 broken links).\n\nCodified the standing rule (Steve, verbatim: \"always link images cartoons to\nthe real article\") as a comment block at the top of cartoons/manifest.js, and\nwired the same requirement into scripts/build-cartoon-batch.sh — the\nnot-yet-installed daily batch pipeline that appends new manifest entries:\nupdated its claude -p prompt to require writing a companion story + a\nnon-null story_id, and added a cheap substring-based post-build check that\nnow fails (not passes) a build whose manifest entry has story_id: null.\n\nScope: only cartoons/manifest.js, stories-data.js, and\nscripts/build-cartoon-batch.sh touched. Left untouched (a sibling agent's\nuncommitted CSS-unification work): cartoons/index.html, index.html, and all\ncartoons/*.html strip files.\n\nTK-12158\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01V29xaoBwcFwJyR4KC43ptn"},{"hash":"dff6f9f","date":"2026-09-24 12:44:18 -0700","author":"Steve","subject":"P24: add Paul Conrad style reference (Masters page + device toolbox) and 3 Conrad-manner strips (meter mayor, corn marshal, toll treadmill)","body":""},{"hash":"e2f9f7b","date":"2026-09-24 12:43:08 -0700","author":"Steve Abrams","subject":"Add Paul Conrad style-reference page for P24 Political Cartoon Desk","body":"Links from the cartoons index header (\"Masters: Paul Conrad ->\") to a new\ncartoons/reference/index.html: bio, creed, the visual devices/brief P24\nuses to draw \"in the Conrad manner,\" and a documented-works table that is\nmetadata + citation-only (no cartoon images hosted — Conrad's actual work\nis copyright LA Times/estate and non-embeddable). Explicitly credits and\nlinks to the standalone research catalog at ~/Projects/paul-conrad-cartoons\n(run `node server.js`, http://localhost:9933) for the fuller 38-record\narchive. Presents P24's Conrad-styled strips as original in-house satire\ninspired by his technique, never as reproductions.\n\nVerified: node --check on manifest.js + reference/paul-conrad.js, and a\nPlaywright pass through cartoons/index.html (23 cards, 0 console errors)\ninto reference/index.html (12 works rendered, correct rights disclaimer).\n\nTK-12155\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01V29xaoBwcFwJyR4KC43ptn"},{"hash":"122888f","date":"2026-09-24 12:42:50 -0700","author":"Steve Abrams","subject":"Feature Conrad-style Filibuster Marathon ink cartoon on P24 front page","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VGPARcQepnFT49pzMk1ZE5"},{"hash":"edcd6b3","date":"2026-09-24 12:40:57 -0700","author":"Steve Abrams","subject":"Add filibuster-marathon P24 cartoon (3-panel SVG, click-advance)","body":"Delegate Holloway filibusters the invented Assembly, cycling reading\nmaterial (phone book -> cereal box -> furniture instructions -> his\nown bill) via Plot Twist. Panel 2 has a melting Dali-style clock;\npanel 3 shows the chamber asleep at their desks. Fully self-contained,\nmatches the commission-turtles.html house style. Verified click-advance\n+ plot-twist cycling with Playwright, zero console/page errors.\n\nAlso fixed manifest.js, which a concurrent process had left with a\nJS syntax error (a raw object literal appended after the array's\nclosing bracket) — folded that entry properly into the array and\nappended this cartoon's entry alongside it.\n\nTK-12146\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01V29xaoBwcFwJyR4KC43ptn"},{"hash":"6db3d93","date":"2026-09-24 11:58:53 -0700","author":"Steve Abrams","subject":"Add Model Arena winner: The Filibuster Marathon (Paul Conrad style, qwen25-7b)","body":"- Qwen2.5 7B won the 3-model Conrad showdown (vs Qwen3 14B; Claude CLI failed)\n- Stark black-ink single panel: delegate reads phone book, clock melts Dali-style, Congress sleeps\n- Challenge 56406f116d2a auto-judged by vision-referee (qwen25-7b PNG 7.5x larger → more detail)\n- Added to cartoons/ + registered in manifest.js with model attribution + challenge_id\n- No external publish; fully reversible (git revert + cartoons/ delete)\n\nChallenge: https://localhost:9758/#/challenges/56406f116d2a\nModel Arena: https://localhost:9758 (admin/DW2024!)\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01V29xaoBwcFwJyR4KC43ptn"},{"hash":"37653be","date":"2026-09-24 11:17:52 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-24T11:17:31 (2 data files) — cartoons/queue/2026-09-24.json yolo/cartoon-batch-log.jsonl","body":""},{"hash":"76360d3","date":"2026-09-24 10:55:05 -0700","author":"Steve Abrams","subject":"Add P24 cartoon: The Revolving Door (invented regulator/lobbyist revolving-door satire)","body":"Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>"},{"hash":"2f0a37c","date":"2026-09-24 10:47:19 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-24T10:46:49 (1 data files) — yolo/cartoon-batch-log.jsonl","body":""},{"hash":"dcc569c","date":"2026-09-24 10:44:38 -0700","author":"Steve Abrams","subject":"P24: register business/entertainment/scitech/sports/weather cartoons in manifest.js","body":"Atomic read-modify-write per coordinator direction (re-read fresh, parse\nwith the same regex daily-cartoon-gen.mjs uses, append, write back) to\navoid a race with the concurrently-running build-cartoon-batch.sh politics\nbatch (still in flight, 4/6 done at commit time, its own entries untouched).\n\nAdded a `section` field (politics/business/entertainment/scitech/sports/\nweather) to all 16 entries -- backfilled \"politics\" on the 11 pre-existing\nones, set to the real category key on the 5 new ones -- for the parallel\ngallery color/filter work on cartoons/index.html. Non-politics entries use\ncategory: \"Editorial Cartoon\" instead of \"Political Cartoon\"."},{"hash":"7ddc237","date":"2026-09-24 10:39:12 -0700","author":"Steve Abrams","subject":"Add P24 cartoon: Cloture o'Clock (invented senate-clock filibuster/cloture satire)","body":"Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>"},{"hash":"9d290d7","date":"2026-09-24 10:37:45 -0700","author":"Steve Abrams","subject":"crazy-news-channel: live-verify hardened admin server (vp-engineering close-out)","body":"Verified directly (not just read the diff): git log confirms b0b4970 shipped,\nbutton POSTs /api/refresh-real-news, server binds 127.0.0.1 only (lsof\nconfirmed), a live POST re-ran fetch-real-news.mjs and refreshed\nreal-news-data.js (mtime advanced, 42 real headlines), and file:// direct-open\nstill degrades gracefully via the location.protocol guard.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01166faVcwiztzWqhHwD9yer"},{"hash":"35cc1c2","date":"2026-09-24 10:30:47 -0700","author":"Steve Abrams","subject":"Add P24 cartoon: The Quorum Call (invented legislative hide-and-seek quorum satire)","body":"Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>"},{"hash":"90bdc43","date":"2026-09-24 10:30:34 -0700","author":"Steve Abrams","subject":"cartoons gallery: front-page per-category colors + category chip filter","body":"Replace the single hard-coded red palette with the same 8 mood blocks\n(all/politics/weather/scitech/sports/entertainment/business/uncategorized)\nfrom ../index.html, add the matching 8-chip category filter that retunes\nbody[data-mood] the same way, give each card a data-cat + --cat-accent\ncolor (left border + badge) so the All view is multicolored, classify\ncartoons via section -> story_id lookup (../stories-data.js) -> \"Political\nCartoon\" -> uncategorized, and persist the selection via\nlocalStorage/#/category/<key> hash (hash wins on load). Sort, density,\ngrid/list toggle, and textContent-only rendering all preserved.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01DqUe1Hvqsnrkxpe4R6jrk1"},{"hash":"3e6ffab","date":"2026-09-24 10:28:55 -0700","author":"Steve Abrams","subject":"P24: add first cartoon for business/entertainment/scitech/sports/weather categories","body":"Five single-panel interactive SVG cartoons (matching the\n20260924-gnome-who-wouldnt-hush.html pattern: 5-stage click-to-advance,\nPlot Twist button, dark mode, keyboard nav, reduced-motion safe), each\nlinked via story_id to a real invented story in stories-data.js:\n- tollpoints-diamond-status -> bs-toll-loyalty (Business)\n- jackalope-custody-battle -> es-jackalope-statue-town-rivalry (Entertainment)\n- tuber-bank-of-chugwater -> sci-battery-potato (Sci-Tech)\n- the-zipper-that-ended-an-inning -> sw-mascot-costume (Sports)\n- the-corner-with-its-own-weather -> sw-persistent-crosswind (Weather)\n\nPlaywright-verified at 1440/390px, dark+reduced-motion, and full keyboard\nnav (Tab reaches #art, Enter/'n' both advance) — zero console/page errors\non all 5. manifest.js registration follows once the live\nbuild-cartoon-batch.sh run (today's 6-cartoon politics batch) finishes\nwriting to that file, to avoid a race on the shared array.\n\nAlso (prep only, not wired into rotation mix): cartoon-prompt-bank.mjs now\ncarries a category field per bank entry (existing 12 default to politics)\nplus 3 new stub entries per non-politics category (15 total), and\ndaily-cartoon-gen.mjs threads category through to queue briefs. The picker\nitself is still a flat shuffle across the whole bank -- a future session\ndecides the rotation mix."},{"hash":"bb599b5","date":"2026-09-24 10:26:35 -0700","author":"Steve Abrams","subject":"Add P24 cartoon: The Pork Barrel Express (invented infrastructure-bill train satire)","body":"Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>"},{"hash":"b0b4970","date":"2026-09-24 10:24:20 -0700","author":"Steve Abrams","subject":"admin-server: fix 3 verified security/robustness holes","body":"- Malformed-URL crash: decodeURIComponent was outside try; a single\n  'curl /%' killed the process on Node v26 (unhandled URIError). Now 400.\n- Path-traversal prefix bug: startsWith(ROOT) without separator let a\n  sibling <ROOT>-secrets path through. Now boundary is ROOT + path.sep,\n  plus a top-level allowlist (index.html, *-data.js, cartoons, images)\n  so .git/, scripts/ source, and .env are no longer served.\n- Unauthenticated cross-origin POST: 127.0.0.1 is not a CSRF boundary;\n  now rejects any non-same-origin Origin on the state-changing refresh.\n\nAll three reproduced live before fixing and proven fixed with negative\ntests (crash→400, leak→403, drive-by→403); legit paths + refresh still 200."},{"hash":"6609a82","date":"2026-09-24 10:24:06 -0700","author":"Steve Abrams","subject":"cartoons gallery: add list/grid view toggle alongside density slider","body":"Adds a segmented Grid/List view-mode toggle to the P24 cartoon gallery\ncontrols bar. List mode renders cards as full-width horizontal rows\n(reusing the existing textContent-only card-building code via a\n.list-mode CSS modifier) and hides the density slider since row width\nis no longer density-controlled. View mode persists to localStorage\n(p24cartoons.viewmode) alongside the existing sort/density keys.\nVerified with Playwright at 1440px and 390px viewports: 0 console\nerrors, 0 page errors, correct persistence across reloads.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01DqUe1Hvqsnrkxpe4R6jrk1"},{"hash":"0368b35","date":"2026-09-24 10:16:49 -0700","author":"Steve Abrams","subject":"crazy-news-channel: verify admin refresh endpoint live (session-close check)","body":""},{"hash":"1b61464","date":"2026-09-24 10:16:32 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-24T10:15:55 (2 data files) — cartoons/queue/2026-09-24.json yolo/cartoon-batch-log.jsonl","body":""},{"hash":"6504ba2","date":"2026-09-24 10:15:53 -0700","author":"Steve Abrams","subject":"crazy-news-channel: final refresh of real Google News data (session end)","body":""},{"hash":"e79e251","date":"2026-09-24 10:14:39 -0700","author":"Steve Abrams","subject":"Add P24 cartoon: Who Gets the Last Slice (budget pie-slicing satire)","body":"Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>"},{"hash":"daf7ea3","date":"2026-09-24 10:11:16 -0700","author":"Steve Abrams","subject":"cartoons: 6 new political cartoons for invented politics stories, story_id-linked (TK-12138)","body":"- The Meter That Wouldn't Yield (pw-meter-street): a parking meter's honors escalate from street-naming to a council seat to a re-election bid to a one-day mayoralty it refuses to vacate.\n- Row by Row Only (pw-corn-etiquety): Kansas legislates the one true corn-eating method, deputizes Corn Marshals, and bans cob holders as cheating devices.\n- The Gnome Who Wouldn't Hush (pw-lawn-gnome-tax): a loud lawn gnome gets registered, ankle-monitored, sued over, and finally reviewed by a Gnome Parole Board.\n- Four Minutes Ahead of Everyone (pw-clock-secession): a town clock set 4 minutes fast escalates into its own named time zone and a secession petition from Mountain Time.\n- The Casserole Succession (pw-bean-queen): a ceremonial Bean Queen title gets term-limited, the sash is contested, and a courthouse rules succession passes by casserole, not bloodline.\n- The Hedge Non-Aggression Pact (pw-hedge-statue): a statue-shaped hedge draws a height limit, a fine, an interstate espionage accusation, and a formal treaty with Idaho.\n\nEach cartoon is a single-panel inline SVG scene with a Plot Twist button that cycles through the story's own 5-headline escalation arc, concluding cleanly at the final stage (no loop-back). Dark mode, keyboard nav (N/arrow/Enter/Space), focus-visible outlines, and prefers-reduced-motion all match the commission-turtles template. manifest.js gains an additive story_id field on every entry (null on the pre-existing commission-turtles cartoon) linking each cartoon to its source invented-politics story for category-view lookup.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XKaojLnVW8hyxsVriY5f9a"},{"hash":"0839cdd","date":"2026-09-24 10:08:36 -0700","author":"Steve Abrams","subject":"Category view: V4 Newspaper Column dense listing + cartoon-thumb slot for politics (TK-12138)","body":"Steve picked V4 (of 6 Figma mockups: figma.com/design/oqcZhYvN4kWvw0VRUa1FYG) — dense\nmulti-column newspaper-style headline listing for the category route (#/category/<x>),\ndistinct from the 'All Categories' home view which keeps its original photo-card stack.\n\n- .story-grid.newspaper-mode: true CSS column-count flow (1/2/3/4 cols by breakpoint),\n  restyles the SAME storyCardHTML() markup used everywhere else — no markup fork, so\n  patchStoryGrid()'s keyed morph reconcile never bridges a shape change from this toggle\n  alone (a category route change already swaps the id set on its own).\n- Inline category label (.np-cat-label) replaces the badge-pill kicker in dense mode;\n  detail paragraph + read-more link hidden (headline does the work); escalation bar\n  shrinks to a left rail-style accent.\n- Toggled in renderStories(): els.storyGrid.classList.toggle('newspaper-mode',\n  state.category !== 'all').\n- findCartoonForStory(): looks up cartoons/manifest.js (window.P24_CARTOONS, now loaded\n  via a new <script> tag) by story_id. A POLITICS story with a linked cartoon shows a\n  cartoon-thumb link INSTEAD OF its photo, in the same image slot, styled for both card\n  and newspaper mode. Gracefully falls back to the plain photo (or nothing) when no\n  cartoon is registered — safe to ship ahead of / independent from the cartoon content\n  landing. Real-news political stories are NEVER cartoon-linked (only invented pw-*\n  stories get one — the Cartoon Desk's own policy: invented bureaucracy only, never real\n  people/parties).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01XKaojLnVW8hyxsVriY5f9a"},{"hash":"c832bd4","date":"2026-09-24 10:08:29 -0700","author":"Steve Abrams","subject":"Add admin button to refresh real news via a tiny local admin server","body":"Steve wanted a button instead of typing `node scripts/fetch-real-news.mjs`\nin a terminal. Browsers on file:// can't execute local processes, so this\nadds an opt-in zero-dependency admin server (scripts/admin-server.mjs,\nbinds 127.0.0.1 only) that serves the static site and exposes\nPOST /api/refresh-real-news to shell out to the existing fetch script.\n\nThe admin panel's new \"Refresh Real News\" button posts to that endpoint\nand reloads on success. Opening index.html directly via file:// (the\ndefault, unchanged \"no server needed\" experience) shows a clear\ninstruction to start the admin server instead of failing silently.\n\nVerified live: server-mode click-through refreshed 42 real headlines\n(real-news-data.js mtime advanced) with a real headless-Chrome run, and a\nseparate file:// run confirmed the rest of the page is unaffected and the\nbutton degrades gracefully with zero console errors.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01166faVcwiztzWqhHwD9yer"},{"hash":"24b3be0","date":"2026-09-24 09:58:20 -0700","author":"Steve Abrams","subject":"crazy-news-channel: refresh real Google News headlines (42 stories, all 7 categories)","body":"Re-ran scripts/fetch-real-news.mjs to restore current real headlines after a\nparallel-session rewrite left the feed stale. $0, no API key.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01166faVcwiztzWqhHwD9yer"},{"hash":"2d5e99e","date":"2026-09-24 09:54:59 -0700","author":"Steve Abrams","subject":"Cartoon batch: exit non-zero when any cartoon fails verification","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01DqUe1Hvqsnrkxpe4R6jrk1"},{"hash":"5aff77e","date":"2026-09-24 09:54:34 -0700","author":"Steve Abrams","subject":"Cartoon batch: verify output file + manifest entry (not just exit 0); grant headless claude scoped edit/test tools","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01DqUe1Hvqsnrkxpe4R6jrk1"},{"hash":"30215fc","date":"2026-09-24 09:54:17 -0700","author":"Steve Abrams","subject":"Cartoon batch: set PATH for launchd so node/claude resolve; fail loudly if missing","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01DqUe1Hvqsnrkxpe4R6jrk1"},{"hash":"7518fc5","date":"2026-09-24 09:42:31 -0700","author":"Steve Abrams","subject":"Fix Cody's 4 verified findings: XSS in cartoon gallery, file:// manifest fetch failure, cover-label desync, dead tab stops on inert panels","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01DqUe1Hvqsnrkxpe4R6jrk1"},{"hash":"5cd57ad","date":"2026-09-24 09:35:42 -0700","author":"Steve Abrams","subject":"P24 Political Cartoon Desk: gallery archive, daily prompt generator, first strip (Commissions All the Way Down)","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01DqUe1Hvqsnrkxpe4R6jrk1"},{"hash":"3a5ce94","date":"2026-09-24 09:18:39 -0700","author":"Steve Abrams","subject":"Stop tracking Playwright screen-recording video output; ignore *.webm/*.mp4 going forward","body":"47 tracked recordings (~317MB) from prior screenrecord/cta harness runs\nwere regenerable QA artifacts, not source — permanently bloating .git for\nevery future clone/gc, including one 0-byte truncated recording. Untrack\nthem (files remain on disk unchanged) and add *.webm/*.mp4 to .gitignore\nso future harness runs stop re-committing video. debug-log.jsonl and the\n*.md reports under screenrecord/ stay tracked (small, useful history).\n\nNote: this untracks going forward only — the 317MB already sits in git\nhistory/blobs and .git stays large until that history is rewritten\n(filter-repo/BFG), which is a separate, more invasive action not taken\nhere since it permanently discards the original commit objects.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_019yVgDG93UFMN6sNbRaUfzd"},{"hash":"f58b710","date":"2026-09-24 09:18:27 -0700","author":"Steve Abrams","subject":"chaos-screenrecord: fix chaosFF-concluded's coverage gap on the standby flash","body":"Two bugs in the same step, both confirmed by direct repro against the\nlive app with a fake clock:\n\n1. The 12x25s=300s virtual-time cap was sized for the app's old 6s stage\n   interval. Once MIN_STAGE_INTERVAL_SEC (a separate fix) raised the\n   effective floor to 75s, a full escalation needs 4 advances x 75s =\n   300s ALONE, leaving zero room for any story's offsetSec — and in\n   run-orderings where this step executes before tier1/2/3 (e.g. run1's\n   reversed order), elapsedSec is still ~0 when it starts, so the loop\n   fell a few seconds short and PLEASE STAND BY was never reached.\n\n2. Even with enough virtual time, the standby flash is only visible for\n   ~2900ms of app-time before its own auto-hide timer fires. Sampling at\n   25s chunk boundaries meant the entire visible window could open and\n   close inside a single chunk and never get observed — verified\n   empirically: with (1) fixed alone, allConcluded reliably went true but\n   caught stayed false.\n\nFix: read MIN_STAGE_INTERVAL_SEC live from the page instead of\nre-hardcoding a number that can drift out of sync with the app, size the\nloop off it with headroom, and sample every 1s (well under the flash's\nvisible window) instead of every 25s. Verified end-to-end against the\nexact worst-case ordering (elapsedSec~3 at loop start): caught=true,\nallConcluded=true, ~0.8-3.5s real wall-clock overhead.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_019yVgDG93UFMN6sNbRaUfzd"},{"hash":"1ce86db","date":"2026-09-24 09:13:03 -0700","author":"Steve Abrams","subject":"Fix Chaos Index reading ~51% on fresh load — exclude single-stage real-news from the escalation gauge","body":"computeChaos() scored every story's stageIndex/(stages-1), with a `span>0 ? … : 1`\nfallback that returned FULL chaos for a single-stage story. After the \"one feed\"\nreal-news merge, ~42 of 82 stories are single-stage (real news doesn't escalate),\nso each was counted as maximally escalated — pinning a fresh channel at ~51%\n\"Concerning\" and making the Nominal band unreachable on load.\n\nCompute the gauge over escalatable (multi-stage) stories only. Verified: fresh\nload 0% Nominal, climbs to 100% Post-Journalism at true meltdown, klaxon +10\nbump preserved, 0 console/page errors.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01BkfgaotGs6PaEBcnKxnfZG"},{"hash":"5643bfe","date":"2026-09-24 09:00:40 -0700","author":"Steve Abrams","subject":"One feed: invented satire + real Google News together","body":"fetch-real-news.mjs had overwritten stories-data.js, deleting the 37 invented\nstories. Real news now lives in real-news-data.js (P24_REAL_STORIES); the\ninvented set is restored from 195e22c. Saved browser lists pick up any\ndefault story they've never seen, while admin deletions stay deleted.\n\nCo-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VLjoHsyGTDeVCa1rdrAxnk"},{"hash":"6018a55","date":"2026-09-24 08:56:29 -0700","author":"Steve Abrams","subject":"Remove [CORRECTION] headline stamp from concluded stories","body":"At high chaos, concluded stories were stamped [CORRECTION], which on real\nGoogle News headlines reads as if the outlet issued a correction.\n\nCo-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VLjoHsyGTDeVCa1rdrAxnk"},{"hash":"4d10d98","date":"2026-09-24 08:43:22 -0700","author":"Steve Abrams","subject":"5x: final report, Cody SHIP IT on 2472ae0","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VLjoHsyGTDeVCa1rdrAxnk"},{"hash":"2472ae0","date":"2026-09-24 08:40:04 -0700","author":"Steve Abrams","subject":"5x sweep 5: route every home link through goHome() via one delegated handler","body":"The empty-state 'All Categories' link added in b868846 still native-navigated\nto '#/', recreating the two-home-URLs bug. Replaced the per-link listeners\nwith a single delegated handler for a[href=\"#/\"] so no home link can miss it.\nModified clicks (cmd/ctrl/shift/middle) are left to the browser.\n\nCo-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VLjoHsyGTDeVCa1rdrAxnk"},{"hash":"b868846","date":"2026-09-24 08:34:15 -0700","author":"Steve Abrams","subject":"5x sweep 3: Cody's 3 verified findings — article back/Escape returns to its own category (not Home), one canonical home URL, empty-state link is real","body":"- goBackToGrid() now derives its target from the OPEN ARTICLE's own category\n  (works the same for a direct deep link), not from a since-changed shared\n  variable — fixes the regression Cody's proposed one-liner would have\n  introduced (Escape/back-link on a category page with no article open would\n  have silently done nothing, since state.category already equals the target).\n- New goHome() is the single path to hash=\"\": the logo and the category\n  page's \"All Categories\" link both route through it now, instead of the\n  logo native-navigating to a second, non-canonical \"#/\" URL.\n- Escape now closes one layer per press (article -> its category -> Home),\n  matching the UI's own depth instead of collapsing two steps into one.\n- The category-empty-state's \"All Categories\" text is a real link now,\n  not inert bold text next to a working one two lines up.\n\nVerified: composed category->article->back/Escape journeys, deep-linked\narticle back, categoryBackLink always-home even mid-category, second-Escape\ndepth, logo->home canonical hash, empty-state link href. 0 console errors,\n390/1440/reduced-motion clean.\n\nCo-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VLjoHsyGTDeVCa1rdrAxnk"},{"hash":"0466ef4","date":"2026-09-24 08:29:44 -0700","author":"Steve Abrams","subject":"Slow the breaking-news ticker to a constant readable ~55-70 px/s","body":"Loop duration was fixed (60s -> 24s by chaos tier) while the loop grew to\n40+ headlines, so the crawl hit ~1,600 px/s. Duration is now computed from\nthe track length so the speed stays readable at any headline count.\n\nCo-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VLjoHsyGTDeVCa1rdrAxnk"},{"hash":"40beaff","date":"2026-09-24 08:25:26 -0700","author":"Steve Abrams","subject":"5x: sweep 1 (category-page fix) and sweep 2 (clean) notes","body":"Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VLjoHsyGTDeVCa1rdrAxnk"},{"hash":"d7c494a","date":"2026-09-24 08:04:02 -0700","author":"Steve Abrams","subject":"Add scripts/fetch-real-news.mjs: real Google News RSS per category, $0","body":"index.html is static with no server, so it can't fetch news.google.com\nitself (CORS). This is the offline refresh step (run manually or via\ncron) that pulls current headlines from Google News RSS — no API key,\nno spend — and writes them into stories-data.js as\nwindow.P24_EXTRA_STORIES, replacing the invented satire that lived\nthere before.\n\n- politics/weather -> Google News search RSS (no topic feed exists)\n- scitech/sports/entertainment/business/uncategorized -> topic feeds\n  (TECHNOLOGY/SPORTS/ENTERTAINMENT/BUSINESS/WORLD)\n- Dependency-free XML parsing (regex + a small entity decoder); Google's\n  <description> is XML-entity-encoded HTML, so entities are decoded\n  BEFORE tags are stripped (stripping first leaves literal escaped tags\n  sitting in the \"stripped\" text — caught and fixed during verification).\n  Topic-feed descriptions are a related-coverage <ol><li> list; <li>\n  becomes a bullet separator instead of one run-on line.\n- Each story ships with exactly ONE stage (stageIndex 0), so tick()'s\n  alreadyMax check holds it there forever -- real news never escalates\n  into the satirical spiral (dovetails with the Ask-1 slowdown/pause).\n- Carries sourceUrl/sourceName so the article view's new outbound-link\n  block (previous commit) renders a real, working link to the source.\n\nRan live: 42 real, current, correctly-categorized headlines across all\n7 categories (politics/weather/scitech/sports/entertainment/business/\nuncategorized), verified via Playwright against a fresh page load --\neach category filter shows its real stories, the outbound link opens\nthe real news.google.com redirect URL in a new tab with rel=noopener,\nzero console errors.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01166faVcwiztzWqhHwD9yer"},{"hash":"f79b2f1","date":"2026-09-24 08:03:52 -0700","author":"Steve Abrams","subject":"Article view: render a real outbound link to a story's source article","body":"renderArticleContent() now checks source.sourceUrl and, when present,\nrenders a real <a target=\"_blank\" rel=\"noopener noreferrer\"> pointing at\nit (\"Read the full story at <source> →\"). Prepares the article view for\nRSS-backed stories, which carry a real sourceUrl/sourceName and have no\nfull article text of their own to show.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01166faVcwiztzWqhHwD9yer"},{"hash":"458b2cc","date":"2026-09-24 07:55:25 -0700","author":"Steve Abrams","subject":"Category selection opens a real page (#/category/<key>), not just an in-place recolor; logo links home","body":"- Clicking a category navigates to a distinct, deep-linkable URL with its own\n  page header (name + blurb + back link), same pattern as the article view.\n- Back/forward and direct links work; radios stay in sync with the route.\n- Escape backs out of a category page to Home, same as it does for articles.\n- The P24 logo/wordmark now links back to Home.\n\nCo-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_01VLjoHsyGTDeVCa1rdrAxnk"},{"hash":"d583f26","date":"2026-09-23 20:31:26 -0700","author":"Steve Abrams","subject":"Reset to Defaults clears FAILED_IMAGES","body":"Cody's re-gate on 30c5077 passed but found one real gap: a failed image is\noften a transient hiccup, but resetToDefaults() never cleared FAILED_IMAGES,\nso it stayed dead for the rest of the session even after \"start fresh\".\nReproduced (image failed -> Reset -> still in the set, image never came\nback) and fixed with one FAILED_IMAGES.clear(). Verified: image reappears\nafter Reset. Full regression pass: fuzz 4888/0, click race 30/30, overflow\n4 widths x Chrome/WebKit, hash freeze, Cody fixes 4/4.\n\nCo-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_011iuURMrsPBwAqMc5LgvFLH"},{"hash":"30c5077","date":"2026-09-23 20:25:04 -0700","author":"Steve Abrams","subject":"Keyed child morph + failed-image memory: stop cascading card rebuilds","body":"Cody gate on 43aba19 was right: morphNode matched children by position, so\nonce a card's photo 404'd (onerror removed the <img>) every later sibling\nwas off by one, each tag mismatch triggered replaceWith, and the whole card\nwas rebuilt -- and the dead <img> was re-added and re-fetched. Reproduced on\n43aba19: 0/20 descendants survived a stage change, link detached, image back.\n\n- morphNode now matches children by tag + first class (forward search), so\n  an optional child appearing/disappearing doesn't shift siblings.\n- imageFailed() records 404'd srcs; card and article templates omit them,\n  so the card matches its template again instead of fighting it.\n- patchStoryGrid compares with isEqualNode (morph can reorder attributes,\n  which made outerHTML differ forever and re-morph every tick).\n\nAfter: 19/19 descendants survive, link kept, image fetched once. Fuzz: 150\nrandom mutations (stage bumps, creates, deletes, image failure, 12 lead\nchanges), 4888 card checks vs fresh render, 0 mismatches. All prior\nregressions pass (cards destroyed 0/30s, clicks 30/30, Esc race, focus\nreturn, overflow 4 widths x Chrome/WebKit, hash, Cody 4/4, arcs, escaping,\nstandby, reduced motion, WebKit smoke).\n\nCo-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_011iuURMrsPBwAqMc5LgvFLH"},{"hash":"43aba19","date":"2026-09-23 20:12:31 -0700","author":"Steve Abrams","subject":"Morph escalated cards in place; close articles synchronously on Escape","body":"Screenrecord (chaos pass) reproduced two leftovers, both confirmed on the\nprior HEAD before fixing:\n- patchStoryGrid() still replaceWith()'d any card whose markup changed, so a\n  click in flight on a card that just escalated hit a detached node. Now\n  morphNode() syncs attributes/text and recurses into children in place;\n  the card's <article> and <a> nodes survive a stage change. Cards\n  destroyed per 30s: 19 -> 0.\n- goBackToGrid() closed the article only via the async hashchange, and the\n  Escape handler checked the not-yet-rendered DOM, so click -> Esc -> Esc\n  could leave the article open. Now it renders synchronously, Escape checks\n  route intent, and hashchange skips when the view already matches.\nVerified: article closed after rapid Esc x2, back/forward still work, focus\nreturns to the originating card, and every prior regression passes\n(overflow sweep 4 widths x Chrome/WebKit, grid race 30/30, hash freeze,\nCody fixes 4/4, arcs, escaping, standby, reduced motion, WebKit smoke).\n\nCo-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>\nClaude-Session: https://claude.ai/code/session_011iuURMrsPBwAqMc5LgvFLH"},{"hash":"5d6d91c","date":"2026-09-23 20:05:07 -0700","author":"auto-commit-fleet","subject":"auto-data-snapshot: 2026-09-23T20:04:48 (1 data files) — screenrecord/debug-log.jsonl","body":""}]}