<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Xiaofei&apos;s Yuanyang hotpot</title><description>Miscellaneous thoughts on life and work, thrown in one pot.</description><link>https://xiaofeidu.com/</link><language>zh-tw</language><item><title>第一次創業最貴的一課：太早 scale</title><link>https://xiaofeidu.com/zh/posts/premature-scale/</link><guid isPermaLink="true">https://xiaofeidu.com/zh/posts/premature-scale/</guid><description>有客戶，不等於找到了 Product-Market Fit（PMF）。我們在 PMF 之前就忙著擴編，低估了團隊管理和溝通的成本，founder 的專注力也越來越散。招人補不了短板，因為 founder 的短板，就是公司的短板。</description><pubDate>Tue, 13 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;figure&amp;gt;&amp;lt;img src=&quot;/media/premature-scale/zh/cover-blog.webp&quot; alt=&quot;人越多，跑得越慢？鴛鴦豆在忙碌的辦公室頂著搖晃的方塊，蝸牛從旁經過&quot; width=&quot;1200&quot; height=&quot;630&quot; fetchpriority=&quot;high&quot; decoding=&quot;async&quot;&amp;gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;第一次創業，拿到 seed 之後，我們就開始招人。engineer、designer、marketing、sales，一個一個補齊，公司從三、四個人，一路長到最多的時候二十來人，終於像一間公司了。&lt;/p&gt;
&lt;p&gt;那幾年，公司的 vision 沒變過，產品倒是一直在改。2022 年底 ChatGPT 出來以後，整個市場都被 LLM 帶著走，外面的環境也跟著大變。&lt;/p&gt;
&lt;p&gt;一切似乎都在變好。後來我才發現，人越多，我們跑得越慢。&lt;/p&gt;
&lt;h2&gt;能靠招人解決 Go-to-Market（GTM）嗎？&lt;/h2&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;&amp;lt;img src=&quot;/media/premature-scale/zh/scene-gtm.webp&quot; alt=&quot;六個灰豆子朝不同方向拉扯鴛鴦豆，代表團隊各自用力卻無法前進&quot; width=&quot;1600&quot; height=&quot;900&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&amp;gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;我是學術背景出身。為了 steer founder 的 vision，我當時在公司負責團隊管理和產品開發。&lt;/p&gt;
&lt;p&gt;一開始，我們自己在賣，也真的拿到了客戶。於是開始找業務，想把它放大。&lt;/p&gt;
&lt;p&gt;後來才發現，那是假的 PMF。有客戶，不代表有 PMF。真正的 PMF，是你感受到市場推著你往前走：客戶追著你，新的需求也追著你。我們當時沒有這種感覺。&lt;/p&gt;
&lt;p&gt;過早地以為自己找到了 PMF，比沒有 PMF 更危險。&lt;/p&gt;
&lt;p&gt;第一位業務沒有跑通。我們當時的分析是：早期新創沒有 playbook，環境一直在變，業務得從零開發陌生客戶，而我們做的又是 AI infra，產品本身就很難講清楚。&lt;/p&gt;
&lt;p&gt;後來我們試了別的路。請一位 Chief Commercial Officer（CCO），希望用更高的視野和策略，補上執行面的僵化和主動性。同一時期，我們也找了 Developer Relations（DevRel）經營社群，想解決產品的 distribution channel。總之，就是想靠招進不同角色，各管一塊、各有 KPI，多位一體地解決產品的 GTM。&lt;/p&gt;
&lt;h2&gt;為什麼人越多，跑得越慢？&lt;/h2&gt;
&lt;p&gt;現在回頭看，問題從來不在找進來的人身上，而在「多位一體」這個策略本身。對早期新創來說，它非常危險，因為 founder 會把大量精力放在招募和團隊建設上。&lt;/p&gt;
&lt;p&gt;你可能會問，這有什麼錯？&lt;/p&gt;
&lt;p&gt;想像你開著一輛拼裝車，路很顛簸，車上的人快被甩出去。於是你一手掌舵，一手攏住大家。&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;&amp;lt;img src=&quot;/media/premature-scale/zh/scene-cart.webp&quot; alt=&quot;鴛鴦豆一手開著顛簸的拼裝車，一手攏住快被顛出去的隊友&quot; width=&quot;1600&quot; height=&quot;900&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&amp;gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;早期新創就是有這麼一段很 ugly 的階段：邊修，邊往前跑。車上的人越多，出狀況的地方就越多，你的注意力也越容易被拉到這些問題上：&lt;/p&gt;
&lt;p&gt;「我精力不夠了，要不要找個更勝任的人來分擔？」&lt;/p&gt;
&lt;p&gt;「我要怎麼優化我的團隊？」&lt;/p&gt;
&lt;p&gt;然後就陷入惡性循環：多位，卻不一體。每個角色朝不同方向用力，互相拉扯，更糟的是原地空轉。&lt;/p&gt;
&lt;p&gt;早期新創最怕的，是內耗。這裡說的內耗，不是人和人處不來，而是角色切得太早。每多切出一個角色，就多一條要對齊的線，溝通成本和管理成本一層一層往上疊。最後拖垮的，是新創唯一的優勢：快速迭代。&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;&amp;lt;img src=&quot;/media/premature-scale/zh/scene-communication.webp&quot; alt=&quot;灰豆子互相交談，溝通線纏成鴛鴦豆手上的一團&quot; width=&quot;1600&quot; height=&quot;900&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&amp;gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;我們低估的，正是這種成本：對齊、招募、one-on-one、解釋方向，這些時間本來應該花在客戶和產品身上。人變多了，founder 的執行力反而變弱。&lt;/p&gt;
&lt;p&gt;PMF 之前，公司只有一件事要做：搞清楚誰是客戶、他到底要什麼、為什麼願意付錢。每多一個人，founder 的 focus 就被稀釋一點。&lt;/p&gt;
&lt;p&gt;這不只是我們的問題。2011 年 Startup Genome 的報告指出，74% 的高成長網路新創，死於過早擴張。YC 合夥人 Pete Koomen 在 &lt;a href=&quot;https://www.youtube.com/watch?v=0fKYVl12VTA&quot;&gt;Startup School&lt;/a&gt; 也講過：PMF 之前的銷售，本質上是創業，需要願景、客戶的信任、大量實驗，還要跟做產品的人緊密回饋。這是 founder 的工作，還不能交給別人。&lt;/p&gt;
&lt;h2&gt;我們後來怎麼補救&lt;/h2&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;&amp;lt;img src=&quot;/media/premature-scale/zh/scene-refocus.webp&quot; alt=&quot;精簡後的團隊坐在穩定的小車上，轉向前方的客戶&quot; width=&quot;1600&quot; height=&quot;900&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&amp;gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;認識到這一點後，我們有意識地&lt;a href=&quot;/posts/linear-to-github-issues/&quot;&gt;精簡團隊&lt;/a&gt;，安頓好員工，重新聚焦。&lt;/p&gt;
&lt;p&gt;我明顯感受到，掌控感回來了。&lt;/p&gt;
&lt;p&gt;這是痛苦而正確的決定，但一擴一縮之間，公司還是付出了代價，最貴的是時間。&lt;/p&gt;
&lt;p&gt;回頭看，這幾乎是早期摸索繞不開的一段路。沒有人一開始就知道答案，重要的是發現走偏的時候，能不能及時承認、及時調整。&lt;/p&gt;
&lt;h2&gt;對抗自己的本能&lt;/h2&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;&amp;lt;img src=&quot;/media/premature-scale/zh/scene-customer.webp&quot; alt=&quot;鴛鴦豆專心聽客戶說話並做筆記，旁邊的電腦自行運作&quot; width=&quot;1600&quot; height=&quot;900&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&amp;gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;心理學家 Jonathan Haidt 有個&lt;a href=&quot;https://www.happinesshypothesis.com/&quot;&gt;比喻&lt;/a&gt;：我們以為自己是騎在大象上、決定方向的那個人。其實更多時候，是大象（直覺）先轉了彎，騎象人（理性）才回頭解釋為什麼要往這邊走。&lt;/p&gt;
&lt;p&gt;而且這不是某一刻的決定，是一道滑坡，是溫水煮青蛙。每一步都說得通，每一步都讓你更靠近舒適圈一點。最迷惑的是，從表面來看，大家都在忙碌，你安慰自己，事情總有起起伏伏。實際上，你的體感已經無法反映真實的狀況。等回過頭來，才發現自己已經離 founder 真正該做的事很遠了，focus 也散了。&lt;/p&gt;
&lt;p&gt;「我的時間應該花在我擅長的事情上。」這句話沒有錯，但在新創初期，它是反直覺的。Founder 要有意識地保護自己的 focus、注意力和執行力，即使這個過程讓你不舒服：把擅長的事情自動化，再把時間花在不擅長的事情上。&lt;/p&gt;
&lt;p&gt;招人並不會彌補短板。因為 founder 的短板，就是公司的短板。&lt;/p&gt;
</content:encoded></item><item><title>We stopped paying for Linear</title><link>https://xiaofeidu.com/posts/linear-to-github-issues/</link><guid isPermaLink="true">https://xiaofeidu.com/posts/linear-to-github-issues/</guid><description>At some point I stopped opening Linear and just asked the agent. The good UI, the workflows, the automations I used to love had stopped mattering. That&apos;s when the unsubscribing started.</description><pubDate>Mon, 17 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the last two months, me and my friend &lt;a href=&quot;https://pinglin.tw/&quot;&gt;Pinglin&lt;/a&gt; have been building an interesting project, &lt;em&gt;Pingbo&lt;/em&gt;, together, and we have iterated our way into one setup for project management. The road there was longer: a ten-person team on the sleekest SaaS, then a two-person board run entirely by an agent. We dropped both, for very different reasons, and ended up on plain GitHub issues. I think that is where this is all going.&lt;/p&gt;
&lt;h2&gt;I used to love Linear, but it is not perfect&lt;/h2&gt;
&lt;p&gt;I am a fan of &lt;a href=&quot;https://linear.app&quot;&gt;Linear&lt;/a&gt;. We ditched &lt;a href=&quot;https://clickup.com&quot;&gt;ClickUp&lt;/a&gt; for it without a doubt back in 2023.&lt;/p&gt;
&lt;p&gt;Compared to Linear, ClickUp immediately felt like a clumsy all-in-one platform that tries to do everything and excels at nothing. Linear is a winner that suits my tastes better, elegant and focused. As a product, it is sleek, it is professional, the UI/UX is phenomenal. It holds high standards when delivering a feature, which I appreciate A LOT. I love products that do less but better.&lt;/p&gt;
&lt;p&gt;But this does not mean that it is all a fairy tale when using it. Our team had grown to 10-ish, with one PM, one designer, and the rest engineers. I was overseeing the project development.&lt;/p&gt;
&lt;p&gt;We constantly encountered issues: an ever-growing, overblown, outdated backlog; sprints stuffed with more tickets than they could ever finish. Fail enough of them in a row, and the sprint stops meaning anything. Of course, you can say: the tool is not the problem, the way you use it is. Set strict team rules, keep the dashboard clean, and the problem goes away.&lt;/p&gt;
&lt;p&gt;But I just don&apos;t think it is that simple. We definitely tried. We defined the full workflow: what labels to use, who should do what after what has finished, setting up every automation we could to cover for human forgetfulness. Constantly reminding the team to follow the rules. No matter how smooth Linear is, none of it held, because the problem is not the rules. Everyone works differently. These tools create a unified platform for everyone to work on, but that does not mean co-working is solved. You can FORCE a unified way for a team to work together, but that demands adjustment, or sacrifice, that bends how each person usually works.&lt;/p&gt;
&lt;p&gt;I still remember being stressed, sending messages to our PM complaining: what is the actual status of the project, are we going to finish all the planned features in the sprints? She was also frustrated, because she couldn&apos;t understand why I needed her constant updates, why I couldn&apos;t just read her daily report and go to the dashboard to see for myself.&lt;/p&gt;
&lt;p&gt;To make the communication smooth — funny, the word, smooth — we agreed on how the daily report should look, what it should include and what it should not.&lt;/p&gt;
&lt;p&gt;Looking back, it is funny to think that we did EXTRA paperwork, designed our workflow around whatever tool we used, and called it smooth communication. Then the rules on the wall slowly got ignored, the docs went stale, and over time it all decayed into a chaotic, outdated junkyard — the paperwork we invented to manage the project became the thing nobody managed. It was not pleasant for either of us. Our PM spent more time managing my expectations; I spent more time trying to feel more in control.&lt;/p&gt;
&lt;p&gt;That was my compromise. I started to accept that modern project management is a mess, inevitably. Like, what can you do?&lt;/p&gt;
&lt;h2&gt;Then, the agent era comes. Things have changed.&lt;/h2&gt;
&lt;p&gt;Our team stepped into the vibe coding period. Everyone was anxious, doubting, resisting and excited at the same time. More code was produced, so features were expected to iterate and launch faster. At the same time, the market changed daily; we needed to pivot, and we had to cut the team size down. All of it added to the chaos of project management. As a coder and manager, I lived in uncertainty.&lt;/p&gt;
&lt;p&gt;We opened the Linear UI less and less; we managed via Linear &lt;a href=&quot;https://modelcontextprotocol.io&quot;&gt;MCP&lt;/a&gt;. I just asked the AI agent to summarize the status. I didn&apos;t need to — or WANT to — go somewhere, click a button, or ask a person in the team, back and forth, to get what I wanted. I would honestly say that the things I used to appreciate — the good UI, the well-built workflow and automations — do not matter that much anymore.&lt;/p&gt;
&lt;h2&gt;So, I outsourced everything in PM to the agent. Turns out not the best idea&lt;/h2&gt;
&lt;p&gt;Unfortunately, our product did not work out. It is a long story that I may tell another day, another time. Fast-forward to now: me and Pinglin are building &lt;em&gt;Pingbo&lt;/em&gt; together.&lt;/p&gt;
&lt;p&gt;OK, a super tiny team still needs project management. I would say even a one-person army still needs project management. Things can get out of hand very quickly when the scope or the details keep changing.&lt;/p&gt;
&lt;p&gt;What tool should we use? After everything the ten-ish team had taught us, we dropped Linear first. The price was not even the main reason: if sleek UI/UX does not matter anymore, I just need a place to manage tickets, that&apos;s all.&lt;/p&gt;
&lt;p&gt;Then we took it a step further. Since we had become so used to vibe coding, I suggested: why don&apos;t we just use an agent purely for management? I vibed a simple ticket board and hosted it on a page via Cloudflare. We could just go there and eyeball all the tickets; it even had a Gantt chart. We even had an agent routinely check the status every day — if anything was outdated, the agent could send a PR to fix it.&lt;/p&gt;
&lt;p&gt;We ran it while rapidly developing the project.&lt;/p&gt;
&lt;h2&gt;The residue&lt;/h2&gt;
&lt;p&gt;I don&apos;t know if you have ever watched a Japanese horror movie called &lt;a href=&quot;https://en.wikipedia.org/wiki/Pulse_(2001_film)&quot;&gt;回路 Kairo&lt;/a&gt;. When people die, they leave a charcoal-stained residue on the wall, in the room.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;figure-row&quot;&amp;gt;&amp;lt;figure&amp;gt;&amp;lt;img src=&quot;/media/kairo-shadow.webp&quot; alt=&quot;A dim room in the film Kairo; on the wall, a dark human-shaped stain where a person used to be&quot; width=&quot;1080&quot; height=&quot;590&quot;&amp;gt;&amp;lt;figcaption&amp;gt;When someone dies in &amp;lt;em&amp;gt;Kairo&amp;lt;/em&amp;gt;, they do not vanish — a charcoal stain in their shape stays on the wall of the room (回路, 2001, dir. Kiyoshi Kurosawa)&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;&amp;lt;figure&amp;gt;&amp;lt;img src=&quot;/media/kairo-door.webp&quot; alt=&quot;A doorway sealed with red tape in the film Kairo, seen across an empty gravel yard&quot; width=&quot;1080&quot; height=&quot;573&quot;&amp;gt;&amp;lt;figcaption&amp;gt;The rooms the ghosts came through get sealed with red tape; the film keeps finding the tape, and it keeps not being enough&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;At first, the board worked fine. Then our project&apos;s dead specs started doing what the dead in Kairo do: they left a residue. We had defined team rules again, this time for the agent, and written skills to keep the specs, the tickets and the board up to date. It happened anyway. Outdated docs and tickets got left behind every time we updated the specs, and the agent kept building on them. Slowly, the old discussions crawled back.&lt;/p&gt;
&lt;p&gt;Initially we hoped that the daily agent board correction could be helpful. But it slowly became an empty ritual. I merged the board correction PR without even reviewing it, because I simply did not have the time to read through the growing markdown docs and html. It became like we were maintaining and updating the board for the sake of management.&lt;/p&gt;
&lt;p&gt;PM is semi-structured work: it holds your thoughts about the project, and it needs to constantly evolve, so it needs lifecycle and workflow. If you let an agent manage all of it via markdown and html, it does well on the unstructured part — every thought you went through, every detail you pinned down, the reasons behind them and how they evolved — but it lacks the structure. The boundary of things is loose, and when you update, reject ideas, or change specs, it gets lost in the details. Your board becomes a hoarder&apos;s house.&lt;/p&gt;
&lt;p&gt;It took me longer to see that the hoarder&apos;s house was not the biggest problem. The mess actively misleads; it does not just sit there being untidy.&lt;/p&gt;
&lt;p&gt;When an agent reads the board, everything in the file arrives with the same standing. There is no field marking one paragraph current and another historical — the model has to infer that from the prose, and prose does not reliably carry it. In a tracker, being current is a property. In a document, being current is a claim the reader has to evaluate.&lt;/p&gt;
&lt;p&gt;So when we changed a spec or reversed a decision, the old one did not go anywhere. It stayed in the plan that proposed it, in the other specs that referenced it, in the ticket text written around it. We even stamped the docs with statuses and update times, and left notes saying the old references were historical, not current. But a stamp is one line, and the doc under it is two thousand. Every sentence of a spec claims, just by its grammar, that it is true right now; the one line saying otherwise is outvoted by the body it sits on. Nothing tells the model which sentence is newer, so it weighs what it sees: which version is mentioned more, which sits closer to the end, which sounds more certain. Sometimes the new decision wins. Often the old one does, because the old one was discussed longer and left more supporting text behind it. Being current is just one signal among many, and often not the loudest.&lt;/p&gt;
&lt;p&gt;And the stamps themselves decayed. Changing the content and updating the stamp are two actions, and the second is something an agent has to remember while holding ten other things in context. It drops it, not often, just sometimes. Then the shape drifts: &lt;code&gt;updated&lt;/code&gt; becomes &lt;code&gt;last_updated&lt;/code&gt;, &lt;code&gt;2026-08-17&lt;/code&gt; becomes &lt;code&gt;Aug 17&lt;/code&gt;, because nothing constrains it. A query over the stamps does not throw when this happens. It just returns fewer results.&lt;/p&gt;
&lt;p&gt;That is what the residue does. It is not noise. It votes.&lt;/p&gt;
&lt;p&gt;So after several weeks, we gave up on the idea. As the project grew bigger and the specs iterated fast, the self-built, self-hosted board was slowing us down and causing confusion. We needed a proper tool.&lt;/p&gt;
&lt;h2&gt;GitHub issues are enough in the agent era&lt;/h2&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;&amp;lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 1200 700&quot; role=&quot;img&quot; aria-label=&quot;Three places to keep the tickets: an agent-maintained markdown board that only grows, GitHub issues where state is a field, and a project management SaaS that pays in UI&quot; style=&quot;max-width:100%;height:auto;display:block&quot;&amp;gt;&amp;lt;rect width=&quot;1200&quot; height=&quot;700&quot; rx=&quot;14&quot; fill=&quot;var(--code-bg)&quot;/&amp;gt;&amp;lt;text x=&quot;64&quot; y=&quot;96&quot; style=&quot;font-family:var(--font-sans);font-size:38px;font-weight:600;letter-spacing:-.5px&quot; fill=&quot;var(--fg)&quot;&amp;gt;Where do the tickets live?&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;65&quot; y=&quot;132&quot; style=&quot;font-family:var(--font-sans);font-size:22px&quot; fill=&quot;var(--fg-muted)&quot;&amp;gt;The same job, three homes. Only one of them has a machine-readable lifecycle.&amp;lt;/text&amp;gt;&amp;lt;rect x=&quot;64&quot; y=&quot;180&quot; width=&quot;330&quot; height=&quot;380&quot; rx=&quot;14&quot; fill=&quot;var(--bg)&quot; stroke=&quot;var(--rule-strong)&quot; stroke-opacity=&quot;.45&quot; stroke-width=&quot;1.5&quot;/&amp;gt;&amp;lt;clipPath id=&quot;c1&quot;&amp;gt;&amp;lt;rect x=&quot;64&quot; y=&quot;180&quot; width=&quot;330&quot; height=&quot;380&quot; rx=&quot;14&quot;/&amp;gt;&amp;lt;/clipPath&amp;gt;&amp;lt;g clip-path=&quot;url(#c1)&quot;&amp;gt;&amp;lt;rect x=&quot;104&quot; y=&quot;216&quot; width=&quot;176&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.4&quot;/&amp;gt;&amp;lt;rect x=&quot;104&quot; y=&quot;243&quot; width=&quot;196&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.4&quot;/&amp;gt;&amp;lt;rect x=&quot;104&quot; y=&quot;270&quot; width=&quot;150&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.18&quot;/&amp;gt;&amp;lt;line x1=&quot;104&quot; y1=&quot;276.5&quot; x2=&quot;254&quot; y2=&quot;276.5&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;2.5&quot; opacity=&quot;.85&quot;/&amp;gt;&amp;lt;rect x=&quot;104&quot; y=&quot;297&quot; width=&quot;188&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.4&quot;/&amp;gt;&amp;lt;rect x=&quot;104&quot; y=&quot;324&quot; width=&quot;214&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.4&quot;/&amp;gt;&amp;lt;rect x=&quot;104&quot; y=&quot;351&quot; width=&quot;164&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.4&quot;/&amp;gt;&amp;lt;rect x=&quot;104&quot; y=&quot;378&quot; width=&quot;196&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.18&quot;/&amp;gt;&amp;lt;line x1=&quot;104&quot; y1=&quot;384.5&quot; x2=&quot;300&quot; y2=&quot;384.5&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;2.5&quot; opacity=&quot;.85&quot;/&amp;gt;&amp;lt;rect x=&quot;104&quot; y=&quot;405&quot; width=&quot;154&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.4&quot;/&amp;gt;&amp;lt;rect x=&quot;104&quot; y=&quot;432&quot; width=&quot;182&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.4&quot;/&amp;gt;&amp;lt;rect x=&quot;104&quot; y=&quot;459&quot; width=&quot;200&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.28&quot;/&amp;gt;&amp;lt;rect x=&quot;104&quot; y=&quot;486&quot; width=&quot;170&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.22&quot;/&amp;gt;&amp;lt;rect x=&quot;104&quot; y=&quot;513&quot; width=&quot;190&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.16&quot;/&amp;gt;&amp;lt;rect x=&quot;104&quot; y=&quot;540&quot; width=&quot;160&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.1&quot;/&amp;gt;&amp;lt;/g&amp;gt;&amp;lt;text x=&quot;229.0&quot; y=&quot;542&quot; text-anchor=&quot;middle&quot; style=&quot;font-family:var(--font-sans);font-size:23px;font-weight:600&quot; fill=&quot;var(--fg)&quot;&amp;gt;Agent-run markdown&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;229.0&quot; y=&quot;594&quot; text-anchor=&quot;middle&quot; style=&quot;font-family:var(--font-mono);font-size:19px&quot; fill=&quot;var(--fg)&quot;&amp;gt;the file only grows&amp;lt;/text&amp;gt;&amp;lt;rect x=&quot;435&quot; y=&quot;180&quot; width=&quot;330&quot; height=&quot;380&quot; rx=&quot;14&quot; fill=&quot;var(--bg)&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;2.5&quot;/&amp;gt;&amp;lt;circle cx=&quot;487&quot; cy=&quot;228&quot; r=&quot;9&quot; fill=&quot;var(--accent)&quot;/&amp;gt;&amp;lt;rect x=&quot;509&quot; y=&quot;222&quot; width=&quot;150&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;.42&quot;/&amp;gt;&amp;lt;circle cx=&quot;487&quot; cy=&quot;262&quot; r=&quot;9&quot; fill=&quot;var(--accent)&quot;/&amp;gt;&amp;lt;rect x=&quot;509&quot; y=&quot;256&quot; width=&quot;196&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;.42&quot;/&amp;gt;&amp;lt;circle cx=&quot;487&quot; cy=&quot;296&quot; r=&quot;9&quot; fill=&quot;var(--accent)&quot;/&amp;gt;&amp;lt;rect x=&quot;509&quot; y=&quot;290&quot; width=&quot;172&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;.42&quot;/&amp;gt;&amp;lt;circle cx=&quot;487&quot; cy=&quot;330&quot; r=&quot;9&quot; fill=&quot;var(--accent)&quot;/&amp;gt;&amp;lt;rect x=&quot;509&quot; y=&quot;324&quot; width=&quot;188&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--accent)&quot; opacity=&quot;.42&quot;/&amp;gt;&amp;lt;line x1=&quot;469&quot; y1=&quot;364&quot; x2=&quot;731&quot; y2=&quot;364&quot; stroke=&quot;var(--rule-strong)&quot; stroke-width=&quot;1.5&quot; stroke-dasharray=&quot;5 5&quot; opacity=&quot;.6&quot;/&amp;gt;&amp;lt;text x=&quot;731&quot; y=&quot;356&quot; text-anchor=&quot;end&quot; style=&quot;font-family:var(--font-mono);font-size:16px&quot; fill=&quot;var(--fg)&quot;&amp;gt;the view&amp;lt;/text&amp;gt;&amp;lt;circle cx=&quot;487&quot; cy=&quot;392&quot; r=&quot;8&quot; fill=&quot;none&quot; stroke=&quot;var(--fg-faint)&quot; stroke-width=&quot;2&quot; opacity=&quot;.7&quot;/&amp;gt;&amp;lt;rect x=&quot;509&quot; y=&quot;386&quot; width=&quot;180&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--fg-faint)&quot; opacity=&quot;.28&quot;/&amp;gt;&amp;lt;circle cx=&quot;487&quot; cy=&quot;423&quot; r=&quot;8&quot; fill=&quot;none&quot; stroke=&quot;var(--fg-faint)&quot; stroke-width=&quot;2&quot; opacity=&quot;.7&quot;/&amp;gt;&amp;lt;rect x=&quot;509&quot; y=&quot;417&quot; width=&quot;150&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--fg-faint)&quot; opacity=&quot;.28&quot;/&amp;gt;&amp;lt;circle cx=&quot;487&quot; cy=&quot;454&quot; r=&quot;8&quot; fill=&quot;none&quot; stroke=&quot;var(--fg-faint)&quot; stroke-width=&quot;2&quot; opacity=&quot;.7&quot;/&amp;gt;&amp;lt;rect x=&quot;509&quot; y=&quot;448&quot; width=&quot;190&quot; height=&quot;13&quot; rx=&quot;6&quot; fill=&quot;var(--fg-faint)&quot; opacity=&quot;.28&quot;/&amp;gt;&amp;lt;text x=&quot;600.0&quot; y=&quot;542&quot; text-anchor=&quot;middle&quot; style=&quot;font-family:var(--font-sans);font-size:23px;font-weight:600&quot; fill=&quot;var(--accent)&quot;&amp;gt;GitHub issues&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;600.0&quot; y=&quot;594&quot; text-anchor=&quot;middle&quot; style=&quot;font-family:var(--font-mono);font-size:19px&quot; fill=&quot;var(--fg)&quot;&amp;gt;closed leaves the view&amp;lt;/text&amp;gt;&amp;lt;rect x=&quot;806&quot; y=&quot;180&quot; width=&quot;330&quot; height=&quot;380&quot; rx=&quot;14&quot; fill=&quot;var(--bg)&quot; stroke=&quot;var(--rule-strong)&quot; stroke-opacity=&quot;.45&quot; stroke-width=&quot;1.5&quot;/&amp;gt;&amp;lt;rect x=&quot;844&quot; y=&quot;218&quot; width=&quot;254&quot; height=&quot;240&quot; rx=&quot;10&quot; fill=&quot;none&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.6&quot; opacity=&quot;.75&quot;/&amp;gt;&amp;lt;line x1=&quot;844&quot; y1=&quot;252&quot; x2=&quot;1098&quot; y2=&quot;252&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.2&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;circle cx=&quot;864&quot; cy=&quot;235&quot; r=&quot;4.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.6&quot;/&amp;gt;&amp;lt;circle cx=&quot;880&quot; cy=&quot;235&quot; r=&quot;4.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.6&quot;/&amp;gt;&amp;lt;circle cx=&quot;896&quot; cy=&quot;235&quot; r=&quot;4.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.6&quot;/&amp;gt;&amp;lt;rect x=&quot;1020&quot; y=&quot;226&quot; width=&quot;64&quot; height=&quot;18&quot; rx=&quot;9&quot; fill=&quot;var(--accent)&quot; opacity=&quot;.16&quot;/&amp;gt;&amp;lt;text x=&quot;1052&quot; y=&quot;239.5&quot; text-anchor=&quot;middle&quot; style=&quot;font-family:var(--font-mono);font-size:13px;font-weight:600&quot; fill=&quot;var(--accent)&quot;&amp;gt;$ /seat&amp;lt;/text&amp;gt;&amp;lt;rect x=&quot;862.0&quot; y=&quot;266&quot; width=&quot;37.61333333333333&quot; height=&quot;9&quot; rx=&quot;4.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;rect x=&quot;862.0&quot; y=&quot;288&quot; width=&quot;60.666666666666664&quot; height=&quot;34&quot; rx=&quot;6&quot; fill=&quot;none&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.3&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;rect x=&quot;870.0&quot; y=&quot;298&quot; width=&quot;30.666666666666664&quot; height=&quot;7&quot; rx=&quot;3.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.4&quot;/&amp;gt;&amp;lt;rect x=&quot;862.0&quot; y=&quot;332&quot; width=&quot;60.666666666666664&quot; height=&quot;34&quot; rx=&quot;6&quot; fill=&quot;none&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.3&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;rect x=&quot;870.0&quot; y=&quot;342&quot; width=&quot;30.666666666666664&quot; height=&quot;7&quot; rx=&quot;3.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.4&quot;/&amp;gt;&amp;lt;rect x=&quot;862.0&quot; y=&quot;376&quot; width=&quot;60.666666666666664&quot; height=&quot;34&quot; rx=&quot;6&quot; fill=&quot;none&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.3&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;rect x=&quot;870.0&quot; y=&quot;386&quot; width=&quot;30.666666666666664&quot; height=&quot;7&quot; rx=&quot;3.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.4&quot;/&amp;gt;&amp;lt;rect x=&quot;940.6666666666666&quot; y=&quot;266&quot; width=&quot;37.61333333333333&quot; height=&quot;9&quot; rx=&quot;4.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;rect x=&quot;940.6666666666666&quot; y=&quot;288&quot; width=&quot;60.666666666666664&quot; height=&quot;34&quot; rx=&quot;6&quot; fill=&quot;none&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.3&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;rect x=&quot;948.6666666666666&quot; y=&quot;298&quot; width=&quot;30.666666666666664&quot; height=&quot;7&quot; rx=&quot;3.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.4&quot;/&amp;gt;&amp;lt;rect x=&quot;940.6666666666666&quot; y=&quot;332&quot; width=&quot;60.666666666666664&quot; height=&quot;34&quot; rx=&quot;6&quot; fill=&quot;none&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.3&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;rect x=&quot;948.6666666666666&quot; y=&quot;342&quot; width=&quot;30.666666666666664&quot; height=&quot;7&quot; rx=&quot;3.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.4&quot;/&amp;gt;&amp;lt;rect x=&quot;1019.3333333333333&quot; y=&quot;266&quot; width=&quot;37.61333333333333&quot; height=&quot;9&quot; rx=&quot;4.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;rect x=&quot;1019.3333333333333&quot; y=&quot;288&quot; width=&quot;60.666666666666664&quot; height=&quot;34&quot; rx=&quot;6&quot; fill=&quot;none&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.3&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;rect x=&quot;1027.3333333333333&quot; y=&quot;298&quot; width=&quot;30.666666666666664&quot; height=&quot;7&quot; rx=&quot;3.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.4&quot;/&amp;gt;&amp;lt;rect x=&quot;1019.3333333333333&quot; y=&quot;332&quot; width=&quot;60.666666666666664&quot; height=&quot;34&quot; rx=&quot;6&quot; fill=&quot;none&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.3&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;rect x=&quot;1027.3333333333333&quot; y=&quot;342&quot; width=&quot;30.666666666666664&quot; height=&quot;7&quot; rx=&quot;3.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.4&quot;/&amp;gt;&amp;lt;rect x=&quot;1019.3333333333333&quot; y=&quot;376&quot; width=&quot;60.666666666666664&quot; height=&quot;34&quot; rx=&quot;6&quot; fill=&quot;none&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.3&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;rect x=&quot;1027.3333333333333&quot; y=&quot;386&quot; width=&quot;30.666666666666664&quot; height=&quot;7&quot; rx=&quot;3.5&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.4&quot;/&amp;gt;&amp;lt;text x=&quot;971.0&quot; y=&quot;542&quot; text-anchor=&quot;middle&quot; style=&quot;font-family:var(--font-sans);font-size:23px;font-weight:600&quot; fill=&quot;var(--fg)&quot;&amp;gt;The PM SaaS&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;971.0&quot; y=&quot;594&quot; text-anchor=&quot;middle&quot; style=&quot;font-family:var(--font-mono);font-size:19px&quot; fill=&quot;var(--fg)&quot;&amp;gt;pays in UI you no longer visit&amp;lt;/text&amp;gt;&amp;lt;/svg&amp;gt;&amp;lt;figcaption&amp;gt;Three homes for the same tickets; we ended up in the middle one&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;Years ago we had left GitHub issues for Linear, and never looked back at what we left.&lt;/p&gt;
&lt;p&gt;But, this time we chose GitHub issues. &amp;lt;span lang=&quot;zh-Hant&quot;&amp;gt;返璞歸真&amp;lt;/span&amp;gt; — back to the plain thing. It wins in so many ways.&lt;/p&gt;
&lt;p&gt;First, if you use GitHub for code, you get &lt;a href=&quot;https://docs.github.com/en/issues&quot;&gt;issues and projects&lt;/a&gt; for free — and they link to pull requests, so closing the work closes the ticket. The code and the management live in the same place. One less tool to deal with, one less subscription. The same layer ships with GitLab, by the way; our code just lives on GitHub.&lt;/p&gt;
&lt;p&gt;Second, these features provide the basic lifecycle and workflow that an agent needs for project management. Yes, the agent. The era of humans directly managing projects is over; GitHub issues and projects hand the agent the APIs to run it. A thin layer is exactly what an agent needs.&lt;/p&gt;
&lt;p&gt;Third, the lack of fancy UI is not a disadvantage anymore. Well, just for being fancy, I vibed a project board that better matches mine and Pinglin&apos;s taste and habits. It renders itself straight from the GitHub API every couple of hours, nothing on it hand-maintained, so we have the best of both worlds. To be clear, the board is not necessary at all. It is taste — a treat we made for ourselves, so everyday work feels good. The mechanics live in the issues; the taste lives in our board. And when the taste changes, we throw the board away and vibe a new one. The state underneath does not move.&lt;/p&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;&amp;lt;a href=&quot;/media/board-full.mp4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; title=&quot;Open the full-resolution recording&quot;&amp;gt;&amp;lt;video autoplay loop muted playsinline width=&quot;1104&quot; height=&quot;689&quot; poster=&quot;/media/board-poster.jpg&quot;&amp;gt;&amp;lt;source src=&quot;/media/board.webm&quot; type=&quot;video/webm&quot;&amp;gt;&amp;lt;source src=&quot;/media/board.mp4&quot; type=&quot;video/mp4&quot;&amp;gt;&amp;lt;/video&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;figcaption&amp;gt;Our own board for Pingbo, regenerated from GitHub issues — watch it jump into the GitHub Project that automates the lifecycle&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;We have run with this for several weeks, and are genuinely happier with this setup.&lt;/p&gt;
&lt;p&gt;Not because issues cannot rot. An issue can go stale — nobody touches it for six months and it rots, the same as any list anywhere. The difference is that here you can &lt;em&gt;find&lt;/em&gt; it. An issue carries its whole lifecycle as data: when it was opened, touched, closed, by whom, and what state it sits in. So &quot;every open issue untouched in ninety days&quot; is a query, and an agent can run it and close or revise what comes back. A line in a markdown file has no time of its own.&lt;/p&gt;
&lt;p&gt;That lifecycle cannot decay the way our stamps did. GitHub writes &lt;code&gt;updated_at&lt;/code&gt; as a side effect of the change: there is no second action to remember, no shape to drift, no way for it to be wrong. That is the whole difference. Not what the format can represent, but who guarantees it. We had written skills to keep the board current, and the skills were advice. The tracker&apos;s fields are not advice.&lt;/p&gt;
&lt;p&gt;The residue is a separate thing again. In markdown, changing your mind is an edit: you either delete the old decision and lose the reason for it, or you strike it through and leave it sitting in the context. Either way the file only grows. Closing an issue rewrites nothing — the dead thing leaves the default view without anyone remembering to remove it, and it is still there when you go looking for why.&lt;/p&gt;
&lt;p&gt;The review ritual from the board era simply disappeared. Not because we became more disciplined: there is nothing left to review. An issue opened or closed notifies us by itself. A markdown diff was prose with no shape, and reading it cost more than it returned. A state change is one fact, and the fact comes to us.&lt;/p&gt;
&lt;p&gt;Opening an issue mid-implementation is cheap too, and not because there is no lookup — there is. The agent still checks whether something similar is already open. The difference is where the filtering happens. Against a markdown board, &quot;is this a duplicate?&quot; means loading the whole file and letting the model do the filtering in its own attention: two hundred tickets go into the context so that one of them can be compared. Against the API it is a search that comes back with three, and the other one hundred and ninety-seven never cost anything. The operations are typed, too — &lt;code&gt;search&lt;/code&gt;, &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;close&lt;/code&gt; — so the agent never has to decide &lt;em&gt;how&lt;/em&gt; to write the change, only what it is.&lt;/p&gt;
&lt;p&gt;Remember the daily report our PM and I fought over? We still get one every morning — the agent writes it now, shaped the way we want. But the negotiation is gone. When I want to know something about the project, I no longer go through another person. I just ask the agent, and so does Pinglin, each in our own way.&lt;/p&gt;
&lt;h2&gt;Is project management SaaS dead?&lt;/h2&gt;
&lt;p&gt;&amp;lt;figure&amp;gt;&amp;lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 1200 600&quot; role=&quot;img&quot; aria-label=&quot;What the subscription was paying for: before agents, the UI made everyone the same shape; with agents everyone contributes and reads in their own shape, and the agent is the interface&quot; style=&quot;max-width:100%;height:auto;display:block&quot;&amp;gt;&amp;lt;rect width=&quot;1200&quot; height=&quot;600&quot; rx=&quot;14&quot; fill=&quot;var(--code-bg)&quot;/&amp;gt;&amp;lt;text x=&quot;64&quot; y=&quot;92&quot; style=&quot;font-family:var(--font-sans);font-size:38px;font-weight:600;letter-spacing:-.5px&quot; fill=&quot;var(--fg)&quot;&amp;gt;What the subscription was paying for&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;65&quot; y=&quot;128&quot; style=&quot;font-family:var(--font-sans);font-size:22px&quot; fill=&quot;var(--fg-muted)&quot;&amp;gt;The UI existed to absorb the difference in how each person works. Now something else absorbs it.&amp;lt;/text&amp;gt;&amp;lt;rect x=&quot;64&quot; y=&quot;170&quot; width=&quot;520&quot; height=&quot;360&quot; rx=&quot;14&quot; fill=&quot;var(--bg)&quot; stroke=&quot;var(--rule-strong)&quot; stroke-opacity=&quot;.45&quot; stroke-width=&quot;1.5&quot;/&amp;gt;&amp;lt;text x=&quot;94&quot; y=&quot;212&quot; style=&quot;font-family:var(--font-mono);font-size:17px;letter-spacing:2px&quot; fill=&quot;var(--fg)&quot;&amp;gt;BEFORE&amp;lt;/text&amp;gt;&amp;lt;circle cx=&quot;134&quot; cy=&quot;278&quot; r=&quot;13&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.85&quot;/&amp;gt;&amp;lt;path d=&quot;M134 337 L146.35 359.75 L121.65 359.75 Z&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.85&quot;/&amp;gt;&amp;lt;rect x=&quot;122.3&quot; y=&quot;410.3&quot; width=&quot;23.400000000000002&quot; height=&quot;23.400000000000002&quot; rx=&quot;4&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.85&quot;/&amp;gt;&amp;lt;rect x=&quot;252&quot; y=&quot;242&quot; width=&quot;92&quot; height=&quot;226&quot; rx=&quot;12&quot; fill=&quot;var(--accent)&quot; opacity=&quot;.14&quot;/&amp;gt;&amp;lt;rect x=&quot;252&quot; y=&quot;242&quot; width=&quot;92&quot; height=&quot;226&quot; rx=&quot;12&quot; fill=&quot;none&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;2&quot;/&amp;gt;&amp;lt;text x=&quot;298&quot; y=&quot;362&quot; text-anchor=&quot;middle&quot; style=&quot;font-family:var(--font-sans);font-size:19px;font-weight:600&quot; fill=&quot;var(--accent)&quot;&amp;gt;the UI&amp;lt;/text&amp;gt;&amp;lt;line x1=&quot;160&quot; y1=&quot;278&quot; x2=&quot;244&quot; y2=&quot;278&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;244&quot; y1=&quot;278&quot; x2=&quot;235.86335072184644&quot; y2=&quot;281.8464189221045&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;244&quot; y1=&quot;278&quot; x2=&quot;235.86335072184644&quot; y2=&quot;274.1535810778955&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;344&quot; y1=&quot;278&quot; x2=&quot;402&quot; y2=&quot;278&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.7&quot;/&amp;gt;&amp;lt;line x1=&quot;402&quot; y1=&quot;278&quot; x2=&quot;393.8633507218465&quot; y2=&quot;281.8464189221045&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.7&quot;/&amp;gt;&amp;lt;line x1=&quot;402&quot; y1=&quot;278&quot; x2=&quot;393.8633507218465&quot; y2=&quot;274.1535810778955&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.7&quot;/&amp;gt;&amp;lt;rect x=&quot;410&quot; y=&quot;267&quot; width=&quot;22&quot; height=&quot;22&quot; rx=&quot;5&quot; fill=&quot;var(--accent)&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;160&quot; y1=&quot;350&quot; x2=&quot;244&quot; y2=&quot;350&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;244&quot; y1=&quot;350&quot; x2=&quot;235.86335072184644&quot; y2=&quot;353.8464189221045&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;244&quot; y1=&quot;350&quot; x2=&quot;235.86335072184644&quot; y2=&quot;346.1535810778955&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;344&quot; y1=&quot;350&quot; x2=&quot;402&quot; y2=&quot;350&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.7&quot;/&amp;gt;&amp;lt;line x1=&quot;402&quot; y1=&quot;350&quot; x2=&quot;393.8633507218465&quot; y2=&quot;353.8464189221045&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.7&quot;/&amp;gt;&amp;lt;line x1=&quot;402&quot; y1=&quot;350&quot; x2=&quot;393.8633507218465&quot; y2=&quot;346.1535810778955&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.7&quot;/&amp;gt;&amp;lt;rect x=&quot;410&quot; y=&quot;339&quot; width=&quot;22&quot; height=&quot;22&quot; rx=&quot;5&quot; fill=&quot;var(--accent)&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;160&quot; y1=&quot;422&quot; x2=&quot;244&quot; y2=&quot;422&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;244&quot; y1=&quot;422&quot; x2=&quot;235.86335072184644&quot; y2=&quot;425.8464189221045&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;244&quot; y1=&quot;422&quot; x2=&quot;235.86335072184644&quot; y2=&quot;418.1535810778955&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;344&quot; y1=&quot;422&quot; x2=&quot;402&quot; y2=&quot;422&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.7&quot;/&amp;gt;&amp;lt;line x1=&quot;402&quot; y1=&quot;422&quot; x2=&quot;393.8633507218465&quot; y2=&quot;425.8464189221045&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.7&quot;/&amp;gt;&amp;lt;line x1=&quot;402&quot; y1=&quot;422&quot; x2=&quot;393.8633507218465&quot; y2=&quot;418.1535810778955&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.7&quot;/&amp;gt;&amp;lt;rect x=&quot;410&quot; y=&quot;411&quot; width=&quot;22&quot; height=&quot;22&quot; rx=&quot;5&quot; fill=&quot;var(--accent)&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;text x=&quot;324.0&quot; y=&quot;564&quot; text-anchor=&quot;middle&quot; style=&quot;font-family:var(--font-mono);font-size:19px&quot; fill=&quot;var(--fg)&quot;&amp;gt;everyone comes out the same shape&amp;lt;/text&amp;gt;&amp;lt;rect x=&quot;616&quot; y=&quot;170&quot; width=&quot;520&quot; height=&quot;360&quot; rx=&quot;14&quot; fill=&quot;var(--bg)&quot; stroke=&quot;var(--rule-strong)&quot; stroke-opacity=&quot;.45&quot; stroke-width=&quot;1.5&quot;/&amp;gt;&amp;lt;text x=&quot;646&quot; y=&quot;212&quot; style=&quot;font-family:var(--font-mono);font-size:17px;letter-spacing:2px&quot; fill=&quot;var(--fg)&quot;&amp;gt;WITH AGENTS&amp;lt;/text&amp;gt;&amp;lt;circle cx=&quot;680&quot; cy=&quot;278&quot; r=&quot;13&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.85&quot;/&amp;gt;&amp;lt;path d=&quot;M680 337 L692.35 359.75 L667.65 359.75 Z&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.85&quot;/&amp;gt;&amp;lt;rect x=&quot;668.3&quot; y=&quot;410.3&quot; width=&quot;23.400000000000002&quot; height=&quot;23.400000000000002&quot; rx=&quot;4&quot; fill=&quot;var(--fg-muted)&quot; opacity=&quot;.85&quot;/&amp;gt;&amp;lt;path d=&quot;M878 320 C884.6 343.4 884.6 343.4 908 350 C884.6 356.6 884.6 356.6 878 380 C871.4 356.6 871.4 356.6 848 350 C871.4 343.4 871.4 343.4 878 320 Z&quot; fill=&quot;var(--accent)&quot;/&amp;gt;&amp;lt;path d=&quot;M901.4 324.2 C903.5 331.4 903.5 331.4 911.0 333.8 C903.5 336.2 903.5 336.2 901.4 343.4 C899.3 336.2 899.3 336.2 891.8 333.8 C899.3 331.4 899.3 331.4 901.4 324.2 Z&quot; fill=&quot;var(--accent)&quot; opacity=&quot;.6&quot;/&amp;gt;&amp;lt;text x=&quot;878&quot; y=&quot;452&quot; text-anchor=&quot;middle&quot; style=&quot;font-family:var(--font-sans);font-size:15px;font-weight:600&quot; fill=&quot;var(--accent)&quot;&amp;gt;the agent is the interface&amp;lt;/text&amp;gt;&amp;lt;line x1=&quot;712&quot; y1=&quot;271&quot; x2=&quot;836&quot; y2=&quot;326.16&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;836&quot; y1=&quot;326.16&quot; x2=&quot;827.0023885033015&quot; y2=&quot;326.36733392023507&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;836&quot; y1=&quot;326.16&quot; x2=&quot;830.1290597919225&quot; y2=&quot;319.33855872464045&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;836&quot; y1=&quot;336.4&quot; x2=&quot;716&quot; y2=&quot;287&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.75&quot;/&amp;gt;&amp;lt;line x1=&quot;716&quot; y1=&quot;287&quot; x2=&quot;724.9882661562757&quot; y2=&quot;286.54057481136783&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.75&quot;/&amp;gt;&amp;lt;line x1=&quot;716&quot; y1=&quot;287&quot; x2=&quot;722.0598159669281&quot; y2=&quot;293.65421899601773&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.75&quot;/&amp;gt;&amp;lt;circle cx=&quot;776.0&quot; cy=&quot;313.7&quot; r=&quot;8&quot; fill=&quot;var(--accent)&quot; opacity=&quot;.9&quot;/&amp;gt;&amp;lt;line x1=&quot;712&quot; y1=&quot;343&quot; x2=&quot;836&quot; y2=&quot;342.0&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;836&quot; y1=&quot;342.0&quot; x2=&quot;827.8946337969994&quot; y2=&quot;345.9119098552057&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;836&quot; y1=&quot;342.0&quot; x2=&quot;827.832596799426&quot; y2=&quot;338.21932215611344&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;836&quot; y1=&quot;358.0&quot; x2=&quot;716&quot; y2=&quot;359&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.75&quot;/&amp;gt;&amp;lt;line x1=&quot;716&quot; y1=&quot;359&quot; x2=&quot;724.104314392216&quot; y2=&quot;355.0859115707331&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.75&quot;/&amp;gt;&amp;lt;line x1=&quot;716&quot; y1=&quot;359&quot; x2=&quot;724.1684191484301&quot; y2=&quot;362.7784823164282&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.75&quot;/&amp;gt;&amp;lt;path d=&quot;M776.0 352.5 L783.6 366.5 L768.4 366.5 Z&quot; fill=&quot;var(--accent)&quot; opacity=&quot;.9&quot;/&amp;gt;&amp;lt;line x1=&quot;712&quot; y1=&quot;415&quot; x2=&quot;836&quot; y2=&quot;357.84&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;836&quot; y1=&quot;357.84&quot; x2=&quot;830.2208787490229&quot; y2=&quot;364.7394026963574&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;836&quot; y1=&quot;357.84&quot; x2=&quot;827.000419523703&quot; y2=&quot;357.75310206760213&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.55&quot;/&amp;gt;&amp;lt;line x1=&quot;836&quot; y1=&quot;379.6&quot; x2=&quot;716&quot; y2=&quot;431&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.75&quot;/&amp;gt;&amp;lt;line x1=&quot;716&quot; y1=&quot;431&quot; x2=&quot;721.9649379651968&quot; y2=&quot;424.2605997988431&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.75&quot;/&amp;gt;&amp;lt;line x1=&quot;716&quot; y1=&quot;431&quot; x2=&quot;724.9938727748319&quot; y2=&quot;431.33204293417793&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.75&quot;/&amp;gt;&amp;lt;rect x=&quot;768.8&quot; y=&quot;400.1&quot; width=&quot;14.4&quot; height=&quot;14.4&quot; rx=&quot;4&quot; fill=&quot;var(--accent)&quot; opacity=&quot;.9&quot;/&amp;gt;&amp;lt;line x1=&quot;918&quot; y1=&quot;344&quot; x2=&quot;958&quot; y2=&quot;344&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.85&quot;/&amp;gt;&amp;lt;line x1=&quot;958&quot; y1=&quot;344&quot; x2=&quot;949.8633507218465&quot; y2=&quot;347.8464189221045&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.85&quot;/&amp;gt;&amp;lt;line x1=&quot;958&quot; y1=&quot;344&quot; x2=&quot;949.8633507218465&quot; y2=&quot;340.1535810778955&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.85&quot;/&amp;gt;&amp;lt;line x1=&quot;958&quot; y1=&quot;358&quot; x2=&quot;922&quot; y2=&quot;358&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.45&quot;/&amp;gt;&amp;lt;line x1=&quot;922&quot; y1=&quot;358&quot; x2=&quot;930.1366492781535&quot; y2=&quot;354.1535810778955&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.45&quot;/&amp;gt;&amp;lt;line x1=&quot;922&quot; y1=&quot;358&quot; x2=&quot;930.1366492781535&quot; y2=&quot;361.8464189221045&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.8&quot; opacity=&quot;.45&quot;/&amp;gt;&amp;lt;rect x=&quot;968&quot; y=&quot;298&quot; width=&quot;104&quot; height=&quot;104&quot; rx=&quot;10&quot; fill=&quot;none&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;2&quot;/&amp;gt;&amp;lt;circle cx=&quot;986&quot; cy=&quot;318&quot; r=&quot;5&quot; fill=&quot;var(--accent)&quot; opacity=&quot;1&quot;/&amp;gt;&amp;lt;rect x=&quot;999&quot; y=&quot;313.5&quot; width=&quot;56&quot; height=&quot;9&quot; rx=&quot;4.5&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.42&quot;/&amp;gt;&amp;lt;circle cx=&quot;986&quot; cy=&quot;348&quot; r=&quot;5&quot; fill=&quot;var(--accent)&quot; opacity=&quot;1&quot;/&amp;gt;&amp;lt;rect x=&quot;999&quot; y=&quot;343.5&quot; width=&quot;42&quot; height=&quot;9&quot; rx=&quot;4.5&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.42&quot;/&amp;gt;&amp;lt;circle cx=&quot;986&quot; cy=&quot;378&quot; r=&quot;5&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0&quot;/&amp;gt;&amp;lt;circle cx=&quot;986&quot; cy=&quot;378&quot; r=&quot;4.5&quot; fill=&quot;none&quot; stroke=&quot;var(--fg-muted)&quot; stroke-width=&quot;1.6&quot;/&amp;gt;&amp;lt;rect x=&quot;999&quot; y=&quot;373.5&quot; width=&quot;50&quot; height=&quot;9&quot; rx=&quot;4.5&quot; fill=&quot;var(--accent)&quot; opacity=&quot;0.2&quot;/&amp;gt;&amp;lt;text x=&quot;1020&quot; y=&quot;426&quot; text-anchor=&quot;middle&quot; style=&quot;font-family:var(--font-mono);font-size:17px&quot; fill=&quot;var(--fg)&quot;&amp;gt;issues&amp;lt;/text&amp;gt;&amp;lt;text x=&quot;876.0&quot; y=&quot;564&quot; text-anchor=&quot;middle&quot; style=&quot;font-family:var(--font-mono);font-size:19px&quot; fill=&quot;var(--fg)&quot;&amp;gt;everyone contributes — and reads — in their own shape&amp;lt;/text&amp;gt;&amp;lt;/svg&amp;gt;&amp;lt;figcaption&amp;gt;The UI&apos;s job moved to the agent, and the reason to pay moved with it&amp;lt;/figcaption&amp;gt;&amp;lt;/figure&amp;gt;&lt;/p&gt;
&lt;p&gt;A PM tool is a productivity tool, after all. The deal it offered was this: everyone bends to the one workflow the tool defines, and in exchange the UI makes the bending painless. That is why teams shopped on UI polish. The polish was the compensation. Agents break the deal from the other side: nobody has to bend anymore. Each of us works the way we feel most comfortable, and the friction between those ways, the communication bumps, the syncing, the back and forth, all gets ground away by the agent plus basic issue management.&lt;/p&gt;
&lt;p&gt;This made me think: in this era, what is the true value of a PM SaaS tool? I know that Linear has built more features, even shipped fancy agents that triage your issues and write your code. That is not catching up — it is a pivot: a bet that the thing worth paying for becomes managing agents, not people. Even Linear themselves now say &lt;a href=&quot;https://linear.app/next&quot;&gt;&quot;issue tracking is dead&quot;&lt;/a&gt;. Maybe they are right. But it is a different product than the one we left.&lt;/p&gt;
&lt;p&gt;There is a line in that essay I love:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Overhead kept growing, and the process became the work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That was exactly my life back then. The funny thing is, the same essay says Linear was always built on the opposite belief: remove the overhead, let teams focus on building. I believe they meant it. Their product is the best in class, and I felt that pain anyway. If the best product in the category could not remove the overhead, the problem was never the product. It was the category.&lt;/p&gt;
&lt;p&gt;And look at where their roadmap points: code review, coding sessions — straight into territory where Claude Code and Codex already sit, powered by the same frontier models Linear has to rent. Their edge is supposed to be context. But the context a PM tool holds is secondhand: statuses, priorities, conversations about the work. The ground truth is the code. Roadmaps, progress, docs are all projections of the repo now, and an agent can regenerate a projection whenever it goes stale. There is no moat in a layer your customers can derive, and not much point in managing one by hand either. Context grows where the work happens.&lt;/p&gt;
&lt;p&gt;In our case, the context lives in the repo. The agent became the interface. We stopped visiting Linear&apos;s UI and talked to it through MCP. Then we stopped paying for Linear and kept only the thin free layer underneath — the issues and projects API. Everything the subscription used to sell — the overview, the reminders, the coordination, even the pretty board — the agent now does, on top of a commodity. The SaaS didn&apos;t lose to a better SaaS. It got eaten from above.&lt;/p&gt;
&lt;p&gt;I personally feel pessimistic about project management SaaS tools. In a small team, I no longer see the point. To be fair: we are two engineers who live in the terminal, and between the two of us, we are also the entire management chain. Nobody to report up to, no compliance review, no teammate who never touches code. I have not run this in a fifty-person org — maybe the SaaS still earns its seat there. But we are not engineers dodging the management work; we own it, and delivery is on us. After watching what the agent ate in our tiny team, I would not bet on it.&lt;/p&gt;
</content:encoded></item></channel></rss>