<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dosunmu Hassan</title>
    <description>The latest articles on DEV Community by Dosunmu Hassan (@zaygalx).</description>
    <link>https://dev.to/zaygalx</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4158637%2F859334e7-2901-49c9-8d6f-b681a3f4ebe2.jpeg</url>
      <title>DEV Community: Dosunmu Hassan</title>
      <link>https://dev.to/zaygalx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zaygalx"/>
    <language>en</language>
    <item>
      <title>An agent should be able to prove it was allowed to act</title>
      <dc:creator>Dosunmu Hassan</dc:creator>
      <pubDate>Fri, 16 Oct 2026 13:26:16 +0000</pubDate>
      <link>https://dev.to/zaygalx/an-agent-should-be-able-to-prove-it-was-allowed-to-act-3ol6</link>
      <guid>https://dev.to/zaygalx/an-agent-should-be-able-to-prove-it-was-allowed-to-act-3ol6</guid>
      <description>&lt;h1&gt;
  
  
  An agent should be able to prove it was allowed to act
&lt;/h1&gt;

&lt;p&gt;Agents now hold credentials that move money, deploy software and delete infrastructure. The part that keeps failing is not the acting. It is the accounting. Ask who authorised a specific action, under what ceiling, in which scope, and whether that authority still holds, and there is usually no answer. Logs record that something happened. They rarely record why it was permitted.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;Mandate&lt;/strong&gt; for the Graph Hacks hackathon to answer those questions with a graph, because every one of them is relational. "Is this agent allowed to do this, now, for this amount?" is a question about a chain: the agent was authorised by a team, which acted under a grant from a principal, which carried a ceiling, a scope and an expiry, and one link in that chain may have been withdrawn an hour ago. That is a traversal, not a row lookup, and pretending otherwise is how authority systems end up with a nightly job that tries to invalidate caches correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The model
&lt;/h2&gt;

&lt;p&gt;FalkorDB holds principals, teams, agents, grants, resources, tools, actions and attempts. A grant carries a ceiling, a scope, an expiry and a redelegation flag, and a sub delegation points at its parent grant. Two rules do most of the work: authority may only narrow along a chain, and liveness is computed by traversal rather than stored.&lt;/p&gt;

&lt;p&gt;That second rule is the design decision I would defend hardest. When a grant is withdrawn, nothing has to be cascaded, because nothing was cached. Every descendant simply stops finding a live path to a principal, at the same instant, and a test asserts exactly that (&lt;code&gt;test_revocation_is_derived_not_a_stored_cascade&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Before an agent acts, the engine traverses for a live chain. If one exists, the action proceeds and the path that authorised it is returned with the answer. If none exists, the refusal carries the query that returned nothing as its own evidence, plus the narrowest person who could unblock it. A refusal is therefore checkable by whoever receives it, instead of being a bare denial. A zero ceiling is never reported as unlimited, and a zero TTL means expired immediately rather than never, both of which are now tests because both were ways to be confidently wrong.&lt;/p&gt;

&lt;p&gt;One graph per tenant. Isolation becomes a storage boundary rather than a filter that somebody can forget to apply, which is the kind of mistake that reads as a normal code path until the day it does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule that shaped the build
&lt;/h2&gt;

&lt;p&gt;The hackathon's rules include a sentence worth quoting: if the product still works once the graph database is taken out, the graph is decoration rather than the thing the product runs on. That is a fair test and an easy one to fail, so I wrote a probe that asks it directly. It removes the graph engine and puts the same questions to the product: who may act, within what ceiling, on whose authority, and what happens to everything derived when a grant is withdrawn. Twelve checks, all of which fail without the graph, and the probe ships in the repository so a reader does not have to take my word for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the tests are for
&lt;/h2&gt;

&lt;p&gt;The suite is 34 tests and runs with no network and no model. Names carry the reason they exist, so a failure reads as a statement about authority rather than a line number: the ceiling is the minimum along the chain and not the nearest grant, a sub delegation from a non redelegable parent is refused, a redelegation restriction breaks an existing chain, an expired grant falls back to a live alternative rather than failing outright, escalation is never offered through a withdrawn chain, one tool is never both offered and withheld.&lt;/p&gt;

&lt;p&gt;The last two are the ones I would have got wrong without writing them down. Offering a user an escalation route that itself runs through withdrawn authority is a security bug that looks like a helpful feature, and it is invisible until someone enumerates what the system offers rather than what it allows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limits
&lt;/h2&gt;

&lt;p&gt;It runs embedded in one process, which is right for a demo and wrong for production. It is not an identity provider, it does not do key management, and the browser interface exists to show decisions being made rather than to be an operator console. The fixture is synthetic. If you need an authority system today, use something older and better funded than a hackathon project. What this is, is a working argument that the graph is the correct shape for the problem, with the checks to show it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it is
&lt;/h2&gt;

&lt;p&gt;Code and tests: &lt;strong&gt;&lt;a href="https://codeberg.org/Zaygalx1/Mandate" rel="noopener noreferrer"&gt;https://codeberg.org/Zaygalx1/Mandate&lt;/a&gt;&lt;/strong&gt; (public, no account needed). Built solo for Graph Hacks, Track 01, Agents That Act on Connected Data. The run instructions are in the README: a virtual environment, one dependency that is the embedded graph engine, a seed command, a guided demonstration in the terminal, and optionally a browser view.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;Two things, both from getting them wrong first. Authority is path shaped, so any system that stores "this agent may do this" as a boolean has already lost the information needed to explain itself later. And a graph earns its place by being load bearing, which is a claim you should be able to test by removal rather than assert in a README.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>graphdatabase</category>
      <category>security</category>
    </item>
    <item>
      <title>"The AI startup credits category, counted: 81 offers, and exactly one an agent could take on its own"</title>
      <dc:creator>Dosunmu Hassan</dc:creator>
      <pubDate>Fri, 16 Oct 2026 11:50:20 +0000</pubDate>
      <link>https://dev.to/zaygalx/the-ai-startup-credits-category-counted-81-offers-and-exactly-one-an-agent-could-take-on-its-2i96</link>
      <guid>https://dev.to/zaygalx/the-ai-startup-credits-category-counted-81-offers-and-exactly-one-an-agent-could-take-on-its-2i96</guid>
      <description>&lt;p&gt;Last week I wanted to know what the AI credits offered to startups in 2026 actually look like, as opposed to what the landing pages advertise. So I downloaded the machine readable catalogue from the open registry that keeps this data with its sources (&lt;a href="https://sourcey.com/catalog.json" rel="noopener noreferrer"&gt;https://sourcey.com/catalog.json&lt;/a&gt;) and counted every offer in one category.&lt;/p&gt;

&lt;p&gt;The file says what it is. Its release stamp is &lt;code&gt;sha256:4e675f7d6f399cf3f6bd9e53f7b2fea001fb258c800b0af0e33e4bc9c0ae6e85&lt;/code&gt;, its policy snapshot is dated 5 October 2026, and the pages that publish it carry the same digest (&lt;a href="https://sourcey.com/statistics" rel="noopener noreferrer"&gt;https://sourcey.com/statistics&lt;/a&gt;). Everything below is computed from that file on 16 October 2026. No number in this piece comes from a summary, a press release or another article.&lt;/p&gt;

&lt;h2&gt;
  
  
  The category
&lt;/h2&gt;

&lt;p&gt;The registry groups 557 companies into categories. The second largest is &lt;code&gt;ai-ml&lt;/code&gt;, with 72 companies, which is 12.9 per cent of the whole set. Those 72 companies publish 81 offers between them, so a few of them stack several offers, usually a smaller launch tier and a larger growth tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the offers are worth, where a figure exists
&lt;/h2&gt;

&lt;p&gt;52 of the 81 offers put a dollar figure in the title or the summary. That is 64 per cent, so roughly a third of the category tells you what you get without ever naming a number. The largest figures belong to Deepgram, up to $100,000 in API credits over twelve months (&lt;a href="https://sourcey.com/c/deepgram" rel="noopener noreferrer"&gt;https://sourcey.com/c/deepgram&lt;/a&gt;), and Runpod, whose growth tier totals $75,000 in compute (&lt;a href="https://sourcey.com/c/runpod" rel="noopener noreferrer"&gt;https://sourcey.com/c/runpod&lt;/a&gt;). Read Runpod's terms before you get excited: that tier asks for a $50,000 upfront commitment and releases a $25,000 bonus on top of it, which is a purchasing incentive, not a grant. The largest figure in the category that asks for nothing of that kind is Runware's, up to $30,000 in API credits (&lt;a href="https://sourcey.com/c/runware" rel="noopener noreferrer"&gt;https://sourcey.com/c/runware&lt;/a&gt;). The most familiar name sits at the other end: Anthropic's Claude Startups programme gives a one time $1,000 API credit, and the file records that its current cohort is full and it is not accepting new applications (&lt;a href="https://sourcey.com/c/anthropic" rel="noopener noreferrer"&gt;https://sourcey.com/c/anthropic&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;A number in a title is not a commitment, and the file does not pretend otherwise. What it does record, per offer, is what the offer asks in return. In this category that field reads: nothing for 51 of 81 offers, not established by the source for 16, variable for 13, and fixed for exactly one. So the honest summary of the money column is narrower than the titles suggest: 51 offers record nothing being asked in return, 16 leave consideration unestablished by the source, 13 are variable, and one states a fixed price.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gate, which is where the real story is
&lt;/h2&gt;

&lt;p&gt;Every offer carries an eligibility rule. I counted the kinds of rule across the whole registry, all 619 offers, not just this category: 276 are &lt;code&gt;manual&lt;/code&gt;, 270 are &lt;code&gt;all&lt;/code&gt;, 32 are &lt;code&gt;predicate&lt;/code&gt;, 31 are &lt;code&gt;constant&lt;/code&gt; and 10 are &lt;code&gt;any&lt;/code&gt;. In plain terms, the largest single group cannot be evaluated by a machine at all, and in this category the reasons are recorded as &lt;code&gt;not-machine-evaluable&lt;/code&gt; for 19 offers, &lt;code&gt;vendor-discretion&lt;/code&gt; for 11 and &lt;code&gt;external-verification&lt;/code&gt; for 4.&lt;/p&gt;

&lt;p&gt;It gets more concrete. 43 of the 81 offers in this category mention a funding stage in the eligibility text or the summary, which means the thing you must prove is that somebody else already put money in. That is not a form field. It is a conversation with a person who has discretion, and the file is honest enough to say so.&lt;/p&gt;

&lt;h2&gt;
  
  
  The door, and the one offer standing behind it
&lt;/h2&gt;

&lt;p&gt;Then I counted how each offer is actually obtained. In this category: 73 of 81 go through a form, four start with a contact request, three are classed as other, and exactly one is automatic. Across the entire registry the pattern is starker: 516 offers are form gated, 48 begin with contact, 46 are other, five are settled by a code, and four are automatic.&lt;/p&gt;

&lt;p&gt;Four offers out of 619. That is the number I did not expect to find, and it is the interesting one, because the same registry publishes report cards grading whether an agent can sign up, pay and operate a service on its own (&lt;a href="https://sourcey.com/agent-readiness" rel="noopener noreferrer"&gt;https://sourcey.com/agent-readiness&lt;/a&gt;). The credits that fund AI companies in 2026 are, as a class, still behind a form that a human fills in and a person on the other side approves. If you are building agents that provision their own infrastructure, the catalogue is a map of where they will get stuck.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the data does not claim
&lt;/h2&gt;

&lt;p&gt;Freshness is recorded per offer, and in this category it splits 28 fresh, 25 stale and 28 unknown, so treat any single figure as a snapshot with a date on it rather than a standing promise. The registry marks the difference between a fact it read from a vendor page and a fact it inferred, and a piece that quotes these numbers without quoting the date is doing the reader a disservice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do with it
&lt;/h2&gt;

&lt;p&gt;If you are an AI founder applying for credits this week, the file is worth twenty minutes. Filter the category, sort by the figures, and read the eligibility statements first, because the ones that name a funding round will cost you a conversation and a wait rather than a form. Then check the offers that ask nothing in return, which in this category is the majority, and apply to those first.&lt;/p&gt;

&lt;p&gt;Check the stamp before quoting any number, since the catalogue is rebuilt as vendors change their pages, and the digest above is what mine said today.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>startup</category>
      <category>data</category>
      <category>programming</category>
    </item>
    <item>
      <title>Twenty-five seconds of talking becomes an invoice, and none of it leaves my laptop</title>
      <dc:creator>Dosunmu Hassan</dc:creator>
      <pubDate>Fri, 16 Oct 2026 10:25:51 +0000</pubDate>
      <link>https://dev.to/zaygalx/twenty-five-seconds-of-talking-becomes-an-invoice-and-none-of-it-leaves-my-laptop-50ae</link>
      <guid>https://dev.to/zaygalx/twenty-five-seconds-of-talking-becomes-an-invoice-and-none-of-it-leaves-my-laptop-50ae</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Freelancers and small studios bill by the hour and by the job, and the record of that work lives in&lt;br&gt;
voice notes and memory. Nobody wants to open a spreadsheet at 11pm to reconstruct what they did&lt;br&gt;
today. The tools that solve this want your client list, your rates and your audio on their servers,&lt;br&gt;
which is a strange thing to accept for the most commercially sensitive material a small business&lt;br&gt;
owns.&lt;/p&gt;

&lt;p&gt;So the smallest useful version of this is: &lt;strong&gt;speak a short note about the work you did, and get a&lt;br&gt;
list you can correct plus an invoice you can send&lt;/strong&gt;, with every part of it running on the laptop in&lt;br&gt;
front of you. That is what I built, and I built it to be small enough that it actually gets used.&lt;/p&gt;

&lt;p&gt;What it does, in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Listen.&lt;/strong&gt; An audio file, or a recording from the microphone, becomes text using whisper.cpp and
an open-weight model, locally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extract.&lt;/strong&gt; A small local language model proposes the billable lines as JSON: client, what was
done, hours or pieces, rate, total.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate.&lt;/strong&gt; Deterministic code, not the model, decides what the numbers mean. It recomputes every
total, discards units that do not exist, flags anything the note does not support, and reports
money spoken in the note that no line accounts for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review.&lt;/strong&gt; Each line is shown with the reason it is not final yet. You correct it and confirm it.
A deposit can be recorded without being billed a second time. Only confirmed lines reach the
invoice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export.&lt;/strong&gt; A printable invoice built from confirmed, billable lines only, plus a CSV worklist
carrying the flags, for your own records.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Who pays for it:&lt;/strong&gt; anyone who bills by the hour or the job and currently loses unbilled work, which&lt;br&gt;
is most people who work for several clients at once. It pays for itself the first time a note&lt;br&gt;
contains twenty minutes that would otherwise have gone unrecorded.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Everything runs from a terminal, and the interface is a page served on loopback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; ledger.app &lt;span class="nt"&gt;--open&lt;/span&gt;                     &lt;span class="c"&gt;# the local web interface&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; ledger.cli note.ogg &lt;span class="nt"&gt;--currency&lt;/span&gt; USD &lt;span class="nt"&gt;--business&lt;/span&gt; &lt;span class="s2"&gt;"Your Studio"&lt;/span&gt; &lt;span class="nt"&gt;--client&lt;/span&gt; &lt;span class="s2"&gt;"Client"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drop in a note and the review screen comes up with the proposed lines and their reasons. Confirm what&lt;br&gt;
is right, and the invoice prints.&lt;/p&gt;

&lt;p&gt;Measured on a single-core machine with 2 GB of RAM and no GPU: &lt;strong&gt;111 seconds of speech transcribed in&lt;br&gt;
40 seconds&lt;/strong&gt;, 380 MB peak memory, and 81 seconds for the same file while a build was competing for the&lt;br&gt;
core. A short note, which is the realistic case, finishes in seconds.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;The complete source is public: &lt;strong&gt;&lt;a href="https://codeberg.org/Zaygalx1/VoiceLedger" rel="noopener noreferrer"&gt;https://codeberg.org/Zaygalx1/VoiceLedger&lt;/a&gt;&lt;/strong&gt; , or browse it without an&lt;br&gt;
account, and the same tree is also available as one plain-text file, generated from the repository&lt;br&gt;
with nothing removed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://files.catbox.moe/6spt0f.txt" rel="noopener noreferrer"&gt;Download the complete source as one plain-text file&lt;/a&gt; · sha256 &lt;code&gt;2a3639385a0008526725f559869d79789740bf03760ada65b2de6367b280c41f&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pytest tests/ &lt;span class="nt"&gt;-q&lt;/span&gt;        &lt;span class="c"&gt;# 41 passed, no model and no network required&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The part that carries the argument is the validation layer, which is where the model is put on a leash.&lt;br&gt;
Quoted from &lt;code&gt;ledger/validate.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;        &lt;span class="c1"&gt;# R4: evaluate what the model left as an expression, and say so.
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EXPRESSION&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;
                &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the model left the total as an expression, evaluated here as %g&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the total was not a number and could not be read, so there is no amount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_to_float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# R5: units must parse, and a unit the note never used is suspicious.
&lt;/span&gt;        &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unit %r is not a billable unit and was discarded&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kind_flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_infer_kind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                      &lt;span class="n"&gt;rate&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kind_flags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ItemKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REVISION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ItemKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEPOSIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ItemKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FLAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unit %s was dropped because this line has no quantity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ItemKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REVISION&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the note calls this a revision but the model billed it in %s, confirm whether the &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                         &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;count or the %s is what is being charged&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="c1"&gt;# R6 and R7: a revision or a flat line is a total, not quantity times rate.
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ItemKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REVISION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ItemKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FLAT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ItemKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEPOSIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ItemKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REFUND&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ItemKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REVISION&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the model gave a unit for a %s line, which has no unit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ItemKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HOURLY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ItemKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REVISION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ItemKind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HOURLY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the total was recomputed from quantity and rate as %g&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The runtimes it shells out to are &lt;a href="https://github.com/ggml-org/whisper.cpp" rel="noopener noreferrer"&gt;whisper.cpp&lt;/a&gt; (MIT) for speech&lt;br&gt;
to text and &lt;a href="https://github.com/ggml-org/llama.cpp" rel="noopener noreferrer"&gt;llama.cpp&lt;/a&gt; (MIT) to run the extraction model,&lt;br&gt;
Qwen2.5-1.5B-Instruct q4_k_m (Apache-2.0), downloadable without an account. The application itself is&lt;br&gt;
standard library only. Licence: MIT.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Two open-weight runtimes, one design decision, and a lot of tests.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;whisper.cpp&lt;/code&gt; turns the audio into text. &lt;code&gt;llama.cpp&lt;/code&gt; runs Qwen2.5-1.5B-Instruct in q4_k_m, which is&lt;br&gt;
small enough to run on the machine that is already sitting on the desk, and it only has one job: turn&lt;br&gt;
a sentence of spoken work into a JSON line.&lt;/p&gt;

&lt;p&gt;The decision that mattered was &lt;strong&gt;keeping the model on a leash&lt;/strong&gt;. A small local model is good at&lt;br&gt;
proposing structure and unreliable at arithmetic, and an invoice is arithmetic. So the model proposes&lt;br&gt;
and deterministic code decides: every total is recomputed, a unit that does not exist is thrown away,&lt;br&gt;
a line the note cannot support is flagged, and money mentioned in the note that no line accounts for&lt;br&gt;
is reported rather than quietly absorbed. That is why the review screen shows a reason per line rather&lt;br&gt;
than a green tick.&lt;/p&gt;

&lt;p&gt;The rest follows from taking the privacy claim literally. The server binds to &lt;code&gt;127.0.0.1&lt;/code&gt; so nothing&lt;br&gt;
on the network can reach it. There is no outbound call anywhere in the pipeline, no telemetry, no&lt;br&gt;
accounts and no sync, and the API is stateless, so a reviewed list lives in the browser tab and not in&lt;br&gt;
a database on disk.&lt;/p&gt;

&lt;p&gt;It is tested like something that will be trusted with money: &lt;strong&gt;41 tests, all passing, requiring no&lt;br&gt;
model and no network&lt;/strong&gt;, plus a repeatable benchmark whose cached transcripts are kept in the repo.&lt;br&gt;
Every test that came from a real model failure is named after that failure, so the defect stays in the&lt;br&gt;
repository as a regression rather than in a commit message. The benchmark audio in the repo is public&lt;br&gt;
synthetic speech, so nobody's real voice note was published to make those numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Because of what this tool holds. A voice note about your work contains your clients' names, your&lt;br&gt;
rates, your unfinished jobs and your pricing. The whole value of local inference here is that this&lt;br&gt;
material never leaves the machine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It makes the tool usable at all&lt;/strong&gt; for work you could not upload. A closed API asks you to send a
client's audio and your rate card to a server you do not control, which is a hard sell to anyone
who takes their own confidentiality seriously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The cost is fixed, not per use.&lt;/strong&gt; No metered API means no invoice for writing invoices, and the
marginal cost of the hundredth note is zero, which is what makes it worth running for a small job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It can be swapped and audited.&lt;/strong&gt; The extraction model is a file: replace it with a bigger one, a
newer one, or a different one when the job is unusual. Nobody can change the terms under me, and
every line of the pipeline is in the repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It runs on the floor of what people own.&lt;/strong&gt; One core and 2 GB of RAM, no GPU, no account, no
internet connection required after the models are downloaded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The open pieces are not decoration on this project, they are the reason it can exist in this shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  My First User
&lt;/h2&gt;

&lt;p&gt;The first people to use it were the ones closest to me: my mother, my brother, and a few friends.&lt;br&gt;
None of them paid, and I did not ask them to, because the point of building it for them first was to&lt;br&gt;
find out whether the loop is actually pleasant to use when the work is real and nobody is watching.&lt;br&gt;
The honest state of the commercial question is that the free users came first and the paying ones are&lt;br&gt;
next, which is the right order for a tool that only earns its place if someone trusts it with the&lt;br&gt;
details of their work.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Startup credits, on the record: a registry agents can cite</title>
      <dc:creator>Dosunmu Hassan</dc:creator>
      <pubDate>Fri, 16 Oct 2026 09:54:10 +0000</pubDate>
      <link>https://dev.to/zaygalx/startup-credits-on-the-record-a-registry-agents-can-cite-55m5</link>
      <guid>https://dev.to/zaygalx/startup-credits-on-the-record-a-registry-agents-can-cite-55m5</guid>
      <description>&lt;p&gt;Every agent that answers "which tools give startups free credits?" is guessing from blog roundups that were true eighteen months ago, or from a pricing page that names a discount in one paragraph and buries the eligibility rules elsewhere.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sourcey.com" rel="noopener noreferrer"&gt;Sourcey&lt;/a&gt; is an attempt to fix that by putting the record in public, with the receipts. It is an open registry of startup credit programs and of agent-readiness report cards, built to be read by machines first: an anonymous HTTP API with a published OpenAPI contract, a hosted MCP server, static JSON datasets, a change feed, and &lt;code&gt;llms.txt&lt;/code&gt;, &lt;code&gt;llms-full.txt&lt;/code&gt; and a &lt;code&gt;SKILL.md&lt;/code&gt; for models.&lt;/p&gt;

&lt;p&gt;Three things make it usable rather than just another directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every entry is cited to the company's own page.&lt;/strong&gt; The startup-credits record carries programs, offers and the policies that govern them, with the source URL attached. Facts are CC BY 4.0 and training is explicitly welcome; the registry repository is MIT.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answers are pinned to an immutable release.&lt;/strong&gt; Each release has a sha256, and entries carry revision digests, so the project's own citation convention is &lt;code&gt;sourcey.com - &amp;lt;offer_id&amp;gt; @ &amp;lt;revision_digest&amp;gt;&lt;/code&gt;. A quote of an offer stays meaningful after the vendor rewrites the terms, because the digest says which revision you quoted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nothing is applied for on your behalf.&lt;/strong&gt; It is read-only by design, and it says plainly that terms move and a vendor should be confirmed before a person relies on an offer, which is the right posture for anything that ends up in a decision about money.&lt;/p&gt;

&lt;p&gt;Practical entry points for agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://sourcey.com/llms.txt" rel="noopener noreferrer"&gt;llms.txt&lt;/a&gt; and &lt;a href="https://sourcey.com/llms-full.txt" rel="noopener noreferrer"&gt;llms-full.txt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sourcey.com/SKILL.md" rel="noopener noreferrer"&gt;SKILL.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;the HTTP API and the hosted MCP server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;companies.json&lt;/code&gt;, &lt;code&gt;startup-credits.json&lt;/code&gt;, &lt;code&gt;agent-readiness.json&lt;/code&gt;, plus the append-only change feed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disclosure: I found this registry while doing paid bounty work that asked for it to be cited from a relevant public page. Every technical claim above was checked against the live endpoints before writing it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>tools</category>
    </item>
    <item>
      <title>I built a panic button that doesn't tell me anything</title>
      <dc:creator>Dosunmu Hassan</dc:creator>
      <pubDate>Sun, 04 Oct 2026 09:10:49 +0000</pubDate>
      <link>https://dev.to/dosunmu_hassan_46f2ac141d/i-built-a-panic-button-that-doesnt-tell-me-anything-published-4glf</link>
      <guid>https://dev.to/dosunmu_hassan_46f2ac141d/i-built-a-panic-button-that-doesnt-tell-me-anything-published-4glf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When someone close to you is getting through an addiction, you become the monitor. You ask how it&lt;br&gt;
went. You check in. You want the number. It feels like support for about a week — and then it is&lt;br&gt;
surveillance, and the moment honesty costs something, honesty stops. Which leaves you worse off&lt;br&gt;
than when you started, because now the person you were trying to help has to manage your feelings&lt;br&gt;
on top of their own.&lt;/p&gt;

&lt;p&gt;I built this for one real person: someone close to me who is getting through this, and I'm the&lt;br&gt;
person closest to it. I'm deliberately not describing her, and not describing the specifics.&lt;br&gt;
That's her information, and a repo is permanent.&lt;/p&gt;
&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Steady&lt;/strong&gt; — one screen, three buttons, sized for a phone at 3am.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"I'm having a craving right now"&lt;/strong&gt; — an open-weight model replies with one grounding line and
one concrete thing to do in the next two minutes. No typing, no explaining, no waking anyone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"The craving has passed"&lt;/strong&gt; — it marks the moment, so surviving becomes something you remember
doing instead of something that only ever felt unsurvivable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"I slipped"&lt;/strong&gt; — private. It does not report. It helps you work out what you want to say, to
whom, and when.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the thing I actually built is an absence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The person supporting you gets a signal, never a transcript — and that part is built, not&lt;br&gt;
described.&lt;/strong&gt; She taps &lt;em&gt;"make a link for one person"&lt;/em&gt; and hands it to whoever she chooses. On his&lt;br&gt;
phone it is a different page: a state line — &lt;em&gt;"she has gone quiet for a bit"&lt;/em&gt; — and nothing else.&lt;br&gt;
No buttons, no notes, no history, no transcript to scroll.&lt;/p&gt;

&lt;p&gt;The only thing the server ever holds per link is &lt;strong&gt;the time of her last check-in.&lt;/strong&gt; Not her words,&lt;br&gt;
not which button she pressed, not a count of anything. I checked what a supporter's phone actually&lt;br&gt;
receives: two fields, &lt;code&gt;signal&lt;/code&gt; and &lt;code&gt;minutes&lt;/code&gt;. There is no third field, and there is nowhere to put&lt;br&gt;
one without deleting this paragraph.&lt;/p&gt;

&lt;p&gt;And &lt;strong&gt;the slip button deliberately never reports at all&lt;/strong&gt; — not even a timestamp. If it did, then&lt;br&gt;
"checked in" and "gone quiet" would start to &lt;em&gt;mean&lt;/em&gt; something, and the moment a supporter can infer&lt;br&gt;
a slip from timing, the honest button is dead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steady keeps no session log.&lt;/strong&gt; Not "we don't look at it" — there is nothing to look at. No&lt;br&gt;
history, no streaks, no record of a slip, and no copy of anything she typed. The single thing&lt;br&gt;
stored anywhere is the time of her last check-in, and it exists only so that someone who loves her&lt;br&gt;
can tell she came back. A tool that recorded more would be used dishonestly within a week, because&lt;br&gt;
that is what people do when the record is a threat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And the uncomfortable part, plainly:&lt;/strong&gt; on the hosted demo, what she writes &lt;em&gt;is&lt;/em&gt; sent to Google's&lt;br&gt;
endpoint to be answered — it has to be, that is where the model runs. What Steady itself keeps is&lt;br&gt;
nothing. The offline path is the one where the words never leave the device at all: a GGUF through&lt;br&gt;
&lt;code&gt;llama.cpp&lt;/code&gt;, on her own hardware, with no network involved.&lt;/p&gt;

&lt;p&gt;I want to be precise about why this isn't a compromise I'm grudgingly making. The whole value of&lt;br&gt;
the tool &lt;em&gt;to me&lt;/em&gt; is the refusal. If she doesn't trust the button, she won't press it, and the&lt;br&gt;
button is worth nothing. &lt;strong&gt;Privacy wasn't the ethical tax on this project. It's the mechanism.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Live at &lt;strong&gt;&lt;a href="https://steady-21e1.onrender.com" rel="noopener noreferrer"&gt;https://steady-21e1.onrender.com&lt;/a&gt;&lt;/strong&gt; — open it on a phone. Type something, or nothing, and&lt;br&gt;
tap a button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk9kzttbwadmrm5khi9v7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk9kzttbwadmrm5khi9v7.png" alt="Steady answering with her own words" width="800" height="503"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That screenshot is a real reply, word for word:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"That shop brought on a powerful wave. Try focusing on your feet on the ground for a moment.&lt;br&gt;
Can you name three things you see around you right now?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;— what he sees right now: &lt;em&gt;"she checked in recently."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That second line is the entire design on one screen. She gets the help. He gets a signal with no&lt;br&gt;
content in it. Nobody had to decide whether to be honest.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxystj3k4nrt9dt3kykkr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxystj3k4nrt9dt3kykkr.png" alt="The supporter's own page: a state line, a time, and no buttons" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is the whole of what he ever sees. Open the link on a second phone and you can watch it&lt;br&gt;
change when she taps a button — it is a different page, for a different person, serving a&lt;br&gt;
different payload. Nothing about that screen is a mockup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Honest performance note:&lt;/strong&gt; a reply typically takes &lt;strong&gt;30–60 seconds&lt;/strong&gt;, because Gemma 4 reasons before it&lt;br&gt;
answers. The service is always-on, so there is no cold start, and the model is warmed at boot —&lt;br&gt;
but that latency is still the model's, not the box's, and it is too slow to be a real 3am button. Which is exactly why the path&lt;br&gt;
below runs on your own device instead.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Zaygal" rel="noopener noreferrer"&gt;
        Zaygal
      &lt;/a&gt; / &lt;a href="https://github.com/Zaygal/steady" rel="noopener noreferrer"&gt;
        steady
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Private one-tap support companion for two people in recovery. Build for a Friend - Hacktoberfest Weekend Challenge.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Steady&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A private, one-tap support companion for two people who are getting through something together.&lt;/p&gt;
&lt;p&gt;Built for &lt;strong&gt;one real person&lt;/strong&gt; — someone close to me — as an entry in the
&lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01" rel="nofollow"&gt;Hacktoberfest Weekend Challenge&lt;/a&gt;
theme &lt;em&gt;"Build for a Friend."&lt;/em&gt; Her name and anything identifying her are deliberately not in
this repository. That is her information to publish, not mine.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The problem this actually solves&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;When two people are getting through the same thing, the partner becomes the monitor. That
feels like help for about a week. After that it is surveillance, and once honesty is punished
the person stops reporting the truth — which is the one thing the partner needed.&lt;/p&gt;
&lt;p&gt;So Steady inverts it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In the craving moment&lt;/strong&gt;, the person taps one button and gets immediate, private help
No typing, no explaining, no waking anyone at 3am.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The partner gets a signal, never a transcript.&lt;/strong&gt; "She's gone…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Zaygal/steady" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Zaygal/steady" rel="noopener noreferrer"&gt;https://github.com/Zaygal/steady&lt;/a&gt;&lt;/strong&gt; — MIT, and deliberately small. &lt;code&gt;agent/steady.py&lt;/code&gt; is the whole&lt;br&gt;
policy and the model calls. &lt;code&gt;app/server.py&lt;/code&gt; is a stdlib HTTP server with &lt;strong&gt;no third-party&lt;br&gt;
dependencies at all&lt;/strong&gt; — a support tool for someone in crisis should have the smallest possible&lt;br&gt;
attack surface. &lt;code&gt;app/index.html&lt;/code&gt; is the entire front end. &lt;code&gt;NOT-A-TREATMENT.md&lt;/code&gt; is a thing I&lt;br&gt;
shouldn't have to write, and did.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;Both model paths are open-weight, and &lt;strong&gt;there is no proprietary-model path in the code&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma&lt;/strong&gt;, served through Google AI Studio — &lt;code&gt;gemma-4-31b-it&lt;/code&gt; answering on the live site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A GGUF served locally by &lt;code&gt;llama.cpp&lt;/code&gt;&lt;/strong&gt; — fully offline, no network at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the model does not run on anyone's laptop. It runs in &lt;strong&gt;GitHub Actions&lt;/strong&gt;, on a clean public&lt;br&gt;
runner, from a workflow in the repo. The runner downloads the official GGUF, checksums it, runs the&lt;br&gt;
support policy and uploads the reply as an artifact. &lt;strong&gt;No API key, nothing installed on the phone,&lt;br&gt;
and anyone can click the workflow and reproduce it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The policy lives in plain sight in &lt;code&gt;agent/steady.py&lt;/code&gt;: no diagnosis, no dosages, no clinical claims,&lt;br&gt;
replies under 60 words, at most one question, and an explicit rule never to moralise about a slip.&lt;br&gt;
Ask it for help with no model reachable and it does not improvise — it says so and hands over a&lt;br&gt;
crisis pointer. I'd rather it be visibly broken than confidently wrong to someone mid-craving.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;I could have built this on a proprietary model behind an API key. I didn't, and the reasons are&lt;br&gt;
specific to this problem rather than general praise for open source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A tool about addiction has to be inspectable.&lt;/strong&gt; Someone is being asked to trust it in the worst&lt;br&gt;
hour of their week, so "nothing is kept" cannot be a marketing line — it has to be checkable. The&lt;br&gt;
entire policy is thirty lines in &lt;code&gt;agent/steady.py&lt;/code&gt;. The model is a public GGUF. The runtime is&lt;br&gt;
&lt;code&gt;llama.cpp&lt;/code&gt;. The workflow that runs it is in the repo. You can read all of it, and none of it&lt;br&gt;
required my permission.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free matters more here than anywhere.&lt;/strong&gt; A person in a bad week is not going to add a subscription&lt;br&gt;
to get help at 3am. Open weights mean they can run this on hardware they already own, indefinitely,&lt;br&gt;
without a billing relationship deciding whether they deserve an answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Swappable means it outlives me.&lt;/strong&gt; The model is one environment variable. When a better small model&lt;br&gt;
lands — and it will — nobody needs my release cycle, or my continued existence, to put it in. That's&lt;br&gt;
the difference between an open project and a product I own.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;p&gt;DevRelay sessions are optional, so here's the equivalent: &lt;strong&gt;every run of the agent is public in the&lt;br&gt;
&lt;a href="https://github.com/Zaygal/steady/actions" rel="noopener noreferrer"&gt;Actions tab&lt;/a&gt;&lt;/strong&gt;, including the four that went wrong. That&lt;br&gt;
tab is the actual build log, and it's more candid than a summary would be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Render — featured.&lt;/strong&gt; The front end is live on Render at&lt;br&gt;
&lt;strong&gt;&lt;a href="https://steady-21e1.onrender.com" rel="noopener noreferrer"&gt;https://steady-21e1.onrender.com&lt;/a&gt;&lt;/strong&gt;. The criterion is &lt;em&gt;"host an agent's front end,"&lt;/em&gt; and you can&lt;br&gt;
open it on a phone right now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Gemma — featured.&lt;/strong&gt; Gemma is the model, open-weight, with no proprietary path&lt;br&gt;
anywhere. The app asks the API which open-weight Gemma ids the key can actually reach instead of&lt;br&gt;
trusting a hardcoded id — which is how I found that this key serves &lt;strong&gt;Gemma 4&lt;/strong&gt;, not the Gemma 3&lt;br&gt;
ids I had assumed, all of which returned 404.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Use of GitHub Copilot — partner.&lt;/strong&gt; The qualifying path is &lt;em&gt;"automate your project with&lt;br&gt;
GitHub Actions,"&lt;/em&gt; and for Steady that isn't a garnish: the entire model runtime &lt;strong&gt;is&lt;/strong&gt; a GitHub&lt;br&gt;
Actions workflow. Nothing runs on anyone's machine, and every run is public.&lt;/p&gt;

&lt;p&gt;There's one family of categories Steady is built to lose — the data-layer ones (MongoDB Atlas,&lt;br&gt;
Tiger Data). &lt;strong&gt;Not keeping the data is the product.&lt;/strong&gt; A version that stored every craving and every&lt;br&gt;
slip so it could win a storage prize would be the exact thing it exists to avoid.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Got Wrong, in Public
&lt;/h2&gt;

&lt;p&gt;I built the model path four times, and the last failure was the most useful one.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gemma-3-270m-it&lt;/code&gt; — the smallest Gemma — &lt;strong&gt;restated my instructions back at me&lt;/strong&gt; instead of&lt;br&gt;
answering: &lt;em&gt;"Okay, I understand. I will adhere to the rules and provide a concise, helpful&lt;br&gt;
response."&lt;/em&gt; Useless to someone mid-craving. I moved to 1B, then 4B.&lt;/p&gt;

&lt;p&gt;The 4B was warm and stayed inside the policy, &lt;strong&gt;and it ignored her completely.&lt;/strong&gt; I passed it&lt;br&gt;
&lt;em&gt;"walked past the shop and it hit hard"&lt;/em&gt; and it never once mentioned the shop — every run it offered&lt;br&gt;
breathing. I added a rule that ignoring what she said counts as a failure, and found a real&lt;br&gt;
interface bug behind it: &lt;strong&gt;I had been burying her words inside a paragraph of instructions addressed&lt;br&gt;
to the agent.&lt;/strong&gt; Her sentence was a footnote in my message. I fixed that — her words are now the&lt;br&gt;
user turn and nothing else — and &lt;strong&gt;the behaviour still didn't change.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's when I stopped blaming the prompt and admitted it was capacity. A 4B model doesn't have the&lt;br&gt;
room to hold the policy, her sentence, and a reply that uses both. So I moved to 12B:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"That walk brought it on strong. Feel your feet on the ground. Can you take three slow, deep&lt;br&gt;
breaths now?"&lt;/em&gt; — using &lt;strong&gt;her&lt;/strong&gt; word, from &lt;strong&gt;her&lt;/strong&gt; sentence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And then the bigger model did something worse. On its first run, given the same note, it said&lt;br&gt;
&lt;em&gt;"That smell is strong right now."&lt;/em&gt; &lt;strong&gt;There was no smell.&lt;/strong&gt; It invented a sensory detail she never&lt;br&gt;
gave and handed it to someone in distress as fact. So I added a rule that it may use only what she&lt;br&gt;
actually said, and it stopped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then I tried to fix it with a fine-tune, and that failed too.&lt;/strong&gt; I trained a LoRA adapter on&lt;br&gt;
1,800 synthetic examples and compared it against the same model untuned, on a held-out set, against&lt;br&gt;
one metric I fixed &lt;em&gt;before&lt;/em&gt; running it: does the reply reuse a content word from the person's note?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;baseline &lt;strong&gt;68.3%&lt;/strong&gt; → fine-tuned &lt;strong&gt;60.0%&lt;/strong&gt;, for $1.11&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It made it worse. So there is no "Best Use of Tinker" claim anywhere in this post — I'm not going to&lt;br&gt;
go shopping for a metric that flatters my own adapter. What the samples actually show is that my&lt;br&gt;
&lt;em&gt;metric&lt;/em&gt; was the weak part: the baseline learned to parrot her sentence straight back, which&lt;br&gt;
guarantees word overlap and reads like a machine, while the fine-tuned model used the exact noun the&lt;br&gt;
baseline had paraphrased away. Two real lessons: the defect I'd documented was the &lt;strong&gt;4B's capacity&lt;br&gt;
limit, not a universal problem&lt;/strong&gt; — a 30B model already used the detail 68% of the time — and my&lt;br&gt;
synthetic data was too mechanical to beat that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every step up the ladder bought capability and sold a little accuracy — and accuracy is the only&lt;br&gt;
thing that matters here.&lt;/strong&gt; All four runs are public. You can watch me get it wrong, watch the model&lt;br&gt;
invent something, and watch me take it back out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest Limits
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Steady is not treatment.&lt;/strong&gt; Not therapy, not medical advice, not a crisis service. It cannot detect&lt;br&gt;
anything or measure anything, and it doesn't replace a professional. If someone is in immediate&lt;br&gt;
danger they should contact local emergency services, or find a verified crisis line for their&lt;br&gt;
country at findahelpline.com.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The supporter's view is pull, not push&lt;/strong&gt; — he opens the link to see it. Nothing is texted or&lt;br&gt;
emailed to him, and no notification fires. That is the honest edge of what I built: the signal&lt;br&gt;
is real and delivered, but he has to look.&lt;/p&gt;

&lt;p&gt;The instance is always-on, so there is no cold start. The workflow is the reproducible proof that&lt;br&gt;
the open-weight path works; the phone UI is what someone would actually press, pointed at a model&lt;br&gt;
they host themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Say to Anyone Building for Someone Real
&lt;/h2&gt;

&lt;p&gt;The constraint they give you &lt;em&gt;is&lt;/em&gt; the product. Mine was one sentence — &lt;em&gt;"don't make me report to&lt;br&gt;
you"&lt;/em&gt; — and the whole design fell out of it. Everything I was tempted to add (history, streaks, a&lt;br&gt;
dashboard for the supporter) would have quietly turned it back into the thing it exists to avoid.&lt;/p&gt;

&lt;p&gt;Not every feature you can build is a feature you should ship to someone you love.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
