AI 资讯
Planting a Future Breaking Change Today: A launchd Timer Job That Deletes Itself When Done
This is a follow-up to my earlier post, " Automating a config migration with a one-shot launchd job ." Some breaking changes come with a known expiration date, and you can prepare for them long before they land. This time the external event was the end-of-life of Fable 5 (2026-07-07), and I'll walk through how I designed a launchd job you set up today, that fires only on the target day, and that removes itself once it's done. The whole thing started with the thought, "manually fixing this on the shutdown day is going to be annoying." But I also didn't want to run a script every morning that needlessly rewrites JSON. What I landed on was a three-part set: a date gate, a jq rewrite with a backup, and self-unload. The problem: on the day I learn about a deprecation, I want to plant a job that "only runs on the target day" Right now, ~/.claude/settings.json looks like this: { "model" : "claude-fable-5[1m]" , ... } The moment I learned Fable 5 would end on 2026-07-07, creating a calendar reminder to manually rewrite this "model" felt too flimsy — I'll forget. On the other hand, making "a daemon that checks the date every time it boots" is overkill. What I wanted was a job I could set once and leave alone, that runs when the day arrives, and then disappears. launchd can fire at a specified time via StartCalendarInterval . But you can't express "just once at 9:00 on 7/7"; you need a combination of recurring and date-fixed slots. Specifying multiple slots and absorbing the redundancy with idempotency is the standard trick on macOS launchd. The implementation: the three-part set Here's the full ~/.claude/scripts/model-transition-0707.sh (comments omitted). #!/bin/bash set -uo pipefail SETTINGS = " $HOME /.claude/settings.json" LOG = " $HOME /.claude/logs/model-transition.log" PLIST = " $HOME /Library/LaunchAgents/com.shun.model-transition-0707.plist" log () { echo "[ $( date '+%F %T' ) ] $* " >> " $LOG " ; } # ① 日付ゲート if [ " $( date +%Y%m%d ) " -lt 20260707 ] ; then log "ski
AI 资讯
# Reflection – Week 2
" Shifting from Prompt Engineering to Infrastructure Orchestration " Week 2 was a mix of excitement, curiosity, and a little bit of frustration. I learned a lot of new concepts, but I also realized that the best way to understand them is by actually trying them out. Reading or watching tutorials helps, but experimenting with the tools made everything click for me. One of the topics I enjoyed learning about was Claude Code. Before this week, I mainly thought of AI as something that answers questions or helps write content. Seeing how Claude can assist with coding, debugging, and understanding projects made me see it differently. It feels less like a search engine and more like someone you can work with while building something. That really changed how I think about using AI in development. Another interesting topic was Skills. I liked the idea that you can give an LLM specific skills so it behaves more like a specialist instead of a general assistant. It made me realize that the quality of the output doesn't only depend on the model itself, but also on how you guide it and what tools or skills you give it. That was something I hadn't really thought about before, and I can already see how useful it could be for different types of projects. I also learned about Subagents, which was a new concept for me. At first, I didn't really understand why you would need multiple agents instead of just asking one AI to do everything. But after learning more about it, I started to see the benefit. Having different agents focus on different tasks seems like a much cleaner and more organized way to work, especially for bigger projects. The biggest challenge I faced this week was running out of tokens while practicing. It happened a few times, and honestly, it was a little annoying because I would be in the middle of exploring an idea and suddenly had to stop. Even though it was frustrating, it also made me think more carefully about how I write prompts and how I use my conversations.
AI 资讯
How Datadog Used Claude and Cursor for Test-Driven Production Migration
In a recent article, Datadog engineer Arnold Wakim shared what worked, what didn't, and the lessons they learned while evolving a critical production system using AI to overcome hard limits in its storage backend and significantly improve performance. By Sergio De Simone
AI 资讯
Claude Code vs. Codex: Which AI Coding Assistant Is Better?
Artificial intelligence has transformed software development. Instead of simply generating code snippets, modern coding assistants can understand entire codebases, refactor applications, write tests, debug issues, and even execute development workflows. Among the most capable tools available today are Claude Code and Codex. While both are designed to accelerate software development, they take different approaches to coding assistance. This article compares their strengths, weaknesses, and ideal use cases. What Is Claude Code? Claude Code is Anthropic's command-line coding assistant built around the Claude family of language models. Rather than functioning as a traditional autocomplete tool, Claude Code works as an AI development agent that can inspect projects, edit files, explain code, write tests, fix bugs, and help developers navigate large repositories. Its workflow is centered around natural language. Developers describe what they want, and Claude Code performs the necessary steps while keeping the developer involved throughout the process. Key features Deep understanding of large codebases Multi-file editing Test generation Refactoring assistance Terminal-based workflow Strong reasoning for complex programming tasks Excellent documentation generation What Is Codex? Codex is OpenAI's AI coding agent designed to help developers write, understand, and modify software. Unlike the original Codex model introduced in 2021, today's Codex operates as a software engineering agent capable of working across repositories, generating code, fixing bugs, creating pull requests, running tests, and assisting with development workflows. Codex integrates closely with OpenAI's ecosystem and focuses on turning natural language instructions into production-ready code while maintaining awareness of project context. Key features Repository-aware coding Autonomous task execution Code generation Bug fixing Test writing Pull request assistance Integration with modern development workflow
AI 资讯
Anyone else noticing Claude being more stubborn, lying to you with high confidence that things the way he says to find out it's complete non sense?
AI 资讯
How to Create a Skill in Claude Code
This is a cross-post — the original (and any updates) live at broke2builtai.com . The first time I watched Claude Code reach for a skill I hadn't told it to use — read a folder, run the script inside it, and hand back the finished thing — the difference from a slash command finally landed. A slash command waits for you to type it. A skill waits for the situation . Claude decides. That one shift is the whole feature, and building one takes about five minutes once you know where the file goes. Here's the entire thing end to end, including the one gotcha that decides whether your skill ever actually fires. What a Skill actually is A Skill is a folder with a SKILL.md file inside it. The Markdown holds instructions; the YAML frontmatter at the top holds a name and a description . That description is doing the most important job in the whole file: Claude reads it to decide, on its own, whether the current task warrants invoking the skill. Nothing else you write matters if the description doesn't get you picked. That's the mental model to hold onto: a custom slash command is a prompt you trigger by typing /name ; a skill is a procedure Claude triggers when the context matches. Same reusable-instructions idea, opposite trigger. Where the file goes Two locations register, exactly like commands and subagents : Project skill — .claude/skills/<skill-name>/SKILL.md inside the repo. Committed, so your whole team gets it. Personal skill — ~/.claude/skills/<skill-name>/SKILL.md in your home directory. Follows you across every project on your machine. Each skill is its own folder, and the folder name should match the name in the frontmatter. A loose SKILL.md sitting somewhere else won't be picked up. The minimum viable skill Create the folder and the file: .claude/skills/pytest-runner/SKILL.md Then write the two-part file — frontmatter, then body: --- name : pytest-runner description : " Run, generate, or debug pytest tests for this project. Use when the user asks to run the test su
AI 资讯
Anthropic Wants You to Pay Up for Claude Fable 5
Claude subscribers must soon pay usage-based fees to access Anthropic’s best consumer AI model—a sign that the golden era of AI subscriptions is ending.
AI 资讯
RLS recursion infinite loop: why I gave up policies and bet everything on a JWT custom claims hook
Episode 1/4 — 3 incidents, one root: default GRANTs open more than you think — [CANONICAL URL EPISODE 1: fill in after push] Episode 2/4 — await mutation() lies when nobody opens the { error } envelope — [CANONICAL URL EPISODE 2: fill in after push] The morning Françoise sees zero rows, again It's a Tuesday in April 2026. I've just added the agent_readonly role to the authenticated membership — a one-liner, meant to share a GRANT for a reporting job. First SELECT on cours , Sentry receives infinite recursion detected in policy for relation "user_roles" , code 42P17 . From the office next door, Françoise is already on the phone with the Maisons-Laffitte branch: "So they can't see anything over there — is that normal?" Foreman tone, not really a question. I read the error on my screen. The difference from episode 1: this time Postgres is talking. What came out of Sentry was no longer a silent empty set — it was an explicit error. That difference saved me two days. When Postgres shouts, you listen. The trap is that what it says isn't where you're looking. I won't pretend this is obscure. A policy on user_roles that queries user_roles to decide who can read user_roles is a loop. You avoid it, you work around it with SECURITY DEFINER , you move on. The problem: my user_roles policy didn't reference user_roles . I had already cleaned it up three weeks earlier. The recursion was coming from somewhere else. The diagnostic that targets the wrong object First reflex: re-read the user_roles policy. It's clean, reads auth.email() , never calls itself. Second reflex: disable policies one by one to find the culprit. Wrong angle. -- supabase/migrations/20260420_admin_write_cours_v1.sql -- "Admin write cours" policy — original version that loops CREATE POLICY "Admin write cours" ON public . cours FOR ALL TO authenticated USING ( EXISTS ( SELECT 1 FROM public . user_roles WHERE email = auth . email () AND role IN ( 'admin' , 'super_admin' ) ) ); The recursion doesn't come from a fau
AI 资讯
The PostgREST query that silently ORDER BY ctid: a Supabase week, distilled
The fourth call of the week Catherine calls from the Maisons-Laffitte site on a Tuesday afternoon in early May. "It's broken, but it's a quick fix." That's her line — I know it, and she's usually right. She describes it in three sentences: the newsletter export for the enrolled-students segment comes back with ninety-two names, the planning view shows ninety-two active courses, but the counter page shows eighty-nine. Three enrolled students missing. She'd checked the database directly — they're all there. "Why three steps for that?" She's not asking for my benefit. She's asking for herself. Except this time, hanging up, I realize it's the fourth time this week I've hung up thinking the same thing. Four Supabase incidents, four fixes, four closed tickets. And not a single exception raised by the database. I reopen the three previous ones and lay all four side by side on screen. This isn't four bugs. It's one failure mode, declinated four times. The first three Episode 1 was about the default GRANT s Supabase places on functions and policies. A SQL function created without an explicit REVOKE inherits anon access that nobody wrote in the migration, and that nobody caught in review because the diff doesn't show it. The function works. It's just callable from outside. [CANONICAL URL EPISODE 1: to fill in after publication of #48 — "3 Supabase security incidents, one shared root cause: SECURITY DEFINER inherits EXECUTE TO PUBLIC"] Episode 2, an ON DELETE SET NULL cascade coupled with a CHECK NOT NULL on the target column. The parent DELETE attempts the SET NULL , the CHECK rejects it, and the transaction surfaces an error we read as a deletion failure — while it actually masks a consistency assumption we'd held for three months. The query fails loudly, which is more charitable than the other three cases, but the diagnosis heads in the wrong direction because nobody had declared that the two constraints lived in tension. [CANONICAL URL EPISODE 2: to fill in after publicati
AI 资讯
Why your agent over-engineers your simplest request (and the 3 prompts that stop it)
The request was eight words Monday morning. I open the outgoing email queue: six hundred and forty-seven drafts waiting, six hundred and seventy-two sent. Nobody clicks Send . First-contact emails are prepared by a pipeline and they sleep, because the last step assumes a human. That human, I had stopped believing she would have the time. I state the decision: automate sending . The response comes in seconds. Three levels of automation. Four channels. Three risk thresholds. All correct, all fit for a half-day architecture workshop. I had not asked for a workshop. Pauline walks behind me, glances at the screen, says nothing. Three timed reframes First reframe , brief: too strange, let's simplify . The agent drops two axes, keeps four residual layers, progressive warm-up over three weeks, deterministic anti-replay hash, configuration table in the database, manual Phase 1 followed by an automated Phase 2 to validate after two weeks of measurement. The target stays the same, that an email leaves without a human click. The path has grown accordingly. Second reframe , drier: simple, three safeguards, a kill-switch, we do this in one day . The agent re-architects, accepts the one-day target, keeps the three safeguards. But slips in three prostheses it calls industry standard : real-time dashboard, exponential retry, structured audit log in a new table. Each justifiable in isolation. None of them requested. Third reframe , shorter still: I don't understand why you're adding this . An opening line almost embarrassed, which I had never read from it before: "you're right, I'm over-engineering without necessity." And the version that should have arrived on the first round. A function that takes the draft record, checks three conditions, calls the send engine, returns. // lib/email-outbox.ts — generateFirstContactDraft (commit 3756e63) if ( ! EMAIL_REGEX . test ( input . email )) { return { success : false , error : ' email_invalide ' } } if ( BLACKLIST_EMAILS . has ( input . ema
AI 资讯
Anthropic’s new Claude feature is quietly selling you on AI
Claude’s new Reflect dashboard doesn’t just visualize how you use AI. It also subtly reinforces how much of your daily work now depends on Anthropic’s chatbot.
AI 资讯
LED Strip Tetris: Zero-Code Hardware Game with TuyaOpen + Claude Code Tutorial
I built an LED Strip Tetris game — without writing a single line of code. No keyboard mashing. No debugging at 2 AM. No reading 500 pages of datasheets. Just natural language prompts, an AI agent, and a Tuya T5 AI Core board. Here's the full breakdown of how it works 👇 🧩 What Is LED Strip Tetris? LED Strip Tetris is a DIY hardware game built entirely through natural language prompts using TuyaOpen IDE and Claude Code. It runs on a Tuya T5 AI Core development board with a WS2812 LED strip (72 LEDs) and three color-matched buttons — red, green, and blue. Colored LEDs fall from the top of the strip; players press the matching button to shoot a colored LED upward and eliminate the falling one on contact. The entire game — firmware, game logic, hardware wiring, sound effects, compilation, and flashing — was generated by AI. Zero manual coding. 🔌 The Hardware (Ridiculously Simple) Component Role Tuya T5 AI Core Board Main MCU — runs game logic, drives LED strip and buttons WS2812 LED Strip (72 LEDs) Display — colored LEDs fall and get eliminated 3 Push Buttons (Red / Green / Blue) Input — shoot matching color upward to clear falling LEDs Speaker Sound effects on button press That's it. No custom PCB. No complex wiring harness. Just four components plugged into a dev board. 🤔 Why This Is a Big Deal Here's what building a hardware game normally looks like: Step Traditional Approach Vibe Coding with TuyaOpen IDE Dev environment setup Install toolchain, configure SDK, fight dependencies Copy a workflow link, paste into Claude Code, click confirm Game logic Write C code from scratch, design state machines Describe the game in one sentence, AI generates the code Hardware config Read datasheets, look up GPIO mappings, manually configure Tell AI which pins you're using, it handles the rest Sound effects Write audio decoding code, integrate codecs Give AI the file path, it decodes and compiles Debugging Serial logs, oscilloscope, hours of trial and error AI self-diagnoses compile
AI 资讯
Anthropic Shipped @Claude For Slack. My Team Runs On
Anthropic Shipped @claude for Slack. My Team Runs on Telegram. Anthropic just shipped @Claude inside Slack channels. Tag the bot, it reads the thread, does work async, posts back. Nice product. Except roughly 95% of small businesses don't live in Slack — they run on WhatsApp, Telegram, and Gmail. If you're a solopreneur or a 1-to-10-person team, here's the exact four-part recipe I use to run the same pattern in Telegram for under $12/month. What Anthropic actually shipped (and who it's for) Anthropic shipped an enterprise distribution deal wearing a product launch t-shirt. @Claude for Slack lets you tag the bot in a channel or thread, gives it channel memory, connects to your other apps, and returns work asynchronously — but only on Slack Team and Enterprise plans. That's the punchline: it lives where the annual contracts live. Look at the raw user counts. Slack's own reporting puts it around 35–40 million weekly active users globally. WhatsApp is over 2 billion. Telegram is over 900 million. Gmail sits around 1.8 billion. In the 1-to-10-employee segment outside US tech, Slack penetration is single digits. Small teams in Europe, LATAM, and most of Asia coordinate in WhatsApp groups and run pipeline out of Gmail. They are not about to add Slack seats at $15/user/month just to get an @Claude mention. That's a rational call for Anthropic — Slack is where the enterprise procurement motion already exists. It's just not a product for the operator segment. And the pattern they productized is trivially replicable on any messenger with a bot API. Platform Weekly/monthly active users Bot API Cost to run a mention-bot Slack ~35–40M WAU Yes, paid plan $15/user/mo + API Telegram ~900M MAU Yes, free ~$5–12/mo API only WhatsApp Business ~2B MAU Yes, metered $0.005–0.08/conversation + API Gmail ~1.8B MAU Pub/Sub push Free tier + API The four-part recipe (works in any messenger) Every mention-bot is the same four moving parts: a webhook that fires on mention, a context store that ho
AI 资讯
Keeping context and decisions consistent across parallel AI agents
You start the morning with four Claude Code agents running, each in its own git worktree, each on a separate task. By mid-afternoon something is off. One agent has re-implemented a helper another already wrote. A second built against an interface that a third changed an hour ago. A fourth made a naming choice that contradicts a decision you made — out loud, to yourself — at 9am. Every diff is reasonable on its own. The system they add up to is not. This is the failure mode that shows up the moment you go from one agent to several. The code each agent produces is fine. What drifts is everything between the agents: the decisions, the conventions, the current shape of the interfaces they all depend on. Running the agents in parallel is the easy part. Keeping them coherent is the hard part, and it's a different problem. Why parallel agents drift An agent's context is per-session. Each Claude Code instance has its own context window, populated by what it has read and done in that session. Nothing about that window is shared with the agent running in the next worktree. There is no common memory they all write to and read from. So when agent A decides "we use the repository pattern for data access," that decision exists in exactly two places: agent A's context, and your head. Agent B never hears about it. Three kinds of state cause the drift, and they're worth separating because they need different handling: Decisions already made. Architecture, naming, conventions, the approach you settled on for a cross-cutting concern. These are durable — once made, they should bind every agent, including ones you spawn tomorrow. The current contract. The shape of the interfaces, types, and APIs that agents share. This changes during the work: agent A edits a signature, and agents B and C are now building against a version that no longer exists. What's in flight. Who is touching which files right now. Two agents editing the same module in separate worktrees won't see each other until th
AI 资讯
Claude Cowork expands to mobile and web
With this update, users can start a task from their desk, get status updates on their phone, and pick up the finished output later — even if their laptop is closed.
AI 资讯
Shut Those Laptops! Anthropic Puts Its Claude Cowork Agent on Your Phone
Claude Cowork now keeps working on tasks even after you close your laptop. It’s part of a larger push toward smartphone-controlled agents.
AI 资讯
Why AI code review hallucinates — and the two gates that fix it
CCA-Audit — open source (MIT) AI code review has a trust problem, and it's not that it misses bugs. It's that it invents them. If you've run an LLM over a diff, you've seen it: a "possible null dereference" on a value that's guarded three lines up. A "SQL injection" your ORM already parameterizes. A "race condition" that can't happen. And then — worse — it confidently rewrites working code to "fix" the thing that was never broken. The real bug, meanwhile, sits quietly in the noise. The problem isn't intelligence. It's that most AI reviewers report their first impression as a verdict. A model reads a diff, pattern-matches "this looks like X," and emits a finding — without ever going back to check whether X is actually reachable in this code. Humans do a second pass ("wait, is price validated upstream?"). Most AI-review pipelines skip it. Here are two gates that add that second pass — and a stress test showing what they catch. Gate 1: verify findings before you fix (anti-hallucination) The idea is simple: no finding is allowed into the fix plan until a separate step re-checks it against the real code. After the auditors produce findings, a verification pass takes each one and asks three questions: Does the issue actually exist at the cited line? Is it in the code that changed, or a pre-existing thing outside the diff? Is the stated impact real, or already mitigated elsewhere — a guard upstream, a value validated before this point, a config defined in another module? The key design choice: bias the verifier toward refuting. A wrongly-confirmed finding causes a needless (sometimes harmful) fix; a wrongly-dropped one is cheap to recover. So when the evidence isn't clear, drop it or escalate to a human — don't fix on a hunch. This one step kills the majority of hallucinated findings, because hallucinations rarely survive contact with "show me the exact line, and prove the impact can occur." Gate 2: prove the fix maps to the finding (anti-regression + provenance) Catching
AI 资讯
How to Set Up Claude Code for a Project with Skills, Agents, Hooks, and a Secure GitHub Repository
How to Set Up Claude Code for a Project with Skills, Agents, Hooks, and a Secure GitHub Repository AI coding tools work best when they understand the project around the code. A fresh Claude Code session can answer questions and edit files, but it does not automatically know your architecture decisions, coding standards, security expectations, testing rules, pull request format, or operational constraints. That context needs to live somewhere predictable. This guide walks through a full Claude Code project setup using a reusable repository owned by desertfox33 : Reference repository: https://github.com/desertfox33/claude-code-project-template The goal is not just to create folders. The goal is to make Claude Code behave consistently across real project work: reviewing code, writing tests, preparing pull requests, checking security concerns, and following project-specific rules. Before using this setup in a production environment, verify current Claude Code behavior against the latest official documentation. Tooling, configuration names, and feature behavior can change. What We Are Building The repository uses this structure: claude-code-project-template/ ├── CLAUDE.md ├── CLAUDE.local.md.example ├── AGENTS.md ├── .mcp.example.json ├── .gitignore ├── SECURITY.md ├── CONTRIBUTING.md ├── .github/ │ ├── CODEOWNERS │ ├── dependabot.yml │ └── pull_request_template.md ├── .claude/ │ ├── settings.json │ ├── settings.local.json.example │ ├── rules/ │ │ ├── code-style.md │ │ ├── api-conventions.md │ │ ├── testing-standard.md │ │ └── pr.md │ ├── commands/ │ │ ├── review.md │ │ ├── deploy.md │ │ ├── scaffold.md │ │ ├── test.md │ │ └── pr.md │ ├── skills/ │ │ ├── code-review/ │ │ │ ├── SKILL.md │ │ │ └── review-checklist.md │ │ ├── testing-patterns/ │ │ │ ├── SKILL.md │ │ │ └── test-strategy.md │ │ ├── pr-description/ │ │ │ ├── SKILL.md │ │ │ └── template.md │ │ └── security-review/ │ │ └── SKILL.md │ ├── agents/ │ │ ├── security-reviewer.md │ │ ├── test-writer.md │ │ └── researc
AI 资讯
I Got Tired of My Portfolio Looking Like a List of Links. So I Built an MCP Server for It.
The obvious fix for "my projects all look similar" is a better README — more screenshots, clearer descriptions, maybe a comparison table. I considered that for about five minutes and decided it was still just a nicer list of links. What actually made a portfolio project feel different was making it something you could talk to instead of read. That's what MCP (Model Context Protocol) is built for — it's the standard that lets AI clients like Claude Desktop call external tools directly, not just process text. So I built a server that exposes my 9 projects as queryable tools instead of static entries. What is MCP, and why does it matter here Almost every AI-developer portfolio I've seen is a list of links. Mine now includes something you can actually talk to . Open Claude Desktop, connect my server, and ask "what has Ayush built with FastAPI?" — it doesn't guess from a cached README, it calls a real tool and answers from structured, live data. What I built A Python MCP server ( FastMCP , stdio transport) exposing five tools: list_projects — short summary of all 9 projects get_project_details(project_name) — full stack, GitHub link, demo URL for one project, fuzzy-matched by name search_projects_by_stack(technology) — "show me everything using Groq" or "LangGraph" or "React" get_flagship_project — the single best project to look at first get_resume_summary — background, target role, core stack The data itself lives in plain Python dictionaries right now — no database needed for something this size. Each tool is a thin function around that data, decorated with @mcp.tool() . @mcp.tool () def search_projects_by_stack ( technology : str ) -> list [ dict ]: """ Find all projects that use a given technology or tool. """ query = technology . lower (). strip () matches = [ { " name " : p [ " name " ], " stack " : p [ " stack " ], " github " : p [ " github " ]} for p in PROJECTS if any ( query in tech . lower () for tech in p [ " stack " ]) ] return matches or [{ " message " : f
AI 资讯
What 74 ADRs in 70 days actually buy a solo dev (no hire, no clients, just the file)
The question you don't dare ask out loud It's 10:40 PM on a Tuesday, I just closed an ADR — the seventy-fourth in this setup, written conscientiously, dated, cross-referenced with its migration, its contract test, and the commit that triggered it. And the question rises, the way it always rises at that hour when you've been coding alone for ten hours: who did I just write this for . No tech lead to convince, no PR review that'll catch it, no hypothetical acquirer to reassure, no architecture committee to brief tomorrow. Just the file, just me, just the doubt. It's the question of a solo dev at 70 days of serious practice. It has an honest answer, and that answer is neither "it'll pay when you sell" nor "it'll pay when you hire". Those two ROIs belong to other trajectories. The ROI of the solo dev who documents is an ROI he buys himself — deferred, intangible at moments, but materially countable if you force yourself to measure it in the first person. Here's mine, over 74 ADRs and 18 doctrine rules accumulated in 70 days, with no external observer to validate the grid. The false economy of "I'll remember" First trap, the one that cost me three weeks before I learned the lesson. The solo dev believes he doesn't need to write down what he decided because he decided it himself — his memory is worth an ADR. False at 14 days, systematically false at six weeks. Not because general memory fails, but because technical memory has a deceptive shape: you remember perfectly that you decided , you no longer remember why you decided that way. Three weeks after the May 5 session where I wrote ADR-0051 (FK ON DELETE SET NULL + CHECK NOT NULL incompatible, DELETE failing silently), I reopen the migration to add a column. I reread the diff, I don't understand why a certain CHECK constraint is phrased like this — the alternative I mentally dismiss today seems simpler, and I'm two clicks from refactoring. I go check the ADR. The answer is there, dated, sourced, in three lines. The simpl