<?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: Manas Dutta</title>
    <description>The latest articles on DEV Community by Manas Dutta (@manasdutta).</description>
    <link>https://dev.to/manasdutta</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%2F4100456%2F1dc428c9-3434-4c5d-beed-5e00fc98ecca.jpeg</url>
      <title>DEV Community: Manas Dutta</title>
      <link>https://dev.to/manasdutta</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manasdutta"/>
    <language>en</language>
    <item>
      <title>Your company's knowledge doesn't go missing. It goes wrong.</title>
      <dc:creator>Manas Dutta</dc:creator>
      <pubDate>Sun, 18 Oct 2026 16:30:06 +0000</pubDate>
      <link>https://dev.to/manasdutta/your-companys-knowledge-doesnt-go-missing-it-goes-wrong-5ecd</link>
      <guid>https://dev.to/manasdutta/your-companys-knowledge-doesnt-go-missing-it-goes-wrong-5ecd</guid>
      <description>&lt;p&gt;&lt;em&gt;How we built Strata, a company brain on FalkorDB that notices when a document has become false, for Graph Hacks.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Code: &lt;a href="https://github.com/manasdutta04/strata" rel="noopener noreferrer"&gt;https://github.com/manasdutta04/strata&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The failure nobody searches for
&lt;/h2&gt;

&lt;p&gt;Picture a Tuesday. In &lt;code&gt;#platform&lt;/code&gt;, a lead writes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Decision: sessions move from Redis to Postgres. Cost and durability. ADR-004 is superseded, migration starts Monday.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Good decision, clearly stated, and it will be lost within a week. The onboarding guide still says &lt;em&gt;"user sessions are stored in Redis, so run a local Redis when testing auth flows."&lt;/em&gt; The &lt;code&gt;auth-service&lt;/code&gt; README says the same. An open ticket is still building Redis failover alarms. A new engineer follows the guide on Monday.&lt;/p&gt;

&lt;p&gt;None of this is a search problem. Every one of those documents is easy to find. The problem is that &lt;strong&gt;nothing knows they are now wrong&lt;/strong&gt;. A search index stores what documents say. It has no concept of &lt;em&gt;what the company currently believes&lt;/em&gt;, so it cannot tell you which documents disagree with it.&lt;/p&gt;

&lt;p&gt;Strata is our attempt at the missing piece: a company brain that keeps every decision as a layer, knows which layer is current, and tells you, with a graph path as proof, which docs and tickets just became stale and who owns them. It also checks a new doc &lt;em&gt;before&lt;/em&gt; anyone follows it, and audits itself for drift nobody reported.&lt;/p&gt;

&lt;p&gt;The name is literal. Rock strata are layers: older ones get buried by newer ones but are never erased. Strata (the product) never deletes a decision. It closes it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The model: facts that expire, documents that assert them
&lt;/h2&gt;

&lt;p&gt;The whole product is a small idea expressed as a graph. Decisions &lt;em&gt;establish&lt;/em&gt; claims. Documents and tickets &lt;em&gt;assert&lt;/em&gt; those claims or &lt;em&gt;cite&lt;/em&gt; the decisions. Every decision and claim has a validity window.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(Decision)-[:ESTABLISHES]-&amp;gt;(Claim)
(Doc | Ticket)-[:ASSERTS]-&amp;gt;(Claim)
(Doc | Ticket)-[:CITES]-&amp;gt;(Decision)
(Decision)-[:SUPERSEDES]-&amp;gt;(Decision)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Time is a convention, not a feature: every timestamp is epoch milliseconds, and &lt;code&gt;valid_to&lt;/code&gt; unset means "currently in force". "As of time T" is always &lt;code&gt;valid_from &amp;lt;= T AND (valid_to IS NULL OR valid_to &amp;gt; T)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When a decision is reversed, we close the old one. We never delete anything:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MATCH (new:Decision {id: $new_id}), (old:Decision {id: $old_id})
SET old.valid_to = new.valid_from
MERGE (new)-[:SUPERSEDES {ts: new.valid_from}]-&amp;gt;(old)
WITH old, new
OPTIONAL MATCH (old)-[:ESTABLISHES]-&amp;gt;(c:Claim) WHERE c.valid_to IS NULL
SET c.valid_to = new.valid_from
RETURN old.id AS old_id, new.id AS new_id, collect(c.id) AS closed_claims
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And now "what is stale?" is a single traversal. Anything that asserts a claim we just closed, or cites the decision we just superseded:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MATCH (new:Decision {id: $new_id})-[:SUPERSEDES]-&amp;gt;(old:Decision)
MATCH (item)-[r:ASSERTS|CITES]-&amp;gt;(x)
WHERE (x = old OR (old)-[:ESTABLISHES]-&amp;gt;(x)) AND NOT (item)-[:NEXT_VERSION]-&amp;gt;()
  AND (item:Doc OR (item:Ticket AND item.status = 'open'))
RETURN DISTINCT item.id AS item_id, labels(item)[0] AS item_label, item.title AS title,
       type(r) AS how, x.id AS stale_id, coalesce(x.text, x.summary) AS stale_text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That query is the product. There is no keyword matching, no similarity threshold, no model deciding whether a doc "seems outdated". A doc is stale because there is an edge from it to a fact that is no longer true, and the edge is the explanation we show the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  The demo: one Slack message
&lt;/h2&gt;

&lt;p&gt;Our synthetic company, Northwind Labs, has 25 people, 5 teams, 8 systems, about 40 docs, 60 tickets and 270 messages, generated deterministically from a written storyline so every demo beat is reproducible.&lt;/p&gt;

&lt;p&gt;Then we paste a message that is &lt;em&gt;not&lt;/em&gt; in the seed data:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Heads up: from today we're dropping Kafka and moving the event bus to Redis Streams. Ops cost and on-call load are too high.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What happens next, all through the real pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ingestor&lt;/strong&gt; extracts a structured decision. Crucially, the model is shown the decisions currently in force and must choose which one this supersedes &lt;em&gt;from that list&lt;/em&gt;. It cannot invent an id. Here it picks the Kafka decision.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;supersede&lt;/code&gt; query above closes the Kafka decision and its claim.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Steward&lt;/strong&gt; runs the staleness query. Result: exactly &lt;strong&gt;3 docs and 2 open tickets&lt;/strong&gt;. The event-bus README, the onboarding guide, an ADR that cites the Kafka decision, and two tickets building Kafka consumers. (A closed ticket that also mentions Kafka is correctly ignored.)&lt;/li&gt;
&lt;li&gt;Every item gets an owner. Four have a direct owner. The ADR has none, so Strata routes it through the team that owns the system it's about and picks the member with the highest expertise on that system.&lt;/li&gt;
&lt;li&gt;For each item the model drafts a minimal diff: a &lt;code&gt;-&lt;/code&gt; line and a &lt;code&gt;+&lt;/code&gt; line, nothing else.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then the part we care most about: &lt;strong&gt;nothing changes without a click.&lt;/strong&gt; Each fix is an open task. Approve creates a &lt;em&gt;new version&lt;/em&gt; of the doc linked by &lt;code&gt;NEXT_VERSION&lt;/code&gt;; the old version stays in the graph. Reject leaves the item flagged. The human gate isn't a feature bolted on at the end; it's what makes it safe to let an automated system accuse your documentation of being wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Catching wrong knowledge before it spreads
&lt;/h2&gt;

&lt;p&gt;Reacting to a reversal is half the job. The other half is the doc someone writes &lt;em&gt;next week&lt;/em&gt; from memory. So Strata checks new docs on the way in. Paste a draft, say a "Local dev setup for auth-service" page, and the Ingestor's model does one narrow thing: it maps each sentence to a claim the graph already knows (in force or closed), quoting the sentence. Ids that aren't in the graph are thrown away. Whether the doc is wrong is then a traversal, not a judgment call:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MATCH (d:Doc {id: $doc_id})-[r:ASSERTS]-&amp;gt;(c:Claim)
OPTIONAL MATCH (old:Decision)-[:ESTABLISHES]-&amp;gt;(c)
OPTIONAL MATCH (new:Decision)-[:SUPERSEDES]-&amp;gt;(old) WHERE c.valid_to IS NOT NULL
OPTIONAL MATCH (new)-[:DECIDED_BY]-&amp;gt;(p:Person)
RETURN c.id AS claim_id, r.quote AS quote, c.valid_to IS NULL AS in_force,
       new.id AS closed_by, new.summary AS closed_by_summary, new.valid_from AS closed_at, p.name AS closed_by_name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the demo draft, "user sessions are stored in Redis" comes back flagged: closed by D-02, Leo Park, Aug 14. The two other statements (CI on GitHub Actions, 15-minute tokens) are current. The Steward drafts a fix and routes it to the author, through the same Ingestor → Steward → Concierge hand-off as a Slack reversal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The brain audits itself
&lt;/h2&gt;

&lt;p&gt;A brain that only notices drift when you paste a message misses everything that went wrong before it was switched on. The &lt;strong&gt;Health&lt;/strong&gt; tab runs one query over the whole graph: every current doc and open ticket that asserts a closed claim or cites a superseded decision, with the decision that made it wrong and whether a fix is already in flight. On a fresh Northwind seed it finds &lt;strong&gt;7 items that were already wrong&lt;/strong&gt;: the deploy runbook still says Jenkins, two tickets still build features on a deprecated API, the onboarding guide still says Redis. Health also reports what share of docs and tickets are still correct (88.5%), the one ADR nobody owns, and the one system only one person understands. One click hands the untracked drift to the Steward; every fix still waits for a human.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions people actually ask
&lt;/h2&gt;

&lt;p&gt;Judges, and colleagues, don't ask the five questions in your demo script. So beyond &lt;em&gt;why&lt;/em&gt;, Strata answers the questions a company brain gets every day, all as graph queries with citations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Who owns the onboarding doc?&lt;/em&gt; resolves the doc by its distinctive title words, returns the owner (or the owning team's top expert if nobody owns it), &lt;strong&gt;and adds that the doc is out of date&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;What does Aisha work on?&lt;/em&gt; gives role and team, systems by activity, docs owned, decisions made and whether they're still in force, and fixes waiting on her.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;What changed last week? / since June?&lt;/em&gt; is a window over decision validity, plus the knowledge that went stale because of it.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;How do we deploy?&lt;/em&gt; quotes the closest passages verbatim (no generation; FalkorDB's vector and full-text indexes, fused) and, because it's a graph, can say: &lt;em&gt;"Out of date: this doc says 'CI runs on Jenkins', but on Jun 2 CI moved to GitHub Actions (D-04)."&lt;/em&gt; That sentence is the whole product in one line.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  "Why?" is a graph path, and so is time travel
&lt;/h2&gt;

&lt;p&gt;Ask &lt;em&gt;"Why do we use Postgres for sessions?"&lt;/em&gt; and the answer is built from one query that returns the decision in force, who made it, the chain of decisions it replaced, and the evidence. In the UI the exact path is highlighted: the new decision, a dashed &lt;code&gt;SUPERSEDES&lt;/code&gt; edge to the old one, the Slack messages that evidence each. Every sentence of the answer carries the ids of its sources, and you can click through to them.&lt;/p&gt;

&lt;p&gt;Because nothing is deleted, time travel costs nothing. The slider on the graph re-runs the &lt;strong&gt;same query&lt;/strong&gt; with a different &lt;code&gt;$t&lt;/code&gt;. Drag back to April and &lt;em&gt;"Why is the event bus on Kafka?"&lt;/em&gt; answers with the Kafka decision and adds that it was later superseded. Drag forward and it's Redis Streams. Closed decisions dim; they don't vanish.&lt;/p&gt;

&lt;p&gt;And when the graph has nothing, Strata says &lt;strong&gt;"I don't know"&lt;/strong&gt; and shows what it searched. We spent more design effort on that behavior than on any happy path. A company brain that confidently invents a decision rationale is worse than no brain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who actually knows? Ask the algorithms
&lt;/h2&gt;

&lt;p&gt;The org chart says who &lt;em&gt;owns&lt;/em&gt; a system. It doesn't say who &lt;em&gt;understands&lt;/em&gt; it. Strata derives symmetric &lt;code&gt;COLLABORATES&lt;/code&gt; edges (both directions, since the algorithms treat edges as directed) from shared authored and reviewed work, then runs FalkorDB's graph algorithms over them:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CALL algo.pageRank('Person', 'COLLABORATES') YIELD node, score
CALL algo.betweenness({nodeLabels: ['Person'], relationshipTypes: ['COLLABORATES']}) YIELD node, score
CALL algo.labelPropagation({nodeLabels: ['Person'], relationshipTypes: ['COLLABORATES']}) YIELD node, communityId
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A person's expertise on a system is their normalised activity there times &lt;code&gt;(1 + PageRank)&lt;/code&gt;. Betweenness drives a "knowledge bridge" badge: in the demo data, one data engineer reviews PRs across Payments, Growth and Data and lights up as the connection between teams. Label propagation finds an informal team (two Growth engineers and one Mobile engineer who work together far more than with their own teams), which the org chart doesn't show.&lt;/p&gt;

&lt;p&gt;Bus factor is a plain aggregation: systems where at most one person has meaningful authored/reviewed activity. In the seed data exactly one system, &lt;code&gt;billing-reconciler&lt;/code&gt;, trips it, and the Who Knows card shows the warning next to the one person who holds it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agents that only talk through the graph
&lt;/h2&gt;

&lt;p&gt;Ingestor, Steward and Concierge never call each other. They share state through &lt;code&gt;Task&lt;/code&gt; nodes and &lt;code&gt;HANDED_TO&lt;/code&gt; edges:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(Ingestor)-[:CREATED]-&amp;gt;(Task {status: 'in_progress'})
(Task)-[:HANDED_TO {ts}]-&amp;gt;(Steward)  -&amp;gt;  (Concierge)  -&amp;gt;  status 'done'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Every step persists before the next begins, and the Steward is idempotent (it skips items that already have a task), so an interrupted run can be finished by &lt;code&gt;resume&lt;/code&gt; from task status alone. We test it: set a finished run back to &lt;code&gt;in_progress&lt;/code&gt;, call &lt;code&gt;resume&lt;/code&gt;, and the run is finished from graph state alone. Personal memory works the same way. Each user gets their own FalkorDB graph recording sessions, questions and which decision version they were shown, and on the next session we compare against what's in force now: &lt;em&gt;"Since you last asked about the event bus, that decision changed."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where an LLM is (and isn't) allowed
&lt;/h2&gt;

&lt;p&gt;This was the design rule that shaped everything: &lt;strong&gt;the LLM never writes Cypher and never writes an answer.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It extracts a decision from a message, grounded in a list of real decisions.&lt;/li&gt;
&lt;li&gt;It maps a new doc's sentences to claim ids that already exist in the graph.&lt;/li&gt;
&lt;li&gt;It drafts the diff text for a fix.&lt;/li&gt;
&lt;li&gt;As a fallback, when intent is genuinely unclear, it picks one tool from a short vetted list.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Staleness, supersession, ownership, expertise, lineage and the &lt;em&gt;text&lt;/em&gt; of every answer come from queries. Answers are composed from graph results so each sentence can point at its evidence. The side effect we didn't expect: the product became fast and stable. Graph-backed answers return in a few hundredths of a second; only the extraction and drafting calls touch a model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things that went wrong, and what we fixed
&lt;/h2&gt;

&lt;p&gt;We would rather tell you than let you find them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Our bus-factor system flagged nothing at first.&lt;/strong&gt; Counting every message about a system inflated the secondary people's activity past the threshold. Fixing the &lt;em&gt;data&lt;/em&gt; (the storyline says one person truly knows the reconciler) was right; the query stayed as written.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The memory banner would have lied after a reset.&lt;/strong&gt; Resetting the demo reseeded the company graph but left old per-user graphs behind, so a user could be told a decision "changed" when it hadn't. We caught it while polishing; reset now clears them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-travel views would have polluted memory.&lt;/strong&gt; Asking &lt;em&gt;"as of April"&lt;/em&gt; recorded the old decision as "seen," so the next session would have claimed it had changed. Historical views and slider re-runs are now deliberately not remembered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixing a doc twice erased half of it.&lt;/strong&gt; Approving a fix created a new version that asserted only the new fact, silently dropping everything else the doc said. Approve the sessions fix on the onboarding guide, then reverse Kafka, and the guide no longer showed up as stale, because its new version had "forgotten" it mentioned Kafka. A second fix on the same doc failed silently while reporting success. Now a new version is built on the &lt;em&gt;latest&lt;/em&gt; version and carries over every claim still in force, and a regression test runs exactly that sequence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector search alone said "I don't know" to "How do we deploy?"&lt;/strong&gt; With the real embedding model the runbook was too far away in vector space. Search now fuses FalkorDB's full-text index with the vector index, and CI checks it with the real model.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;The company is &lt;strong&gt;synthetic&lt;/strong&gt;. Northwind Labs is fictional and labelled as such in the UI; it's generated from a fixed seed. We did not test on real company data.&lt;/li&gt;
&lt;li&gt;Locally the model runs through the Claude Code CLI (no API key); a hosted deployment uses the Anthropic API (&lt;code&gt;LLM_PROVIDER=anthropic&lt;/code&gt;). Responses are cached by prompt hash so recordings are reproducible; a cached run is a replay of a real earlier response, not a live call, and we say so in the README. A first uncached run of the live reversal takes about 20 seconds.&lt;/li&gt;
&lt;li&gt;The doc check can only judge statements that match a claim already in the graph. A genuinely new fact in a doc is stored and searchable, but not judged.&lt;/li&gt;
&lt;li&gt;Retrieval-based topic matching uses a distance threshold tuned on the demo corpus. Expertise scores count all authored/reviewed items on a system, including messages and tickets.&lt;/li&gt;
&lt;li&gt;AI coding assistants were used throughout development, and disclosed in the README. The design, graph model and queries are ours to explain; the test suite (40 tests, including the exact "3 docs and 2 tickets with owners" contract, the doc check and the double-fix regression; CI runs the 29 that need no LLM against a FalkorDB service) is how we keep ourselves honest.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why FalkorDB
&lt;/h2&gt;

&lt;p&gt;Remove it and the product doesn't degrade; it ceases to exist. Supersession and as-of answers, staleness, the write-time doc check, the self-audit, ownership routing, PageRank/betweenness/label-propagation expertise, bus factor, provenance paths, agent hand-offs and per-user memory are all graph queries or graph algorithms. FalkorDB's ability to hold many isolated graphs made per-user memory a one-line decision (&lt;code&gt;strata_user_&amp;lt;id&amp;gt;&lt;/code&gt;), and having vector search, full-text search and graph algorithms in the same engine as the traversals meant one system answered "which chunk is relevant?", "which decision does it concern?" and "who knows it?" without glue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;docker compose --profile app up --build&lt;/code&gt; runs FalkorDB, the API and the web app. Ask &lt;em&gt;"How do we deploy?"&lt;/em&gt; and watch it warn you about its own runbook. Open Health. Paste the Kafka → Redis Streams message and watch five stale items light up, then paste the local-dev doc and watch it get caught. Then drag the slider and watch the answer change its mind.&lt;/p&gt;

&lt;p&gt;Company knowledge will always drift. The goal isn't to stop it. It's to &lt;strong&gt;notice&lt;/strong&gt;, name an owner, and keep the layers underneath so you can always dig down and see what was true at any point.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>database</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>So today is day 1 of 100.</title>
      <dc:creator>Manas Dutta</dc:creator>
      <pubDate>Sun, 20 Sep 2026 15:28:25 +0000</pubDate>
      <link>https://dev.to/manasdutta/so-today-is-day-1-of-100-2jfe</link>
      <guid>https://dev.to/manasdutta/so-today-is-day-1-of-100-2jfe</guid>
      <description>&lt;p&gt;I started with linear regression and did it properly this time, from scratch, with no sklearn doing the work.&lt;/p&gt;

&lt;p&gt;Here's how the day went:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intuition first. What it means to fit a line through data, and what "best fit" is actually measuring.&lt;/li&gt;
&lt;li&gt;Cost function. Wrote out MSE and worked through why the errors get squared.&lt;/li&gt;
&lt;li&gt;Gradient descent by hand. Worked out the updates on paper before touching any code. It's a small loop: predict, check the error, adjust the weights, repeat.&lt;/li&gt;
&lt;li&gt;Implemented the whole thing in NumPy from scratch.&lt;/li&gt;
&lt;li&gt;Went through the assumptions the model relies on, mainly linearity and how the residuals behave.&lt;/li&gt;
&lt;li&gt;Ran the same data (California housing) through scikit-learn and compared it against my version.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've used linear models in projects before. The ticket triage pipeline I built has a Linear SVM inside it. But I had never gone through the whole thing step by step myself, and I wanted that done before moving on to anything bigger.&lt;/p&gt;

&lt;p&gt;I also spent some time on core Python, to make sure the basics under everything I write are solid.&lt;/p&gt;

</description>
      <category>100daysofaiml</category>
      <category>ai</category>
      <category>python</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Undox: approval-gated data-broker opt-outs on TrueForge</title>
      <dc:creator>Manas Dutta</dc:creator>
      <pubDate>Sat, 29 Aug 2026 15:50:16 +0000</pubDate>
      <link>https://dev.to/manasdutta/undox-approval-gated-data-broker-opt-outs-on-trueforge-219m</link>
      <guid>https://dev.to/manasdutta/undox-approval-gated-data-broker-opt-outs-on-trueforge-219m</guid>
      <description>&lt;p&gt;People-search sites still republish names, addresses, phones, and dates of birth. Opting out is tedious, easy to mess up, and basically irreversible once you hit Submit.&lt;/p&gt;

&lt;p&gt;For the WeMakeDevs × TrueFoundry × Qodo agent-harness hackathon, I built &lt;strong&gt;Undox&lt;/strong&gt; — a TrueForge agent that does the annoying work carefully, then &lt;strong&gt;stops&lt;/strong&gt; until a person allows the exact payload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://undox.vercel.app/app?session=demo-test-2" rel="noopener noreferrer"&gt;undox.vercel.app&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/manasdutta04/undox" rel="noopener noreferrer"&gt;github.com/manasdutta04/undox&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Connect MCP (paste-ready):&lt;/strong&gt; &lt;a href="https://undox.vercel.app/app/connect" rel="noopener noreferrer"&gt;undox.vercel.app/app/connect&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem (in one breath)
&lt;/h2&gt;

&lt;p&gt;Privacy products exist because this workflow is painful. An agent that “just submits” is worse than no agent. We needed a harness that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Finds&lt;/strong&gt; broker listings
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prepares&lt;/strong&gt; opt-out forms in a sandbox
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pauses&lt;/strong&gt; on literal PII before anything leaves the machine
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Chat summaries don’t count. Tool JSON and a dashboard do.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Undox is
&lt;/h2&gt;

&lt;p&gt;Undox is a TrueForge orchestrator wired to a custom HTTP MCP server (&lt;code&gt;undox-tools&lt;/code&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;TrueForge piece&lt;/th&gt;
&lt;th&gt;How we use it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP tools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;find_*&lt;/code&gt; → &lt;code&gt;run_sandbox_prepare&lt;/code&gt; → approval-gated &lt;code&gt;submit_opt_out&lt;/code&gt;; plus &lt;code&gt;get_session_state&lt;/code&gt; / &lt;code&gt;get_exposure_dashboard&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sandbox + skills&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Broker prepare scripts (&lt;code&gt;prepare_runtime: sandbox-script&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Approval&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Submit requires human &lt;strong&gt;Allow&lt;/strong&gt; on the exact fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subagents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;dynamicSubAgents&lt;/code&gt; + parallel tool fan-out fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sessions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;File store keyed by &lt;code&gt;session_id&lt;/code&gt; — survives TrueForge restart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Status UI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Next.js dashboard on Vercel; same store as MCP on Render&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Demo brokers &lt;strong&gt;PeopleFind&lt;/strong&gt; and &lt;strong&gt;Clearbook&lt;/strong&gt; are fixtures (no live CAPTCHA on stage). Spokeo uses real URL mapping; submit stays &lt;strong&gt;mock&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  How TrueForge fits (not a thin wrapper)
&lt;/h2&gt;

&lt;p&gt;The agent isn’t “one mock function + a chat UI.”&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TrueForge owns the loop, tools, and Allow gate.
&lt;/li&gt;
&lt;li&gt;MCP does the broker work and writes session state.
&lt;/li&gt;
&lt;li&gt;The public UI is evidence of that state — Exposure, Brokers, Approval, Architecture, Connect.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kill TrueForge, restart, reopen the same &lt;code&gt;session_id&lt;/code&gt; — statuses are still there. That’s the Double-O story: durable harness state, not a one-shot demo.&lt;/p&gt;

&lt;p&gt;Judges (and you) can walk the seeded session without cloning:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exposure → Brokers → Approval → Connect&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Session: &lt;code&gt;demo-test-2&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Stack (all free-tier friendly)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TrueForge&lt;/strong&gt; locally (+ Ollama for the agent)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vercel&lt;/strong&gt; for the neo-brutal product UI (&lt;code&gt;web/&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Render&lt;/strong&gt; for API, fixtures, and &lt;code&gt;/mcp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Public demo Bearer on Connect: &lt;code&gt;undox-demo-public&lt;/code&gt; (mock submits only)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Render free tier sleeps — first hit after idle can take ~a minute. Ping &lt;code&gt;/healthz&lt;/code&gt; before a live demo.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Qodo helped
&lt;/h2&gt;

&lt;p&gt;Every substantive change shipped as a PR with &lt;code&gt;/agentic_review&lt;/code&gt;. Qodo caught real issues — auth edges, stale UI after failed loads, CSS specificity bugs — and we fixed High/Medium before merge. CI also runs &lt;code&gt;typecheck&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, and &lt;code&gt;prove:heart&lt;/code&gt; on every PR.&lt;/p&gt;

&lt;p&gt;That mattered more than polish: the approval path and session store had to be trustworthy on camera.&lt;/p&gt;




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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fixture-first demos win.&lt;/strong&gt; Live CAPTCHAs kill stage takes. Fixtures keep find → prepare → allow reliable.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chat lies; tools don’t.&lt;/strong&gt; Point judges at the dashboard and tool JSON.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approval on literal PII is the product.&lt;/strong&gt; If Allow isn’t on the exact fields, you don’t have a safety story.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split hosting is fine.&lt;/strong&gt; UI on Vercel, MCP on Render, TrueForge local — Connect JSON ties it together.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review bots earn their keep&lt;/strong&gt; when you actually fix what they flag before squash-merge.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://undox.vercel.app/app?session=demo-test-2" rel="noopener noreferrer"&gt;the live demo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Walk Exposure → Brokers → Approval → Connect
&lt;/li&gt;
&lt;li&gt;Paste the MCP config into TrueForge as-is
&lt;/li&gt;
&lt;li&gt;Clone &lt;a href="https://github.com/manasdutta04/undox" rel="noopener noreferrer"&gt;the repo&lt;/a&gt; if you want the offline prove scripts
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Built with TrueForge. Reviewed with Qodo. Submits stay mock on purpose.&lt;/p&gt;

&lt;p&gt;Thanks for reading — if you try the Connect paste, tell me what broke first. That’s usually the most useful review.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>hackathon</category>
      <category>trueforge</category>
    </item>
  </channel>
</rss>
