Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .ai-sessions/lessons.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Recent
<!-- 10 most recent lessons, newest first -->
- Google Forms' new publish flow defaults the responder audience to the org; a forms.gle link returns 401 anonymously until Responders is set to "Anyone with the link". Always curl responder links unauthenticated before announcing them (2026-09-06)
- The meetup.com pro dashboard's network-events view does not show network events created via the API filterId path, so a dash-created event can silently duplicate a script-created one; check the origin group's ACTIVE events by networkEvent id when duplicates are suspected (2026-09-06)
- `mkdocs build --strict` (and `just validate`) fail on this repo even on clean main because `site_url` is unset; CI deploys via `gh-deploy` without strict, so validate changes with a plain `mkdocs build` (2026-09-06)
- On a rerun of the monthly update, audit artifact existence first (Mailchimp campaign list, Drive title search, Canva deck page-1 thumbnail, Discord scheduled-events API) instead of walking the skill's from-scratch order (2026-09-06)
- A speaker headshot that arrives as a Google Drive link: download it via the Drive MCP and commit it to `docs/assets/images/` so `mkdocs build --strict` and CI are not blocked; the pushed branch's `raw.githubusercontent.com` URL then doubles as the public source for the Canva `upload-asset-from-url` (a private Drive file is not fetchable by Canva) (2026-08-14)
Expand All @@ -10,8 +12,6 @@
- `str.format(**ctx)` does not re-parse braces inside the substituted values, so a data field like `"Understanding {} in Python"` renders literally and never raises; verify any "format-injection crash" claim empirically before fixing (2026-08-14)
- Squash-merged branches look "not in main" by commit ancestry (`merge-base --is-ancestor` fails) because their commits were rewritten; confirm merged-vs-stale by content: a diff dominated by deletions that re-adds older text means main is ahead and the branch is stale (2026-08-14)
- When resolving an add/add conflict in a sops-encrypted file, take the side that is the plaintext superset (its MAC already covers the full key set) and verify by actually running `sops --decrypt`; a hand-stitched ciphertext would fail MAC validation (2026-08-14)
- Canva share/export links 403 to link checkers (bot-blocked, not broken); add `canva.com` to `.lycheeignore` rather than editing the reference doc (2026-08-14)
- Canva export URLs are signed and expire within hours; for Discord announcements attach the PNG to the webhook message (`files[0]` multipart) so the image outlives the link, and state the link's expiry next to it (2026-07-31)

## Secrets
- `sops set file '["KEY"]' '"value"'` adds a key to a sops dotenv without a decrypt/edit/re-encrypt round trip (2026-07-31)
Expand All @@ -24,6 +24,10 @@
- Don't verify an API write by GETting it back from the same API; it echoes your write. If the system of record is a UI (Mailchimp builder), the UI is the only honest check (2026-07-31)
- Mailchimp Marketing keys end in `-usNN`; Transactional (Mandrill) keys are a separate product and format. The Transactional MCP rejects Marketing keys with "Invalid API key" (2026-07-31)

## Canva
- Canva share/export links 403 to link checkers (bot-blocked, not broken); add `canva.com` to `.lycheeignore` rather than editing the reference doc (2026-08-14)
- Canva export URLs are signed and expire within hours; for Discord announcements attach the PNG to the webhook message (`files[0]` multipart) so the image outlives the link, and state the link's expiry next to it (2026-07-31)

## Workflow
- meetup.com shows "0 upcoming events" to WebFetch because listings render client-side; the raw HTML's `__NEXT_DATA__` Apollo cache has full event data. Use `scripts/scrape_local_meetups.py` in the meetup-update skill, and check for embedded JSON before declaring any Next.js site unscrapeable (2026-07-31)
- The meetup-update skill's `references/newsletter.md` is the canonical newsletter playbook; follow it over memory or improvisation (2026-07-31)
50 changes: 50 additions & 0 deletions .ai-sessions/session-20260906-1718-meetup-dedupe-resend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Session Summary: Meetup.com Dedupe and Announcement Resend

**Date**: 2026-09-06
**Duration**: ~45 minutes (same conversation as the October wrap-up)
**Conversation Turns**: ~15 in this stretch
**Estimated Cost**: moderate (GraphQL inspection plus webhook resends)
**Model**: Claude Fable 5

## Key Actions

- Diagnosed duplicate October meetup.com events: two full network events existed (script-created 316447876, network 342e398b; dashboard-created 316448124, network f4f79e3f), each fanned out to all five network groups
- Mason chose to keep the dashboard event; deleted 316447876 via the deleteEvent mutation and confirmed its public page 404s
- Added a Meetup.com event line to both announcement templates in `build_announcements.py` (new required field `meetup_event_url`) at Mason's request
- Resent both Discord announcements (Mason had deleted the originals) with the surviving meetup.com link, merged PR status, fresh card export, and a trimmed Still-manual list
- Earlier in the conversation: sent the initial marketing and organizers announcements after Mason published both forms (a form-settings 401 had to be fixed first) and posted the website-updated follow-up after PR #55 merged

## Prompt Inventory

| Prompt/Command | Action Taken | Outcome |
|---|---|---|
| "Were the discord messages sent?" | Explained the deliberate hold on unpublished form links | Mason published the forms |
| "Read the google doc back" | Verified run of show; caught both responder links returning 401 | Mason fixed form audience; links went public |
| "Fixed, try again" | Verified 200s, rebuilt payloads, sent both announcements | Delivered with card attached |
| "I merged the PR... send the link" | Verified deploy live, posted website-updated follow-up | Done |
| "I feel there's duplicates" | GraphQL inspection found two parallel network events | Mason picked the keeper |
| "Resend the entire message with the updated meetup link" | Deleted the duplicate, added template line, resent both | Done |

## Efficiency Insights

**What went well:**
- Verifying responder links with an unauthenticated fetch before sending caught the restricted-audience default that would have shipped a login wall to Discord

**What could improve:**
- The duplicate came from creating the network event both via the script and the pro dashboard in the same cycle; the organizers message should state loudly that the meetup.com draft already exists and only needs publishing

**Course corrections:**
- "I deleted the old one" turned out to mean the Discord messages, not the meetup event; both events were still live until verified and deleted explicitly

## Process Improvements

- Before sending announcements, verify every link in the payload anonymously, not just the form links

## Observations

- Google Forms' new publish flow defaults responders to the org audience; forms.gle links return 401 until Responders is set to "Anyone with the link"
- The pro dashboard's network-events view does not list network events created through the API filterId path, which is how the duplicate went unnoticed

## Suggested Skills for Next Session

- `meetup-update`: November cycle; the outreach drafts for Sridhar Irujolla and Kriti Faujdar may have replies to process by then
3 changes: 3 additions & 0 deletions .claude/skills/meetup-update/scripts/build_announcements.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"discord_event_url",
"website_pr_url",
"website_pr_status",
"meetup_event_url",
)

MARKETING_TEMPLATE = """\
Expand All @@ -69,6 +70,7 @@
* [Attendance form]({attendance_url})
* Questions: in chat tonight
* [Discord event]({discord_event_url})
* [Meetup.com event]({meetup_event_url})
* [RSVP]({rsvp_url})"""

ORGANIZERS_TEMPLATE = """\
Expand All @@ -77,6 +79,7 @@
* [Run of Show]({run_of_show_url})
* Card: [{deck_name}, page {deck_page}]({deck_url}); image attached, or [download the PNG]({card_download_url}) (link expires {card_expiry})
* [Discord event]({discord_event_url})
* [Meetup.com event]({meetup_event_url})
* [Attendance form]({attendance_url})
* [Website PR]({website_pr_url}) ({website_pr_status})"""

Expand Down
6 changes: 3 additions & 3 deletions secrets/meetup.sops.env
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ PYTEXAS_MEETUP_WEBHOOK=ENC[AES256_GCM,data:o0eUcyJ8464OqJwnwJwfkBshaliya/9s62tRi
MAILCHIMP_API_KEY=ENC[AES256_GCM,data:iZo1IvkKpsrv7vAhtu3E3DqFuTGe+nR4eTcwKEpZ1byqWbGsAQ==,iv:tai6OvFB2i26xMpx5vRqdL5Z0Tv5fngcDzw6B1QkLlI=,tag:qmh1lqlndVPbWp/bnsDH6g==,type:str]
MEETUP_API_KEY=ENC[AES256_GCM,data:+2sPa/R+VSOtOHioho9c2cKq/3BDA5NSVXA=,iv:SnJCta7ujkSqFDZqkaSsr11pEK253ESqY/U6ePqIs34=,tag:oIUmg68ZjbucVKh6OTYnpg==,type:str]
MEETUP_API_SECRET=ENC[AES256_GCM,data:idZPq0DEvxAlCg38YQ4vHUSGIFI40zZNgDk=,iv:OtAFPf6bZVJt/hOP0JyIeG1TDLnA09HZRdXWuu321ro=,tag:BkAHzj5jr1zQP/5puOntxg==,type:str]
MEETUP_REFRESH_TOKEN=ENC[AES256_GCM,data:JqHX5fKxW3rSBYIH8s+wvpqMoAGZ93lSHxa3RpwnfbJ1fPgNnWOdoMNmmUm9p5Ox20nrJakmHJrZmFaR8TwvnzzLcKzbZXp7rMPDOZiVkwKVCXn9nQcHYuXFXQCAd1qiEdgy7RCOLkU7bUogyTUuJ9xjxjhTxUqipux7QaKXw9dT8MpCLgF+GszbWdVFJNvX41CS13U7+jwuWc28HlTc2UXyW+Hu9/v5LZrYYIxpYjdup6dS0Q+UAgzHimRl+ZO+ONJ+bqA+z6e1ePHz2xvO4B1Rjgnr+71Ldymbh2Iq5gvWi2vBiuFMqIsJ+Kzv75gzKZn5wmRmLSk8QZ78GSpANd7Ye1C33JZY/zWioWGcpkYO26X44ZyKNDoOTB5EquGyki7s8S2Sb/k33HuqXV/hvAKHtDv3z2I0P9GyPgVR32uMPWwSTM4Mv8ZQmWTz+HyDU+hXGGXa0aFo+FYxBrjAM8wftRARzFDrHBBk17RSwCjMQQ69JMpKd309ZJa546BKkZdH48+nOGV+1LgwnClJKIPk20J8b/e6yquEfIMrxXglN7lHQXe65vLaHx2K0+qVmJOtZ0MexIShPifDMqILMt23FWkhUm0ztjvPIs0sB+KML3TMMM2BZ6SOs/tYpTg08HRnoQ5UkPNTYKbgSmdLull/oaZL72nkGG6I8twWISOzqS8zrkRIJte01DclbcTECePrJwnCTZKJ4Iu+L97mKZbqN1+IjuO2egRWi3Yv4NftAMAoH3Sh684m/pjSNVHJ8No6iVTkx5Iay4MC4EvupTDZ57PQNbchYBHqxpxoeMq492z2j73+h0kO3IoSRgEUydwdVw==,iv:sinQpTmcUq61pqpcAOAz+qKfPq3YPqYGER2yjd4czKQ=,tag:6R0+dd6EJE+Wb8VoNTDkyg==,type:str]
MEETUP_REFRESH_TOKEN=ENC[AES256_GCM,data:V+JPfZpJxi8oJMWIH6HqNgoS1IfwHku8A3qJA8KPo6TKVa2aNd2ckVevOsDYZFnu6J84O131oFGqXNuXaoxBqPMUlvEs3Wg5tuQvFGEWcIxJayvjgwEF7E1csJTI2770epM637/R9yY2lclXTEijRSBETgLYWHybdJep4jsmJihjy8BR5hX8T6HsWqCeXkWavTf9DS5DKBCmiQ1+wvnDsCMmqAAAk/ocbtFo6+TvI86IbwVsK5zkj/vJEwKm969X6NZZYvdIWEuBq3NmO4RuqBEsd0osdK6ikpAZ+Ck3RlNBxZy+PK2T7sNXMxtrU1oIKlkJ4jsafjgvTt2RdIHTUv3JwWzcM3CRsZVFJOq63DV0cXntvB9o70/XpJ5ZssII28X3sLzY3sgFqUBQD3LEDhC+E+ikCg7u2P8586cVzqxPrRI/S5T+eV3sUTa7OQSfN+mFzBbDaq6cmppjCC1tGFTA0TCQEN76iHDrsQwqSQyav0NMQLPl/L0MaNHk5xP0oihtUOLH9HJrWEbFMz9TSKpBPmGUW2fDTKEc3N/lHmtGoLoBihzmnBEG7QiIhD/2WGPctf6OOeN+D1S6bKF3DOt29z3b0dbxlXXBRtUsDw3J29dTOTi4rwJZuVmcc6EbdSNiPr0MGSj2rP8I+FDXEEC27lER3Q55zksqx/I5KPm2IvVLg+9hkrgbisBbNgBsnkF1zosxUcxAKys2d+Cqr+tlapNeuOTx5UsK6xjHE/Y7iQLz+JrxAbE2/DIIGQWA1smvyeYF2xf24U2TcKcgB5Z+rHL+dyFrO+cqukNX0ZUdinKxyfGyW/CejYSGAN13bsYbhA==,iv:kveheAFQQDAkiDbhK5FYcj2nAcQaJbbtq6jnjejuLCg=,tag:Bdr+KOvT1LvI8A3hCxdgtg==,type:str]
PYTEXAS_DISCORD_BOT_TOKEN=ENC[AES256_GCM,data:F0JvIvALzRUF6jrnkhtUPEKYb7HXdpKTEcg3sKY3Xkxf2L9H3i5OSKfgyfE6upWjNdPQKZu4jqJaxKw709Wvdg+pMNFfR69B,iv:tN2srDHO9bCJEW+tc7nl4zDbKkcf7a0DE01nw6Jmq6g=,tag:JERscJ5CEEbdyBC3A2WNBw==,type:str]
sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvTlczR3puNFJOa2dJN0hS\ncVp5Tzk1K1ZHMDNIZFJIZG5WM0ordW00MWg4CjFCdWJvWmlDcmlkQ05zZFFqQlVR\nN3RHQzkrbnRteHZYbFM0NzJ3Si9qQlEKLS0tIHIxLzluZ3JCa0dSWG13N2dlYTVn\nMFpycUNHZ3p6SXpzdFRvL3VxeDlOUmsKvgfZuOwDCq+z/q+4m+TVNF2sMKpKZ4zs\nTQS17ua+xa6rgRAk2eG3PBp7GRKAhnGUV5WfA9++TU2O3CKmb6RGww==\n-----END AGE ENCRYPTED FILE-----\n
sops_age__list_0__map_recipient=age17u5zkzgr4ca49sl9n42zk6aruq8d5xntnuxkhyjwvku80tx93afqm7a0fz
sops_lastmodified=2026-09-06T19:37:06Z
sops_mac=ENC[AES256_GCM,data:E1RSpTv75GzlAPj5mawz7qNNhwJu79M/ovrN3niV3qipgOIHsesvsXe1jpWod9JEOPNrXlbIk2C1V0jlL0Cn0WOBjFjMtqF7ES/lLeQ7BCAvtX7gEmf9NW9H8kSdROOKdZcsRJWP0Gb984mLEwxPirQ6oFP75hcga9/FQGPfNuo=,iv:i8O49khFw8Yd3kGj/88zS1YD3/D1JanBMMUHPC3WEfo=,tag:1bnoPDdVh39zXS6WiU98pA==,type:str]
sops_lastmodified=2026-09-06T22:17:11Z
sops_mac=ENC[AES256_GCM,data:qmjfb+lgwMd0glGOlFddayjeFk9kSL4YuftwI7BNkuFv241voqrGMNP3HPgrRkiRuD/LBtEqzySVP1/dPdVNzU7J+Ov+Yfw90TgoBfF6ZTeXpLfpGJaZykUlRsnP/hhTtoWxjj2bCHY2xK3zgIbh/lF4uOuJNmkOiYPs9rFoGlE=,iv:xcxeuN8DMEJ4Eq6GMvZkZrqsv0t+AtP1L+pzIbhCCIw=,tag:qkOShtLIYXwvUXhn8HrZ4g==,type:str]
sops_unencrypted_suffix=_unencrypted
sops_version=3.13.1
Loading