惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
大猫的无限游戏
大猫的无限游戏
J
Java Code Geeks
罗磊的独立博客
雷峰网
雷峰网
G
Google Developers Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
爱范儿
爱范儿
B
Blog RSS Feed
腾讯CDC
Apple Machine Learning Research
Apple Machine Learning Research
D
Docker
Recent Announcements
Recent Announcements
T
Tailwind CSS Blog
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Vercel News
Vercel News
小众软件
小众软件
人人都是产品经理
人人都是产品经理
云风的 BLOG
云风的 BLOG
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
I
InfoQ
S
SegmentFault 最新的问题

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
I built a journaling app where the AI refuses to talk on ...
Vineet Negi · 2026-04-27 · via DEV Community
<p>Day 8 and 9 of building <a href="https://app-b3tuv7opfegx.appmedo.com/" rel="noopener noreferrer">Evengood</a> — a 60-second journaling app — for the <a href="https://medo.devpost.com/" rel="noopener noreferrer">Build with MeDo</a> $50K hackathon.</p> <p>This one took two days because I went the wrong way first.</p> <h2> The trap I almost fell into </h2> <p>By Day 7 I had what looked like a finished product. Voice capture, a calm Tomorrow line, a Kling watercolor keepsake, a 7-day streak ribbon, a Pattern detection that surfaces a one-line rhythm of your week, and a Quiet Mode that shuts the AI up entirely on grief days. v28. Working. Pretty. People could use it.</p> <p>So Day 8 the obvious move was: ship a small polish feature. Maybe surface yesterday's Tomorrow line in tiny grey above the mic. Maybe a "save the week" export. Maybe a streak ribbon that pulses on the dot for today. Safe. Iterative. The kind of thing every journaling app already has.</p> <p>I asked Claude to scan every competitor — Stoic, Day One, Reflectly, Daylio, Finch, Apple Journal, How We Feel, Mood Meter, Rosebud, Mindsera, Replika. Then to brainstorm a Day 8 feature that didn't exist in any of them.</p> <p>The first answer was a "Yesterday's Tomorrow" line. Surfacing yesterday's prompt above the mic. Soft. Tasteful. On-brand.</p> <p>I killed it.</p> <p>I didn't want to ship a feature that any other journaling app could build in an afternoon. Restraint is supposed to be the thesis of this product. If the thesis is real, there should be features the category structurally cannot copy. Not because they're hard — because they're contrary to how every other product makes money.</p> <p>So Day 8 became research and Day 9 became the build.</p> <h2> The thing nobody is building </h2> <p>Here is the structural observation. Every AI journaling app on the market is built around the assumption that the AI's job is to <strong>say more</strong>. Go deeper. Probe. Reframe. Suggest. Coach. Mentor. Advise.</p> <p>Rosebud's UVP is literally "go deeper." Mindsera's is mental models. Stoic's is mentor advice. Replika's whole pitch is the AI talking back. Their entire business model — and their reflex to ship more from the model — is <em>more output</em>.</p> <p>The 2025 Brown University study of LLM "therapy chatbots" catalogued fifteen distinct ethical violations across the major models. The biggest one they named was "deceptive empathy" — phrases like "I hear you" and "that sounds so hard" that mimic care without holding any. The category's failure mode is structural. The AI talks too much, in a register it doesn't actually have authority for.</p> <p>Quiet Mode (v28) was my first move against that. When the user types "my dad died last week" or "I had a panic attack," Evengood doesn't reframe. The model is silenced. The screen says: <em>"some days don't need reframing. be gentle with yourself tonight."</em> No popup. No emoji. No analytics event. It just gets quieter.</p> <p>That worked. It felt right. But it was content-triggered — only on heavy days. Calm days still got the full 2-minute reflection. Which meant Quiet Mode was a single point, not a thesis.</p> <p>For Day 9, I wanted the second point on the same axis.</p> <h2> Witness Mode </h2> <p>What I shipped is called Witness Mode. It runs once a week. By default Sunday evening, but you pick the day in settings.</p> <p>When you open the app on a Witness Night and reflect for 60 seconds — typed or voice — the response screen does not load a 2-minute reframe. It does not load a Tomorrow line. It does not load a theme word. It does not play TTS speech.</p> <p>Instead, the AI returns one sentence. Under fifteen words. About the <em>form</em> of what you just said — a recurring word, a surprising word, an absence — never the meaning. Then the screen sits with you in silence for four seconds. Then the closing line reads <code>come back tomorrow.</code> and that is the whole evening.</p> <p>Examples of what the model is allowed to return:</p> <ul> <li><em>i noticed you said the word "tired" four times</em></li> <li><em>tonight you didn't talk about work</em></li> <li><em>what stood out was the gap between what you wanted and what you did</em></li> <li><em>you returned to the word "ok" — three different times, three different ways</em></li> </ul> <p>That's it. No advice. No empathy phrases. No compliments. No follow-up questions. No interpretation of meaning.</p> <p>The role is borrowed almost verbatim from a forty-year-old practice in narrative therapy called <strong>outsider witnessing</strong>, formalized by Michael White and David Epston in <em>Maps of Narrative Practice</em> (2007). The witness's job is structurally bounded: never advise, never applaud, never interpret meaning, never offer follow-up. The witness names <em>what resonated</em> in the form of what the person said — and then sits with them.</p> <p>The category does not have a verb for this. The verbs are: track, write, coach, chat, record, reflect-back. None of them are <em>witness</em>. Now one is.</p> <h2> Why no competitor will copy this </h2> <p>This is not hard to build. The whole feature is one new lib file, a few new localStorage keys, one replaced LLM system prompt, one new regex guard module, two new settings rows, and a restructured response screen. About 2,800 lines of TypeScript change in total. MeDo did the build in seven minutes.</p> <p>The reason no one else has it is not technical. It is structural.</p> <p>A subscription journaling app with monthly retention metrics needs the AI to talk <em>more</em>, not less, on the days when nothing is wrong. Calm days are when streak-driven products quietly maximize session length. A weekly evening where the AI deliberately does <em>less</em> than on a normal night reads as a regression on every dashboard the category lives on.</p> <p>It is also philosophically backwards for them. Their pitch is the AI as coach, mentor, friend. A bounded outsider-witness role is not a brand position they can hold without rewriting their UVP.</p> <p>So the moat isn't the code. It's the position the code asserts.</p> <h2> The calm-tech lineage </h2> <p>While I was building this I went back and re-read Mark Weiser, John Seely Brown, and Amber Case's <a href="https://calmtech.com" rel="noopener noreferrer">calm-technology principles</a>. Weiser and Brown wrote that "technology should communicate but doesn't need to speak" and "the right amount of technology is the minimum needed to solve the problem." Case formalized eight of these principles in 2015 and now runs a Calm Tech Certified program.</p> <p>Evengood was always intuitively calm-tech. I just hadn't said it out loud. Witness Mode is the moment I'm naming the lineage. The thesis is: in 2026, an AI product that gets quieter as the user gets more vulnerable is not just a feature choice — it is a different product category from the one all my competitors are competing in.</p> <p>Streak research is moving the same way. Katy Milkman's recent work argues that streak design trains people to care more about not failing than about succeeding — surveillance dressed as resilience. Evengood has a 7-day streak ribbon, but it does not show a number. It does not lecture. It just fills a dot. Witness Mode entries fill the dot the same as any other entry, with no special badge, no count, no "you witnessed three times this month" notification. That last one would be gamification, which is forbidden by the spec.</p> <h2> What I deliberately did not build </h2> <p>This list is half the work.</p> <ul> <li>No "tonight is witness night" push notification. The reframe must be in the response, not in the prompt — pre-framing the capture screen would corrupt what the user says toward the system's expectation.</li> <li>No streak counter as a number. Anywhere.</li> <li>No "Witness Night" badge or progress ring.</li> <li>No mood-trend chart, no leaderboard, no level system, no XP.</li> <li>No popup, no toast, no red error state. Failure is silent fallback, every time. If the LLM returns something that violates any of the spec's word rules, a default line — <em>tonight, i'm just sitting with you</em> — is substituted on the client and the user never knows anything went wrong.</li> <li>No login, no account, no password, no SSO. None of the previous 28 versions had auth. This one didn't add it either.</li> <li>No Witness Mode firing on a heavy day. Quiet Mode supersedes always — a heavy-day signal must always route to Quiet, even if it happens to be Sunday. Witness is a calm-day ritual; Quiet is a heavy-day refusal. They never overlap.</li> </ul> <h2> The regex guards </h2> <p>Defense in depth. After the LLM returns its single sentence, the client runs five guards before rendering or storing:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">FORBIDDEN_WORDS</span> <span class="o">=</span> <span class="sr">/</span><span class="se">\b(</span><span class="sr">should|must|need|ought|try|maybe|perhaps</span><span class="se">)\b</span><span class="sr">/i</span><span class="p">;</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">EMPATHY_PHRASES</span> <span class="o">=</span> <span class="p">[</span> <span class="dl">"</span><span class="s2">i hear you</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">that sounds</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">i'm sorry</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">be kind to yourself</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">you could</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">you should</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">i feel</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">you feel</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">must be</span><span class="dl">"</span><span class="p">,</span> <span class="p">];</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">ALLOWED_OPENERS</span> <span class="o">=</span> <span class="p">[</span> <span class="dl">"</span><span class="s2">i noticed</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">tonight</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">what stood out was</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">you said the word</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">tonight you didn't</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">you returned to</span><span class="dl">"</span><span class="p">,</span> <span class="p">];</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">DEFAULT_LINE</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">tonight, i'm just sitting with you</span><span class="dl">"</span><span class="p">;</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">MAX_WORDS</span> <span class="o">=</span> <span class="mi">15</span><span class="p">;</span> </code></pre> </div> <p>If the line exceeds 15 words, contains a forbidden word, contains an empathy phrase, doesn't start with an allowed opener, or contains CJK characters (English-only enforcement), it is silently swapped for <code>DEFAULT_LINE</code>. Sentry breadcrumb. No console error. No user-visible failure.</p> <p>This is the part the LLM industry mostly does not do. The system prompt tells the model what the role is. The client guard refuses to render anything that violates the role even if the model drifts. Trust the model less than the spec.</p> <h2> What ships next </h2> <p>Day 10 onward, on the same spectrum:</p> <ul> <li>A v40-ish feature called <strong>anonymous definitional ceremonies</strong> — opt-in, three users on the same day each pick the same theme word, and on Sunday evening each one sees one anonymous line from another about how that word sat with them. No content shared. No chat. No identity. Just three witnesses, one word, one Sunday. This is the long arc Witness Mode is the seed of.</li> <li>A feature called <strong>The Wake</strong> — programmable forgetting. Reflections expire after a window the user sets unless they explicitly mark them "kept." Default: let the day go. The opposite of every product whose business model depends on the user's data accumulating forever.</li> <li>A feature called <strong>Letter to Tomorrow</strong> — once a month, instead of a Tomorrow line, you write one to your future self. Sealed. Returned on a date you pick. No notification. No reminder. It just appears.</li> </ul> <p>All three obey the same axis. Restraint as feature, not absence.</p> <h2> Try it </h2> <p><a href="https://app-b3tuv7opfegx.appmedo.com/" rel="noopener noreferrer">https://app-b3tuv7opfegx.appmedo.com/</a></p> <p>Open settings. There's a new section called <em>evening</em>. Toggle on <code>witness night</code>. Pick a day. Come back that evening.</p> <p>If the model returns one sentence about a word you used, and the screen sits still for four seconds, and nothing else happens, the feature worked.</p> <p>That is the entire feature.</p> <p>— Day 9 of 30. v29. #BuiltWithMeDo</p>