diff --git a/apps/web/src/content/pages/blog/four-iterations-of-an-incident-timeline.mdx b/apps/web/src/content/pages/blog/four-iterations-of-an-incident-timeline.mdx index 9ad14be9f..ce3100904 100644 --- a/apps/web/src/content/pages/blog/four-iterations-of-an-incident-timeline.mdx +++ b/apps/web/src/content/pages/blog/four-iterations-of-an-incident-timeline.mdx @@ -7,7 +7,7 @@ category: "engineering" image: "/assets/posts/four-iterations-of-an-incident-timeline/04-hierarchy.png" --- -We've been building incident response into openstatus. The activity timeline is the part you stare at most during an incident, so it got the most iterations. Four phases until we got where we are now. I already see some inconsistent paddings, but the core is done and dusted to me. +We've been building incident response into openstatus. The activity timeline is the part you stare at most during an incident, so it got the most iterations. Four phases until we got where we are now. There are still some inconsistent paddings, but the core is done.
@@ -34,21 +34,21 @@ We've been building incident response into openstatus. The activity timeline is ## How I work with Claude -I'm riding Claude Code **raw**. No additional harness, no additional configuration. I like to keep my setup *simple*. It's easy to adapt, easy to switch, and it doesn't bind me to anything concrete. My terminal and settings look the same. I don't see me missing out on something. It evolves daily, and I'm sure I'm not using it to its full potential. +I use Claude Code **without a harness** or much configuration. A simple setup is easy to adapt and doesn't bind me to anything. My terminal and settings look like they always did. I'm sure I'm not using it to its full potential, but I don't feel like I'm missing something. For deterministic tasks and fixes I use Claude Code remote. It spins up a VM and works isolated from the rest. When I'm hands-down on UI, or I want to test and navigate the codebase, I run Claude Code in the terminal. No worktrees either. -I am **obsessed** with proper `PLAN.md` files to `/loop` over once I'm afk. I keep plenty of Claude Code tabs open, grilling me on different ideas with Matt Pocock's goated grill-me skill. I rarely read those plans extensively, except when it's a collaboration with Thibault on a bigger-picture feature like incident response. We worked on that plan for a couple of days, with the idea spelled out for weeks before. Leave it, iterate the next day. +I write a lot of `PLAN.md` files and `/loop` over them once I'm *afk*. I keep several Claude Code tabs open, each grilling me on a different idea with Matt Pocock's grill-me skill. I rarely read those plans in full, except when it's a collaboration with Thibault on a bigger feature like incident response. We worked on that plan for a couple of days, with the idea spelled out for weeks before. Leave it, iterate the next day. -For dashboard work, Claude Code runs Playwright against my local dev server and screenshots the page after every change. That's how it checks its own work. I do the same from my side: a screenshot, the broken part **boxed in red**, one sentence. While a loop runs I go bouldering or for a walk. When I'm back I test on localhost, not on a preview link. The details only show up when you click through the real thing. +For dashboard work, Claude Code drives a browser against my local dev server and screenshots the page after every change. That's how it checks its own work. I do the same from my side: a screenshot, the broken part **boxed in red**, one sentence. While a loop runs I go bouldering or for a walk. When I'm back I test on localhost, not on a preview link, because the details only show up when you click through the real thing. -When it comes to UI/UX, I use Claude Design for a first feeling. Without steering it looks very *claudy*, gradients and italics, so most of the time it's purely for inspiration. I usually ask for three or four variations. For dashboard work we have our design system attached to both. Claude Design gets it as a project asset, so the mocks come out in our tokens, fonts and spacing instead of the default look. Claude Code gets it from the repo: shadcn primitives in `@openstatus/ui`, our own blocks on top, and `AGENTS.md` files that say to compose from those and never fork a primitive into an app. Neither can go off the rails too much. But both stay rather plain if I don't give enough details. +For UI I use Claude Design for a first feeling. Without steering it looks very *claudy*, gradients and italics, so most of the time it's inspiration only. I usually ask for three or four variations. For dashboard work both tools have our design system. Claude Design gets it as a project asset, so the mocks come out in our tokens, fonts and spacing. Claude Code gets it from the repo: shadcn primitives in `@openstatus/ui`, our own blocks on top, and `AGENTS.md` files that say to compose from those and never fork a primitive into an app. Neither goes off the rails much, and both stay plain if I don't give enough detail. ## Phase 1: from the plan -Phase 1 came from the plan. Thibault ran Claude through it and we were gifted 21 stacked PRs to merge. And it was good. **Really good.** The incidents page was feature-flagged to our workspace, and we merged one PR after the other. Thibault focused on the Slack app, me on the dashboard. +Phase 1 came from the plan. Thibault ran Claude through it and we got 21 stacked PRs to merge. They were good. The incidents page was feature-flagged to our workspace, and we merged one PR after the other. Thibault focused on the Slack app, me on the dashboard. -The UI came out raw, but the plan was followed *to the letter*. The two-column layout, the composer above the timeline, the properties on the right, the linked status report: all of that is in the spec. The buttons, badges and selects are ours, because the design system is in the repo and every session reads it. What was missing is **hierarchy**. Every event rendered the same: a left border, Type · Name · time, body text. Notes from Slack showed up as a bare "From Slack" link. It worked. It also looked like a *log file*. +The UI came out raw, but the plan was followed to the letter. The two-column layout, the composer above the timeline, the properties on the right, the linked status report: all of that is in the spec. The buttons, badges and selects are ours, because the design system is in the repo and every session reads it. What was missing was **hierarchy**. Every event rendered the same: a left border, Type · Name · time, body text. Notes from Slack showed up as a bare "From Slack" link. It worked, and it looked like a *log file*. Phase 1: every event as a bordered block with type, name and time @@ -62,11 +62,11 @@ A few minutes later: The first Claude Design output: status pill and severity chip in the header, ghost inputs on the right, composer with a Set status to footer, icon discs on a rail -**Miles better** than what we had. Ghost inputs on the right, the status change in the composer footer, icon discs on a rail, status chips. Next, I pasted a screenshot of it into Claude Code: +**A lot better** than what we had. Ghost inputs on the right, the status change in the composer footer, icon discs on a rail, status chips. Next, I pasted a screenshot of it into Claude Code: > This image is really good. Take your time and figure out how we get there. Be thoughtful about the React components, the way we do it in general: structure, compound components, cva. Don't commit, implement until you believe it's good. The components will be reused for status reports and maintenances later on. -What came back is the mock in our components, minus what didn't survive the data model. The incident ID went, our IDs are autoincrement and "INC-011" would be a lie. Affected monitors went, the incident doesn't link to monitors yet. Good for the first real update. +What came back is the mock in our components, minus what didn't survive the data model. The incident ID went, since our IDs are autoincrement and "INC-011" would be a lie. Affected monitors went, since the incident doesn't link to monitors yet. Good enough for a first real update. Phase 2: a vertical rail with an icon per event type and status pills @@ -76,23 +76,23 @@ A minor visual step. I kept the core Claude Design returned and focused on the u > Notes show an author avatar and a type icon side by side. That's redundant. Make the avatar the timeline node, with the type as a small badge. -The first pass still rendered both. "There is redundancy, check again" fixed it. *More important than the avatars:* this phase migrated status reports and maintenances onto the same timeline, composer and aside components. +The first pass still rendered both. "There is redundancy, check again" fixed it. More important than the avatars, this phase migrated status reports and maintenances onto the same timeline, composer and aside components. Phase 3: the author avatar is the timeline node, the type a small badge ## Phase 4: hierarchy -The last big push. I shared the current visuals on X and Marc from [Papermark](https://www.papermark.com) pointed me to [Linear](https://linear.app). I've been using Linear and I know their great timeline. Which just shows: sometimes you need a *small nudge* in the right direction. It was the final piece of the puzzle. +The last big push. I shared the current visuals on X and Marc from [Papermark](https://www.papermark.com) pointed me to [Linear](https://linear.app). I've been using Linear for a while and I know their timeline. Sometimes you need a *small nudge* in the right direction. -I stared a while at their timeline. What makes it so good, and why don't I get bored watching it? It's the **hierarchy of events**. Comments are *loud*, everything else is *quiet*. You read the conversation and skim the plumbing. And because the shape repeats on every issue, you learn the workflow without anyone explaining it. Our timeline had no levels, so every event asked for the same attention. +I stared at their timeline for a while. What makes it good is the hierarchy of events. Comments are *loud*, everything else is *quiet*. You read the conversation and skim the plumbing. And because the shape repeats on every issue, you learn the workflow without anyone explaining it. Our timeline had no levels, so every event asked for the same attention. -It was clear: **we need it too**. I gave Claude Design some Linear screenshots and a few of our current designs: +We needed the same. I gave Claude Design some Linear screenshots and a few of our current designs: > Design our timelines closer to Linear. The composer is always the first thing, and the activity is newest first. The second Claude Design output: composer on top, notes and status changes as cards, system events as one-liners -I could have given Claude Code the screenshots straight up and it would have nailed it, I'm pretty sure. Then the loop: +I could probably have given Claude Code the Linear screenshots directly. Then the loop: > /loop and improve our activity timeline across incidents, maintenances and status reports, a bit à la Linear like the screenshots. Don't commit, implement and rework over time. Keep everything on the same compound components. @@ -111,6 +111,16 @@ What changed: Phase 4: notes and status changes as cards, system events as one-liners -The timeline is less tall and less loud because it has levels. The important stuff reads like a *story*. +The timeline is shorter and quieter because it has levels. The important parts read like a conversation. -I'm still feeling like a **meat proxy**. I manually juggle between Claude Code, Claude Design and localhost, adding instructions and being a mostly useless human in the loop. Useless in the typing sense only. I'm in *full control* of what happens and what doesn't: what gets built, what gets dropped, what ships. Four phases, a handful of prompts that said what was wrong rather than what to build, and the timeline is where I want it. +I still feel like a **meat proxy**. I juggle between Claude Code, Claude Design and localhost, adding instructions and being a mostly useless human in the loop. Useless in the typing sense only. I'm in *full control* of what gets built, what gets dropped and what ships. Four phases, a handful of prompts that said what was wrong rather than what to build, and the timeline is where I want it. + +## Less meat proxy + +Two things came out of this that aren't the timeline. + +A `DESIGN.md` at the repo root. `AGENTS.md` says where code lives, `DESIGN.md` says what *good* looks like: hierarchy over decoration, two row kinds on one rail, sentences instead of labels, composer first and newest first. Every rule in there is something I typed into a prompt during one of the phases, usually more than once. Now every session reads it before touching anything a user sees. + +And the screenshot loop as a `screenshot` skill. Before, every session rebuilt it: borrow Playwright from somewhere, find the port, get past the login, set the theme. Now it runs on [agent-browser](https://github.com/vercel-labs/agent-browser) from Vercel instead of our own browser code, with a thin script for the parts that are ours: the seeded session, light and dark, the element instead of the viewport. Claude Code judges the PNGs against `DESIGN.md`, and for a dropdown or a dialog it drives agent-browser directly. + +The goal is fewer rounds where I'm the proxy. I still review on localhost, but the first pass should come back **already looked at**.