DEV Community

Sarvar Nadaf
Sarvar Nadaf Subscriber

Posted on AI-assisted

I let an open model delete 1,242 of my saved articles. On purpose. I feel lighter.

Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady Submission 🦥

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady

I had 1,247 articles saved "for later." This week I let a model on my laptop throw almost all of them away, and I have never felt lighter.

Let me tell you why that was so hard, and why I would do it again.

"Read later" is the most optimistic lie in software. Every tab I save is a tiny promise to a future version of me who does not exist, the one with a free Sunday and a cup of tea and nothing better to do. That version is never coming. So the pile just sits there, 1,247 promises deep, and taxes me a little guilt every single time I open a new tab. I was not keeping those articles. They were keeping me.

The challenge this week, "Slow and Steady," asks for something that rewards laziness: channel your inner sloth, build the thing that does the work so you do not have to. So I built the one thing I was too afraid to do by hand. An agent that reads the whole pile overnight, on my own laptop, with the Wi-Fi off, learns which handful I will genuinely read, and then does the scary part for me: it deletes the rest. No cloud. No account. No cost. No more guilt.

It is called Unpile. On the demo pile it reads 14 saved items, keeps the 5 worth my morning, flags 1 maybe, and silences 8, handing back 5.5 hours of reading time I was lying to myself about. Point it at a real 1,200-item backlog and it quietly clears years of guilt while you sleep.


What I Built

Unpile is a local triage agent for your read-later backlog.

You point it at an export (Pocket, Instapaper, browser bookmarks, a CSV) and it does four things, all on your machine:

  1. Learns your taste from your own history of what you actually read versus what rotted in the pile.
  2. Decides every item: keep, skim, or drop, each with a calibrated probability that you will read it.
  3. Writes a calm two-line morning digest of only the keepers.
  4. Silences everything else, so you wake up to five good things instead of a wall of guilt.

The effort it saves is the whole point of the challenge. You stop being the triage system. You stop scrolling a backlog deciding, item by item, "do I care about this?" The model does that boring, repetitive judgment call thousands of times and only taps you on the shoulder for the handful that survive. That is the sloth philosophy in one line: it does the work so you do not have to, and you get your attention back.

Who gets to be lazier? Anyone with a doom-pile of unread tabs. Which, let us be honest, is every single person reading this.


Demo

Live, in your browser (no setup, real baked model output): https://simplynadaf.github.io/unpile-read-later-triage
Video walkthrough: watch on YouTube

The 60-second walkthrough goes end to end: the open-source repo, then the real pipeline running in a terminal (the reading history TabPFN learns from, the keep/skim/drop call with a reason for every item, the local Gemma digest, and the honest held-out scorecard), then the live site. Nothing is staged; the numbers on screen are the actual program output.

Unpile triaging a read-later pile: 5 kept, 1 maybe, 8 silenced, 5.5 hours reclaimed

And here is the triaged pile itself: five keepers on the left (each with the reasons it survived and a one-line Gemma digest), one borderline maybe in the middle, and the eight it silenced on the right, the 11pm two-hour video essay, the "someday build a cabin" photo essay, the tabs-versus-spaces flamewar.

The keep, skim, and drop columns with probabilities and reasons

The whole thing runs on a plain CPU. The numbers on the page are the real TabPFN-v2 and Gemma output, frozen into the static site so the demo cannot break in front of you; toggle any item and an in-browser mirror of the model re-decides live.


Code

GitHub logo simplynadaf / unpile-read-later-triage

A 100% local agent that triages your read-later backlog with TabPFN-v2 and Gemma 3, keeping only the few articles worth your morning. Open-weight, offline, $0. Hacktoberfest 2026 Week 3.


🦥   U N P I L E

⚡ Declare Bankruptcy on Your Read-Later Pile ⚡

An open model, on your own laptop, reads your doom-pile of saved articles overnight,
keeps the handful you will actually read, and silences the rest. Offline. Free. Private.


Live Demo Watch Demo Read Article


TabPFN Gemma CPU Offline Cost License


Unpile, a 100% local read-later triage agent powered by TabPFN-v2 and Gemma 3

📥
Point it at
your pile
➡️ 🧠
TabPFN decides
keep / skim / drop
➡️ ☕
Gemma writes
your morning note
➡️ 🧘
You wake up to
5, not 1,247

🚀 Live Preview  ·  ▶ Watch the Demo  ·  🧠 How It Works  ·  ⚖️ Why Open


🎬 Demo

Watch the Unpile demo on YouTube

A 60-second walk, end to end: the open-source repo → the real pipeline running in a terminal (the history TabPFN learns from, the keep/skim/drop call with a reason for every item, the local Gemma digest, the honest scorecard) → the live site. Nothing staged; every number is real program output.

▶ Watch on YouTube  ·  🚀 Try…


How I Built It

Two open-weight models, one job each. Neither is decoration; take either one out and the project stops working.

TabPFN-v2 is the decision brain

TabPFN (Prior Labs) is an open-weight tabular foundation model. You do not train it. You hand it your history as context and it predicts in a single forward pass. For Unpile, the history is a table of saved items with the signals you know the moment you save something (source, topic, estimated read time, how stale it is, how many times you reopened the tab, whether it is an actionable how-to, how buried it is) and one honest label: did you ever actually read it?

Prior Labs specifically favors TabPFN used inside a wider prediction agent, not as a bare .predict() call. So Unpile wraps it in a real agent loop:

# predict P(you read it) for the whole pile in ONE CPU forward pass
probs = self._proba(items)

for item, p in zip(items, probs):
    action = self._decide(p)        # keep / skim / drop from a tunable keep budget
    reasons = self._reasons(item)   # plain-language "why", so the call is auditable
    ...                             # keep the few, flag the maybes, silence the rot
Enter fullscreen mode Exit fullscreen mode

It predicts, decides an action, explains that action in plain language, and then reports honest held-out metrics on itself. That last part matters, and I will come back to it.

I used TabPFN-v2 on purpose: it decides the whole pile in a single CPU forward pass, no GPU and no training run, which is exactly what lets Unpile run overnight on the laptop you already own.

Gemma 3 is the writer

Gemma 3 runs locally through Ollama and writes the morning digest. It is given the keepers and their probabilities and told to never invent an item. If Ollama is busy or the machine is offline, a deterministic fallback writes a correct digest anyway, so the summary always arrives:

def write_digest(keepers):
    try:
        return gemma_via_ollama(keepers)      # calm two-line morning note, local
    except Exception:
        return rule_based(keepers)            # never blocks your morning
Enter fullscreen mode Exit fullscreen mode

The stack is FastAPI, a synthetic-but-documented dataset, a single-file no-build UI, and GitHub Pages for the live demo. All CPU, all local.


Why Does Open Innovation Matter?

Your reading list is a confession. It leaks your job, your health, your politics, the thing you are quietly trying to learn. That is exactly the data I refuse to send to someone else's server, and it is the reason this had to be open.

  • Your history never leaves the laptop. A closed API would need every row uploaded. TabPFN and Gemma run on-device, so it stays with you.
  • It works with the Wi-Fi off. Open weights mean the overnight triage does not depend on a connection, a login, or a vendor being awake.
  • It is free, at any scale. Triaging a 1,200-item backlog on a per-call API is a real bill. On open weights it is zero, so I can run it nightly and never think about cost.
  • It learns me, not the average. Retrain the decision on my own labels and it mirrors my taste. A closed, general model cannot be bent to one person's weird pile the way an open one can.

A closed API could summarize an article. It could not become a private, offline, free thing that learns my specific reading habits and keeps running on hardware I already own, long after any free tier would have expired. That is the part only open makes possible, and it is the entire reason Unpile exists.


The Lazy Payoff

Here is what this actually bought me, and it was not time. It was permission to let go.

I used to be the triage system. Every few days I would open that folder, feel the weight of 1,247 unread promises, scroll a bit, close it, and feel slightly worse than before. The backlog was not a to-do list. It was a slow-drip guilt machine I had built for myself, one hopeful tab at a time. What I could never do was the obvious thing: just delete them. Deleting felt like admitting I would never become the person who reads 1,247 articles. So I never did. The pile only grew.

The model does not have that hang-up. It looked at my own history, decided which five were real, and threw the rest away without a flicker of guilt, because it does not have any. I did not read 1,242 items this week. I did not even look at them. And the folder that used to make me wince now makes me feel nothing at all, which, after years of that quiet tax, feels like a lot.

Quantified in the only currency the challenge cares about: 8 of 14 items silenced with zero input from me, and 5.5 hours of reading time handed back, time I was lying to myself about anyway. Point it at a real 1,200-item backlog and it is clearing years of that guilt while I sleep. The sloth philosophy, literally: move slow, conserve energy, let the boring, frightening part happen without you.


The Honest Take

Here is the scorecard, measured on a held-out slice of 240 saved items the model never saw, because a number I cannot check is just a vibe.

  • Precision 0.60: when Unpile surfaces something, I actually read it about 60% of the time, against a 36% base rate in the raw pile. It nearly doubles the odds that the thing in front of me is worth it.
  • Lift 1.66x: the kept pile reads 1.66 times better than a random grab from the backlog.
  • Silenced 58%: it auto-archives well over half the pile with no input from me. That is the laziness, quantified.
  • Reclaimed 5.5 hours on the 14-item demo alone. Scale that to a real 1,200-item backlog and the model is doing days of triage you were never going to do yourself.

And now the number I am putting right next to the good ones, on purpose:

  • Wrongly dropped 44%. At this setting, of the items I genuinely would have read, it also silenced about 44% of them. The most painful miss on the demo pile is "this week in AI (issue #214)" at 37% confidence: a fresh newsletter in a topic I follow, and it landed just under the keep line. It got silenced. On another morning I would have wanted it.

I am not hiding that. Unpile is not magic; it trades recall for a calm morning. But the fix is a dial, not a lie: loosen one threshold (UNPILE_KEEP_T) and you recover more of those near-misses at the price of a slightly noisier digest. I would rather ship the honest trade-off, with the failure case named out loud, than a demo that pretends false negatives do not exist.


Prize Categories

  • Best Use of TabPFN (featured): TabPFN-v2 is the decision brain, used as a real prediction agent (predict, decide, explain, measure) over a personal CSV, with honest held-out metrics, exactly the "prediction agent" pattern Prior Labs favors over a bare classifier.
  • Best Use of Gemma (featured): Gemma 3 runs locally via Ollama to write the morning digest, grounded strictly on the model's own output, with a deterministic offline fallback so it never hard-fails.

Built new for this challenge (Oct 19 to 25). It reuses my own local-AI patterns from earlier weeks, re-applied to a new problem, dataset, agent loop, and UI, disclosed in the README per the credit rule.


Thanks for reading all the way down. You just did something your backlog never let you do: you finished. Now go let a model read the rest for you.

Top comments (44)

Collapse
 
techie_1414 profile image
Techie •

The most interesting part isn’t that you deleted 1,242 articles using AI. It’s that you built something that challenges our habit of collecting information we’ll probably never consume.

We often think having more saved content means we’re learning more, but sometimes it’s just digital clutter disguised as productivity. I also appreciate that you shared the 44% false-drop rate instead of hiding the uncomfortable part. That’s real engineering — showing what works, what doesn’t, and where things can improve.

Maybe the real productivity upgrade isn’t finding more things to read, but finally making peace with what we don’t need to read.

Brilliant experiment!

Picked as gem
Collapse
 
sarvar_04 profile image
Sarvar Nadaf •

Thank you! That’s exactly what I was trying to explore with this experiment. At some point, saving more stops being useful and just becomes another backlog we feel responsible for.

And the 44% false-drop rate was important to include because the experiment definitely wasn’t perfect. For me, that failure is actually one of the more interesting results it shows where the idea needs work rather than pretending the model made every decision correctly.

Really appreciate you picking up on that.

Collapse
 
robertpaschal profile image
Odinaka Robert Nnamani •

"Permission to let go" is the sharpest line in the whole piece, and it is doing more work than the 1.66x lift. I have declared read-later bankruptcy by hand before, and the cost was never the reading time — it was the sunk-cost grief of all the intentions I had stacked up. The pile's real function is anxiety management: it keeps the option open forever. The model doesn't feel any of that, which is its only true advantage over human triage. My worry: does the keep-threshold dial just become the new hoarding knob — do you end up tuning UNPILE_KEEP_T instead of reading?

Collapse
 
sarvar_04 profile image
Sarvar Nadaf •

That last bit made me laugh because it's a very real possibility. You can end up optimizing the cleanup instead of actually reading anything. I didn't build Unpile to find the perfect threshold; I wanted to see what it felt like to let go of things I'd been holding onto for no good reason. The model doesn't carry that guilt, and maybe that's the useful part. But yes, if I keep tweaking UNPILE_KEEP to avoid every possible regret, I've just created another backlog, this time made of configuration decisions.

Collapse
 
robertpaschal profile image
Odinaka Robert Nnamani •

The confession hidden inside every cleanup system: the threshold becomes the new hoarding. I did the same with my read-later queue — declared bankruptcy on it last year after the saved count crossed a few thousand, and the relief lasted exactly until I started curating a new list "more carefully" this time.

Your line about the model not carrying guilt is the actual feature. It decides without mourning, which is the part humans can never automate.

Was there anything you flat-out refused to let it touch, or did the full purge apply to everything?

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf •

The “more carefully this time” part is painfully relatable. I think that's how we end up rebuilding the same backlog again.

And yes, the model not carrying guilt was a big part of the experiment. It could make decisions without the emotional attachment we have to things we've saved. That doesn't mean it always makes the right call, though. The 44% false-drop rate is a pretty clear reminder of that. The interesting question is how to get the relief of letting go without losing things you might genuinely need later.

Thread Thread
 
robertpaschal profile image
Odinaka Robert Nnamani •

"More carefully this time" is the whole cycle, isn't it? Every purge system I've built eventually becomes a museum of intentions. The guilt point is sharp — the model deletes without the little ceremony of 'but I might need this,' which is the actual hoarding mechanism. I wonder if the lighter feeling survives the first time you reach for something deleted and it's gone — that's the real test. Did you keep any safety net, or is the point that there is none?

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf •

Honestly, that's the part I'm curious about too. The relief is real, but I can't claim it survives the moment you need something that got deleted. The experiment was about seeing what happens when you let go of that attachment, not proving that every deletion was the right decision. The 44% false-drop rate makes that distinction pretty hard to ignore. A safety net like quarantine is worth considering, but it also raises the question of whether we're actually letting go or just moving the backlog somewhere else.

Thread Thread
 
robertpaschal profile image
Odinaka Robert Nnamani •

The 'are we actually letting go or just moving the backlog' question is the honest one, but I'd defend the quarantine anyway: the cost moves from attention to nothing. A backlog you curate taxes you every time you see it; a quarantine only costs you on the rare reach-back, and the 44% false-drop rate is the real finding — it says the model's judgment about what matters to you is worse than a coin flip, which means the delete step was never the problem, the classification was. What did the false drops have in common? That's the part I'd mine before the next purge.

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf •

I haven't dug deep enough into the false drops to say they had one thing in common. That's probably the most useful next step, though. I'd want to know whether the model was missing long-term value in technical articles or simply misjudging what I'd find useful. Those are two very different problems, and the fix would depend on which one it is. The 44% figure is a good reason to investigate that before trusting the next purge.

Thread Thread
 
robertpaschal profile image
Odinaka Robert Nnamani •

That split is exactly the right next investigation, because the two hypotheses demand opposite fixes: if the model missed long-term value in technical articles, the fix is a better value signal, but if it misjudged what you would find useful, the fix is in the taste model itself. The test I would run is small and cheap: take twenty false drops and sort them by whether you would still want them in six months versus whether they were just badly titled, because my own read-later lesson has been that titles lie and the first paragraph decides. Which of the two do you suspect more — the value miss or the taste miss?

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf •

My guess would be the value miss, especially for technical articles that might not be useful today but become relevant when you're solving a specific problem later. But that's a hypothesis, not something I've verified from the false drops. I like your idea of reviewing twenty of them. That would tell me much more than tweaking the threshold and running another purge blindly.

Thread Thread
 
robertpaschal profile image
Odinaka Robert Nnamani •

The value-miss guess is testable in exactly that review, and the field I would record for each of the twenty is why it was saved: for a specific problem, or for general interest. If the false drops skew toward problem-specific technical saves, the model is misreading deferred value as no value. If they skew toward recently saved items instead, it is a recency effect, and the fix is different. I would sort the twenty by save date first, so a date skew shows up before any topic pattern tempts a story. Would you count a save as problem-specific only when you can name the problem it was for, or is a topic tag enough?

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf •

I'd use the stricter definition: problem-specific only if I can name the problem or use case I saved it for. A topic tag like AWS, RAG, or debugging is still too broad and could easily turn general interest into “problem-specific” after the fact. Sorting by save date first is a good idea too. If there's a recency pattern in those twenty, I'd rather see that before trying to explain it through the content itself.

Thread Thread
 
robertpaschal profile image
Odinaka Robert Nnamani •

That stricter definition is the right guard, because a tag like AWS or RAG can be pasted onto almost anything after the fact and turn general curiosity into a false problem-specific count. I have done that myself — saved a piece under a broad topic, then later convinced myself I saved it for a specific bug I was chasing, when the save date told a different story. Would you require the problem to be named at save time, so a later label cannot rewrite why it was kept?

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf •

Yes, ideally the problem should be captured at save time. Otherwise I'm reconstructing intent months later, and that's exactly where hindsight can distort the data. The catch is that I wasn't recording that context for these existing saves, so for the current twenty I'd have to treat anything I can't confidently tie to a specific problem as general interest. Going forward, capturing a short “why I saved this” signal would make the model's decisions much more interesting to test.

Thread Thread
 
robertpaschal profile image
Odinaka Robert Nnamani •

yeah, capture it at save time or you're guessing at intent months later.

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf •

Exactly. Otherwise I'm reconstructing the reason months later, which isn't reliable enough to treat as ground truth. Capturing a simple “why I saved this” signal at save time is probably something I'd add in the next version.

Thread Thread
 
robertpaschal profile image
Odinaka Robert Nnamani •

one field for it, or would a tag be enough?

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf •

one field. a free-text "why" at save time, not a tag. a tag just becomes another bucket i'll game later, but one honest line like "for the rds failover thing" is the kind of intent a tag can't fake, and it's cheap enough that i'd actually fill it in. the risk is i leave it blank on lazy days, but a blank is still honest data, it tells me that save had no real reason behind it.

Collapse
 
sinarezaei profile image
Sina Rezaei •

The 44% false-drop rate is the part I'd focus on. A saved RFC or a debugging write-up might look stale today but become useful months later when you're investigating a production issue. I'd put borderline items in a quarantine with a retention window instead of deleting them outright, and tune the threshold separately for evergreen technical docs and time-sensitive content. That way, the agent can still clear the backlog without treating "unlikely to read soon" as "never worth keeping."

Collapse
 
sarvar_04 profile image
Sarvar Nadaf •

That's a fair point. The 44% false-drop rate is exactly why I wouldn't treat this as a perfect cleanup system. Something I saved because it wasn't useful at the time could become valuable during a real debugging session months later. A quarantine with a retention window makes sense, especially for technical content. I went into this experiment wanting to reduce the backlog, but there's a difference between reducing clutter and losing something worth keeping. That's a trade-off I need to handle better.

Collapse
 
sinarezaei profile image
Sina Rezaei •

Exactly. I'd also track what happens after retrieval, not just whether an article gets opened. An old postmortem about a PostgreSQL replication lag incident might sit unread for months, then become the most useful reference when a similar issue hits production. If the cleanup system only learns from recency and click history, it can end up removing the material with the highest value during an incident. Keeping those decisions reversible and measuring which items actually help during later searches would give you a much better signal than engagement alone.

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf •

this is the part that would actually change the model, not just the threshold. right now unpile only has the shallow signals, reopen count, recency, length, source, topic in-lane, and you're right that those are exactly the features that would silence that postgres replication postmortem, it looks dead until the night it's the only thing that matters. the problem is that "was useful during an incident" is a label that arrives months after the decision, so the model never gets to learn from it. engagement is easy to log and almost worthless here, an article you open for ten seconds and close scores higher than one you never opened but would've saved you a four-hour outage. what i'd need is a way to capture that late signal, something like a lightweight "this just saved me" mark when an old item actually gets used during a search, and feed that back as the real positive label instead of opens. that turns reversibility from a safety net into training data, the quarantine isn't just where mistakes wait, it's where the model learns what deferred value looks like. the honest catch is that signal is rare and slow, you might get a handful of those marks a year, so it's a long feedback loop to train on. how would you weight a signal that strong but that infrequent against the noisy high-volume engagement data, hand it a much higher weight, or keep the two label types completely separate?

Thread Thread
 
sinarezaei profile image
Sina Rezaei •

I'd keep them as separate signals rather than just giving the rare one a huge weight. Imagine an incident where a team is debugging PostgreSQL replication lag. A postmortem saved eight months ago might explain how a long-running transaction and WAL replay delays interacted, even though nobody has opened it since. If that reference helps the team isolate the cause, that's a much stronger signal of technical value than a high click count, but it shouldn't have to compete with thousands of ordinary opens in the same loss function.

I'd track retrieval utility separately from engagement, then evaluate whether the system is actually surfacing useful material when similar incidents occur. One thing I'd be careful about, though: a "this saved me" mark is still selective feedback. People may forget to leave it, and the most useful document isn't always the one they end up finding. I'd use those marks as high-confidence positive labels, not as proof that everything without one is low-value. That gives the model a way to learn from delayed utility without letting a tiny, biased sample distort the whole ranking.

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf •

yeah, separate is the right call and you pushed me off the "just weight it higher" idea, which was lazy of me. the thing i kept missing is your last line, a missing "this saved me" mark is not evidence of low value, it's just absence, people forget and the doc that actually helped isn't always the one they consciously credit. so the mark can only ever be a positive label, never a negative one, the second i treat "no mark" as "drop it" i've rebuilt the exact bias that silenced the postgres postmortem in the first
place. so the shape is: engagement stays as the noisy day-to-day ranker, retrieval-utility lives in its own table as rare high-confidence positives, and absence stays unlabeled instead of being read as negative. that keeps the small biased sample from distorting everything, which is what you've been saying the whole thread. honestly this exchange gave me a cleaner spec than the article did. are you building something in this space yourself, or is this from running your own triage? you clearly have scars from it.

Collapse
 
mrsaynothing profile image
Mr Say Nothing •

Wi-Fi off — that detail sells it more than any benchmark. An overnight job that has to survive a router reboot is the only kind that ever actually runs, and the deterministic fallback is what makes it survive. The number that earns trust here is the 44%: most triage demos stop at precision and lift, and the wrongly-dropped row is the only one that tells someone what they're actually signing up for. All the threshold dial does is move the guilt somewhere else — every setting is a choice about which morning loses what. 0.60 precision against a 0.36 base rate is real, modest, and honestly the smaller half of the value; the bigger half is deleting without guilt, which is the one thing no sorting algorithm can do for a person.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf •

the wi-fi-off line being the part that sells it is the read i didn't expect but you're right, that's the whole reason the fallback exists. an overnight job that dies the moment the router hiccups or ollama isn't reachable never actually runs, so the deterministic template isn't a nice-to-have, it's the thing that lets the job finish at all. a benchmark you can only hit online isn't an overnight tool, it's a demo. and "every setting is a choice about which morning loses what" is a sharper way to say it than i managed in the whole post, the threshold doesn't remove the cost, it just picks whose saves pay it. the last line is the one i keep circling back to, deleting without guilt is the only part a sorting algorithm genuinely can't do for you, everything else is just ranking. thanks for reading it this closely.

Collapse
 
alexshev profile image
Alex Shev •

Local-first triage is more convincing when deletion remains reversible and inspectable. I would retain a compact decision receipt for each removal—ranking features, model version, and a restore window—so the system can be tuned from mistakes without turning the archive into another cloud service.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf •

you just described the direction unpile is already leaning, which is reassuring to read. it never hard-deletes, it silences, so the item is archived not gone, and every decision already carries its reason so you can inspect what got dropped. where you're ahead of me is the "decision receipt" being a real structured artifact, ranking features plus model version plus a restore window. right now my reason is more of a human-readable line than a tunable record, so i can read why something was dropped but i can't easily replay the exact feature weights and model version that made the call. that version field is the part i underrated, because the moment i retrain, old decisions stop being comparable unless i stamped which model made them. and keeping all of it local is the whole point, the second it becomes a cloud service it's just another pile with a login. curious how you'd store the receipt, inline next to each item, or a separate append-only log you can diff across model versions?

Collapse
 
alexshev profile image
Alex Shev •

I would keep the current state with the item for fast restore, and a separate append-only decision table keyed by item ID for the history. Each event could include the model and feature schema versions, inputs or their snapshot reference, decision, and timestamp. Write the state and event in one local transaction so they cannot drift; retention and export controls matter because those inputs can themselves be sensitive.

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf •

yeah, that split is the right shape and cleaner than what i had in my head. current state next to the item so a restore is cheap, history in a separate append-only table keyed by item id so you can diff across model versions without bloating the hot path. the one-transaction point is the part i'd have gotten wrong, if the state write and the event write can drift you end up with an item that says "kept" and a log that says "dropped" and now neither is trustworthy. stamping the model and feature-schema version on every event is what makes the history actually replayable after a retrain, which is the thing i was missing. and the retention/export line matters more than it looks, those feature snapshots can hold what i was reading and when, so an append-only log of my reading intent is its own sensitive thing, keeping it local and prunable isn't optional. this is basically the receipt spec for the next version, thanks for spelling it out.

Collapse
 
sikamikanikobg profile image
Arsen Apostolov •

the 44% wrongly-dropped figure next to the 0.60 precision is the honest move in this whole post, and it's the one i'd act on before pointing it at a real pile: at that setting you're silently throwing away nearly half of what you'd actually read, so UNPILE_KEEP_T needs to be tuned per person, not shipped at a default. the "did you ever read it" label is the right training signal too, because it captures the gap between saving and reading that no clickstream does. one thing i'd add from running a similar triage on my own reading backlog: the digest quality is where the tool wins or loses, and a two-line Gemma note that just restates the title reads as noise — the keep/skim/drop reasons are the part worth surfacing in the morning, not the summaries.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf •

the digest point is the one that actually lands, and i think you're right. a two-line gemma note that just restates the title is noise, i've seen it do exactly that on short posts where there's nothing to summarize so it pads. the keep/skim/drop reason is the real signal because it's a decision, not a recap, "drop, off your lane and eight months stale" tells me more in the morning than any summary of the article itself. i've been treating the digest as the headline feature when the reason line is the thing people would actually read. on the threshold you're also right that a shipped default is wrong, 44% dropped at one global setting means i'm punishing people whose reading taste doesn't match mine, it has to be tuned per person or better, learned per person from their own "did you ever read it" history instead of a dial they set once and forget. did your own backlog triage end up needing a per-person threshold too, or did you find one setting that held up across different kinds of saves?

Collapse
 
suhassuren profile image
SuhasSuren •

Respect for putting the 44% wrongly-dropped number right next to the precision. Most people would only show the 0.60. Did you try a baseline with just the reopen count? I'd guess "I reopened this tab twice" carries a lot of the signal on its own, and it'd be interesting to see how much TabPFN adds on top.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf • • Edited

good question and it's the one i should've put in the article. i didn't run a clean reopen-count-only baseline, which in hindsight is the obvious ablation. my gut agrees with you, "reopened twice" is probably carrying a big share of the signal on its own, reopens are about the strongest intent signal in the whole feature set. what i think tabpfn is adding on top is the interaction between reopens and the softer features, source, length, how stale the save is, topic being in your lane, a single reopen on a short in-lane how-to means something very different from a single reopen on a long off-topic piece saved eight months ago, and a plain reopen threshold can't see that. but "i think" isn't "i measured," so that's a real gap. the clean test is reopen-count logistic regression vs the full tabpfn feature set on the same held-out slice and just read the lift difference. did your file-sorter end up leaning on one dominant feature like that, or was the signal spread out more evenly?

Collapse
 
debashish_ghosal profile image
Debashish Ghosal •

Fantastic article and very useful software. My 2nd brain repo had a ton of early article links I would try to read but it got too much. I cleaned up those stores and finally settled on some human triggered scripts backed by LLM that finds new articles and extracts the core ideas and saves under topics. If it’s a +1, it updates a score. I focus on various topics like AI adoptions challenges, agentic and so on. As I pick new projects, I refer to those learnings to apply to my PRDs, design and so on. I try to run cleanup and compact the learnings. Compaction is a harder part as that’s where I need to invest more as LLM does not understand. I have a topic, interest list Md file that guides but it’s still not perfect.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf •

this is a really good setup, honestly more disciplined than what i did. the compaction part you mentioned is the exact wall i kept hitting too. the llm is fine at pulling core ideas out of a single article but the moment you ask it to merge ten notes on "agentic challenges" into one compacted learning, it either drops the specific detail that made the note worth keeping or it blurs everything into generic mush. i didn't solve that in unpile, i side-stepped it, the model only decides keep/skim/drop and writes a short digest, it never tries to compress the actual knowledge. the scoring idea where a +1 bumps a topic is clever though, that's a real signal and it's yours, not some average. one thing i'm curious about, when you compact, are you compacting at the topic level or per idea? i found topic-level compaction is where the detail dies, because two ideas under
"ai adoption" can be saying opposite things and the llm averages them. keeping compaction per-idea and only grouping at read time felt safer when i thought about it, but i never built it. how are you handling that today?

Collapse
 
jn_141414 profile image
JN •

This is intresting great work

Collapse
 
sarvar_04 profile image
Sarvar Nadaf •

Thank you so much

Collapse
 
loki_63c4c2b8 profile image
Lokesh Saravanan •

Deleting 1,242 articles on purpose takes guts. How did you protect yourself from the model deleting something you wanted? I used preview + undo for the same fear in my file-sorting entry.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf •

honest answer, for this experiment i didn't have a hard undo and that's a fair hit. unpile's real design keeps it safer, it doesn't hard-delete, it silences, so the item is archived not gone, and every decision ships with the reason it was made so you can scan what got dropped. the 44% wrongly-dropped number is exactly why a preview + undo like yours is the right call, i'd rather move the cost to a rare reach-back than to a permanent loss. going to add a proper quarantine window in the next version. nice approach on the file-sorting entry.