brydup@mac ~/cowork % cat 2026-05-19-evening-tech-brief.md # 2026-05-19 — Evening Tech Brief *Night wrap. Tight read of the day's totality across four session arcs. Companion to the granular per-arc record at ~/cowork/2026-05-19-tech-brief.md.* --- ## What today actually was A four-arc workday that started as a cron-alarm diagnosis and ended as a publishing-stack restructure. The through-line: **every surface we touched today became more honest about its failure modes and more deliberate about its persistence layer.** - **Morning (arc 1) — cowork sync diagnose + harden.** A chronic cowork-backup.sh cron alarm exposed a silent multi-day mirror clobber (rsync -a --delete from Mac → Pi was wiping .git and reverting gotchas.md with mtimes preserved). Fix wasn't a patch — it was retiring the wrong sync model and moving to GitHub-as-source-of-truth + bidirectional pull/push. Plus a security pass (/security-review) remediated 8 of 12 findings in one go. - **Afternoon (arc 2) — Slack → Discord migration.** Five Pi cron wrappers cut over to Discord-only for both failure alerts and content delivery (briefing/triage land in #morning-briefings and #inbox as chunked-by-##-section markdown). Discovery scope-miss on 3 yt-* scripts caught mid-cutover; dual-post added pending Phase 2 soak. Two security incidents during webhook setup (URL leaks via read -rs defeat) → new Mac-side save-discord-webhook-from-clipboard.sh helper that pipes clipboard → pbpaste → ssh, never echoes. - **Afternoon (arc 3) — Anthem MRX 300 calibration reference.** A 14-section reference doc for AV gear setup, Mac-canonical with HTML sibling and a Drive mirror per the canonical-surface rule. - **Evening (arc 4) — FoWR Discord migration + workflow restructure.** Both flash and full FoWR briefings now auto-post to Discord #fowr on Bryan's trigger; full Day 81 produced and shipped end-to-end. Bryan caught structural cleanups for ALL future briefings (drop disclaimer block, drop Date/Day/Operation body line, standardize title/subtitle) — updated permanently in three source docs. Substack .md paste failure documented; HTML-via-browser is the path forward. --- ## Net state shift | Surface | Start of day | End of day | |---|---|---| | Pi cron failure alerts | Slack #cron-alerts + Discord "Pi Cron Alerts" (dual) | Discord-only for 5 wrappers; yt-* on dual-post pending Phase 2 | | Pi cron content delivery | Slack slack_send_message from claude -p sessions | claude -p writes $STATE_DIR/*.md; wrapper posts chunked to Discord | | FoWR briefings | Mac archive + HTML render + manual Substack | Same + auto-post to Discord #fowr on trigger | | FoWR briefing format | Inline disclaimer block + Date/Day/Operation body line | Both removed permanently; Substack title carries day + date | | cowork repo source-of-truth | Mac via rsync mirror (unreliable) | GitHub origin/main with Mac + Pi bidirectional | | ~/bin/save-webhook.sh (Pi read -rs) | Trusted; leaked twice today | Replaced for new webhooks by Mac-side clipboard helper | | Discord webhook count | 1 (cron-alerts) | 4 (cron-alerts, morning-briefings, inbox, fowr) | | Cowork commits today | 0262b4f start of day | 65436a2 end of day (4 commits, all pushed) | --- ## Decisions that will outlast today - **GitHub origin/main is the canonical source for ~/cowork/.** Mac, Pi, and GitHub all participate bidirectionally. No more rsync mirror. The 12 numbered subdirs (01_too_important/ through 31_jukebox_hero/) stay local-only on each machine per .gitignore — accepted divergence for now. - **Allowlist > denylist** in cowork-backup.sh and brain-backup.sh. Explicit pathspec scoped to specific filename patterns; defensive .gitignore is belt-and-suspenders, not primary defense. - **Failure-alert helpers must short-circuit on rc=0.** Both slack-alert.sh (today's rewrite) and discord-alert.sh (already correct) refuse to manufacture false failures. - **Content delivery to chat surfaces is wrapper-level, not in-agent.** claude -p writes files; the wrapper picks them up and POSTs. Decouples generation from delivery and keeps the agent's allowlist scoped to Write rather than arbitrary network calls. - **read -rs is not a reliable secret-entry mechanism** in Claude Code's bash interface or under macOS bracketed-paste. New ~/bin/save-discord-webhook-from-clipboard.sh is the canonical path; the old Pi-side save-webhook.sh is left for non-Claude-Code use cases but its read -rs defeat is documented in ~/.claude/CLAUDE.md §8. - **Channel-name args are command-injection / path-traversal surfaces.** Allowlist regex [A-Za-z0-9_-]{1,32} enforced in both helper scripts after the security review caught it. - **FoWR briefing structural cleanups locked.** Disclaimer block dropped (info on Substack profile); Date/Day/Operation body line dropped (Substack title carries it); title FoWR Day N — Month D, YYYY + subtitle Evening Brief: <theme1>, <theme2>, <theme3> standardized. Three source docs updated permanently. - **HTML-via-browser is the Substack paste path**, not raw .md. Workflow doc was wrong; corrected. The renderer's YAML frontmatter (time_utc, sources_summary, editorial_state) is required for publish-bound briefings or readers see placeholder defaults. --- ## Three lessons worth carrying forward 1. **Silent rollback with preserved mtime is the worst class of bug.** Arc 1 took ~30 hours of MTTD because nothing in git status or ls -l looked anomalous. The cron alarm was the only signal that saved canonical content from being overwritten upstream. Builds the case for: **wire a positive-signal check that the desired side-effect happened, not just rc=0.** 2. **Migration audits must grep -RIl <callsite> ~/ across the full home tree** — not just the obvious wrapper set. Arc 2 missed 3 yt-* callers of slack-alert.sh because the discovery scope was the named cutover targets. If I'd disabled slack-alert.sh for more than the ~60 seconds it actually took to catch the miss, the hourly yt-subs cron would have lost its failure alerts silently. 3. **The chat is the leak surface.** Two webhook URLs leaked today — first via Claude Code's stdin capture defeating read -rs, second via macOS bracketed-paste echoing into a terminal scrollback that Bryan then pasted back. Both required Discord-side revoke + recreate. The clipboard-only entry path (pbpaste → ssh stdin) is the durable fix — secrets never enter chat, never touch terminal echo, never traverse read. --- ## Tomorrow's priorities 1. **Verify 5/20 morning cron firings** of arc 2's Discord-only paths (02:00 brain-backup, 03:00 cowork-backup, 04:00 jukebox-sync, 07:00 inbox-triage, 08:00 morning-briefing). First production confirmation that the failure-alert path is silent on success and Discord-bound on failure, and that content delivery to #morning-briefings / #inbox works under cron timing constraints. 2. **Phase 2 cutover** (Task #8): strip Slack from yt-weekly-synth.sh, cluster-daily.sh, fetch-and-score.py after the dual-post soak. Then mv ~/bin/slack-alert.sh → .disabled. Bryan revokes Slack OAuth via admin (load-bearing per the 5/14 lesson). Then rm ~/.config/slack-bot-token (requires explicit approval per auto-mode policy). 3. **Publish Day 81 to Substack** if not done tonight. HTML-via-browser paste, title FoWR Day 81 — May 19, 2026, subtitle Evening Brief: Gulf-Arab veto, Russia HEU specificity, Artesh signal. Then the 5-min launch Note + 7:30 PM standalone Note rituals. 4. **Anthem MRX 300 commit decision** — files are currently untracked in ~/cowork/. Either commit to repo (becomes durable across machines), keep local-only, or move to Drive. 5. **M2 security finding** when convenient — refactor discord-post.sh curl call to use --config file so the webhook URL isn't briefly visible in ps. --- ## Open items inherited from today - Discord #fowr has stale Day 81 chunks (disclaimer + Date/Day/Operation in the body). Cosmetic; deferred per Bryan ("fix tomorrow"). - /security-review skill needs cwd-tolerance — failed today because cwd was ~/bryanduplantis. Possible fix: --path arg or auto-cd to nearest git repo. - Backup architecture for 01_too_important/ workflow docs — gitignored subdir means today's CLAUDE.md edits + ideas-backlog.md don't reach GitHub. Decision deferred. - AI per-email summaries + Discord-based feedback loop — captured to ~/cowork/01_too_important/inbox-triage-ideas-backlog.md for design conversation later. --- ## Brain refs - 2026-05-19-session-cowork-sync-diagnose-and-harden (arc 1) - 2026-05-19-session-pi-cron-discord-migration (arc 2) - 2026-05-19-session-fowr-discord-migration (arc 4) - (Arc 3 — Anthem MRX 300 — captured separately if/when Bryan ran /capture for it) - Commits today: BryanDuplantis/cowork@main from 0262b4f to 65436a2 — 22f4168, 8c6c029, 4eb1166 (arc 1), 0ca9db7 (arc 1 tech brief), 0e94ac7 (arcs 2+3 tech brief), 65436a2 (arc 4 tech brief). Plus BryanDuplantis/brain@main 84bb124 (brain wrapper update from arc 1). - Granular per-arc record: ~/cowork/2026-05-19-tech-brief.md (455+ lines, all 4 arcs in detail) --- *End of day. Sleep well.* brydup@mac ~/cowork %