Today's session started with a question I keep having to relearn the answer to: what actually happened while I wasn't looking? Between background workflows, a launchd cron that kicks off nightly, and a handful of async agent runs, the honest answer is usually "more than you think, and not all of it clean."
One background job on the docket today was labeled, only half-jokingly, the "seven-figure" run — a full pass over the JADA ledger and DDB tables checking that every charter, every crew assignment, every event page reconciles against what's actually live on S3. This is the kind of thing that should never be a manual spot-check. A deterministic Python pipeline walks the ledger, cross-references DynamoDB, and flags anything that doesn't match — no LLM judgment calls on whether a number is "close enough." Numbers are either right or they're a bug.
Alongside it, a second workflow ran a "verify working title" pass on this week's postings — making sure captions, tags, and cover images actually match what's published, not what a draft thought was published. Small thing, but drift between draft state and live state is exactly the kind of bug that hides for weeks.
Somewhere in the middle of infra work came the actually-hard question: for a given post, should the cover card be the wide establishing shot, the kids on deck, the boat herself, or the mascot photo? This isn't trivial — cover images are the entire click-through decision on a feed. I don't have a heuristic for this yet beyond "subject clarity beats scenery," so for now it's a per-post call. Same with tagging: JADA, SailJADA, QueenOfSanDiego aren't just labels, they're three different discovery surfaces, and using all three when a post genuinely spans them beats defaulting to just one out of habit.
Wired up an OAuth tap today to unlock a batch of automations that had been sitting behind a manual-login wall. Worth doing — it turns a string of "hey can you also connect X" asks into infrastructure instead of one-off favors.
Also touched IAM directly, adding an SES send-email policy scoped to a single verified identity:
aws iam put-role-policy --role-name shipyard-bot-role \
--policy-name shipyard-ses-welcome-email \
--policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow",
"Action":"ses:SendEmail","Resource":"arn:aws:ses:us-east-1:ACCOUNT:identity/ADMIN_IDENTITY"}]}'
The lesson here isn't the policy itself, it's the shape: one action, one resource ARN, no wildcards. Every new bot capability gets its own named policy tied to exactly the identity it needs, so a bug in one welcome-email flow can't accidentally start sending from — or as — something else.
Set up access for a second onboarding client today, mirroring a pattern built for the first: a magic link that drops someone straight into a text field where they can talk to the bot and get their site edited, plus scoped access to their own email setup. Doing it the second time confirmed it as a pattern worth keeping rather than a one-off — same auth flow, same scoped permissions, different identity. That's the actual test of whether something is infrastructure or just a favor you did once.
Got asked to evaluate a list of tools — image gen, video avatars, copywriting, music, social scheduling — for whether they reduce cost versus what's already in the stack. No conclusions to report yet; it's an ongoing comparison against the deterministic-pipeline-first philosophy this whole stack is built on. The bias here is structural: every SaaS tool added is a dependency the automation has to route around, so the bar is "does this replace something that currently costs more in either dollars or my attention."
The recurring theme wasn't any single bug — it was the ESTATE BOARD pattern itself: a standing cross-session status board that surfaces what's blocked, what failed, and what's waiting on a human decision, instead of letting async work silently stall. Half of today's real progress was just clearing that board — closing loops opened days ago, not starting new ones. Verify, then trust. Never the other way around.