12
29 Comments

The easiest version of generation history was probably the least useful one

I recently shipped a lightweight generation history feature for my AI product.

The first version was straightforward: save successful outputs for signed-in users and show the most recent ones in a grid.

It was familiar, useful, and relatively easy to build.

But once it was working, I realized it solved a much narrower problem than the name “history” suggested.

It was really a gallery.

A user could find an old result, but they could not necessarily continue the work that produced it.

That distinction matters in a generative product.

When people say they want history, they may actually be asking for several different things.

Retrieval means finding an old result.

Reproduction means running the same input and settings again.

Continuation means reopening an earlier idea and carrying on from where you stopped.

Variation means keeping most of a result while changing one specific part.

A grid of old outputs only solves retrieval.

Even reproduction is harder than it first appears. Saving the visible prompt may not be enough. The output can also depend on selected controls, hidden instructions, model version, provider behavior, uploaded files, and generation settings.

A saved prompt can make a result look reproducible even when it is not.

Continuation is a different problem.

If a user opens an old result, should the product restore the original form values? Should the next generation become a new branch? Should related results stay grouped together? Should the old result remain untouched?

Then there is variation, which may be the thing users actually want.

They often do not want to recreate the exact same output. They want to say:

“Keep the composition, but remove this element.”

“Use the same idea, but make it narrower.”

“Keep everything except the colors.”

A gallery cannot do that.

This made me realize that generation history is often treated as one feature even though it contains several different product problems.

The easiest version to ship is to save outputs and display them again.

That still has value. Users can recover something they forgot to download, revisit an earlier direction, or compare results.

But it can also create the appearance of continuity without providing any real continuity.

I am keeping the lightweight history page because retrieval is useful on its own.

I just no longer see it as the solution to an iterative workflow.

A gallery, reproducibility, continuation, and controlled variation are separate capabilities. Calling all of them “history” makes it too easy to build the simplest one and assume the problem is solved.

How does history work in your generative product?

Can users only find old results, or can they actually continue from them?

posted to Icon for group Building in Public
Building in Public
on August 7, 2026
  1. 1

    The store-the-facts-plus-a-ruleset-pointer shape is right, and there's one failure in it I hit the hard way: a version pointer is only worth as much as the immutability of the thing it points at.

    I do the same in a compliance validator, and the rules come from standards bodies that reuse filenames and version labels. Same declared version, different bytes, published quietly. If your pointer is a version string and the artifact behind it is mutable, an old verdict re-derives against rules that aren't the ones that produced it, and nothing anywhere reports a discrepancy. Yours lives in git, so you're mostly fine, but that's a property of git rather than of the design.

    What I ended up storing is a content hash per rule artifact that actually executed, plus a combined hash of the set, returned to the caller so they can recompute it themselves. Then "re-derive this verdict" and "diff it against today's rules" are both exact operations, and a changed upstream is detectable instead of silent. Separate job just watches for artifacts whose bytes moved without their label moving.

    Your fifth item is the right one to add, and it's worth saying it isn't a user feature. Nobody asks for it until someone asks why the system said what it said six months ago, and by then you either stored it or you didn't.

  2. 1

    The distinction between retrieval and continuation is the important part.

    A lot of products treat history as “store the output and show it later,” when the real value is preserving enough context to continue the work without rebuilding the state manually.

    There’s actually a way to automate a lot of that continuity and remove much of the manual reconstruction between generations.

    I have a solution that could help with this and save a lot of work automatically. Happy to share if you're interested.

  3. 2

    We hit the same wall building AI caption and post generation into SocialPost.ai. The distinction that mattered most in practice wasn't retrieval versus reproduction, it was that users almost never wanted the exact old output back, they wanted the thing that produced it: same brand voice and constraints, different input. We ended up saving the settings and voice profile as the durable object and treating the output itself as disposable. Worth asking whether your users are trying to save a result or save a recipe.

    1. 1

      That “save a result vs. save a recipe” framing is useful.

      I’m starting to think the durable object may be closer to the generation state than the image itself: inputs, style, placement/proportion, and the choices around it. The old output still matters as a reference, but it may not be the thing the user actually wants to continue from.

      That also makes “history” feel less like storage and more like a starting-point system.

  4. 2

    My product isn't generative in the usual sense — it's an audit layer that checks AI-generated output against published guidelines — but reproduction is more or less the whole job, so that's the section I've spent the most time on.

    The part I'd add: I'm not sure reproduction is achievable at all in a generative product, and it's worth naming that early rather than discovering it later. Even with the prompt, the settings, the uploaded files and the model version all pinned, the same request doesn't reliably come back the same. Provider-side batching and hardware nondeterminism are enough to break it on their own. So the honest goal probably isn't "run it again and get the same thing" — it's "reconstruct the exact state that produced it," and be upfront with the user that those aren't the same promise.

    That reframing changed what I store. I don't save the output. I save the specific facts the verdict was checked against, plus a version pointer to the rule set that evaluated them, which lives in git. So an old verdict can be re-derived, and diffed against what today's rules would say about the same facts. The generative step sits deliberately outside that path, because it's the one part I can't re-derive.

    Which maybe points at a fifth item for your list — one that isn't a user need at all. Someone asking "why did it say that, six months ago?" For that one, a gallery is worth nothing.

  5. 2

    Bence burada en kritik ayrım, geçmişi görüntülemek ile gerçekten kaldığın yerden devam edebilmek arasında. Eski çıktılara erişmek kesinlikle değerli ama gerçek süreklilik için önceki bağlamın, seçimlerin ve yapılan değişikliklerin de korunması gerekiyor. Kullanıcı aynı çalışmayı yeniden kurmak zorunda kalıyorsa, bu daha çok bir arşiv işlevi görüyor. İyi bir üretken üründe geçmişin sadece “ne üretildiğini” değil, “buradan nasıl devam edebilirim?” sorusunu da çözmesi gerektiğini düşünüyorum.

  6. 2

    The naming problem compounds everything else here.

    When you label a feature "history," users arrive with a mental model built from browser history, chat history, file version history. All of those support continuation. The word carries the implicit promise that you can go back and pick up where you left off. When they discover it's retrieval-only, the gap isn't just functional — it feels like a promise that wasn't kept.

    Calling it a "gallery" from the start changes the expectation before users form it. A gallery implies browsing, not resuming.

    The split you described — retrieval, reproduction, continuation, variation — maps loosely to different user needs at different stages of the workflow. Retrieval is recovery behavior ("I lost that thing"). Reproduction is consistency behavior ("I need the same result for a different input"). Continuation and variation are creative behavior ("I want to keep going from this point"). Most products ship the recovery case and accidentally inherit user expectation for the creative cases.

    The practical test I'd suggest for continuation specifically: can a user open an old result and start a new generation that treats the old output as its starting state, without touching the original? If the answer is no, it's retrieval. If yes, you have something closer to branching, which is a meaningfully different product.

    In Genie 007 we solved this by separating the concepts entirely: saved outputs are a gallery, reusable templates are a different object, and branches are a third thing. Conflating them caused months of confused user feedback before we split them.

    1. 1

      The branching test is probably the cleanest way I’ve seen to separate retrieval from continuation.

      If opening an old result only lets you view it, it’s a gallery. If you can reuse its state to create a new path without changing the original, that’s something meaningfully different.

      I also like separating saved outputs, reusable inputs/templates, and branches instead of asking one “History” screen to pretend they’re the same thing.

  7. 2

    The naming is doing a lot of hidden product work here. If the page is called history, users assume they can act on the past, not just look at it. One practical way to keep the lightweight version honest is to label the actions very literally: view result, reuse inputs, branch a variation, compare with current settings. Even if only the first one works today, the labels make the missing workflow visible instead of letting a gallery quietly masquerade as continuity.

  8. 2

    The distinction between a gallery and actual workflow history is really important. Being able to retrieve an old result is useful, but being able to reopen the context behind it and make a controlled variation feels much closer to what users expect from “history.” I especially like the idea of treating continuation and variation as separate capabilities rather than trying to force everything into one history view.

  9. 2

    One more distinction I’d add is whether a historical generation is still actually replayable.

    Even if you save the prompt and every visible setting, the original model may disappear, a provider may change behavior, or your own hidden instructions may evolve. At that point “run this again” can quietly become “make something similar using today’s system.”

    I’d consider storing an immutable run manifest with the result:

    model/provider version, app config version, relevant hidden-prompt version, input/file fingerprints, settings, and seed where one exists.

    Then the UI could distinguish:

    “Replay original recipe” — the required runtime still exists
    “Run with current engine” — same user inputs, newer system
    “Reference only” — exact replay is no longer possible

    I think that honesty matters because otherwise a history feature can promise reproducibility long after the underlying environment has stopped being reproducible.

    It would also be interesting to measure how often users choose exact replay versus deliberately moving an old idea onto the current engine.

    1. 1

      This is a really good distinction. I hadn’t separated “replayable” from “retrievable” clearly enough.

      I especially like the idea of being explicit when an old run can no longer be reproduced with the original environment. “Run again” sounds much stronger than “reuse these inputs with the current system.”

      I’m not sure I’d store everything needed for perfect replay from day one, but the distinction itself feels important enough to model now rather than discover later.

      1. 1

        Exactly — I wouldn’t make perfect replay a day-one requirement either.

        The important part is avoiding a data model that assumes retrievable always means replayable.

        Even a minimal manifest can preserve that distinction: model identifier, app/config version, prompt or template version, key settings, and a flag for whether the original runtime is still available.

        Then you can add stronger reproducibility later without changing what an old history record means.

        Modelling the distinction now is probably much cheaper than trying to retrofit it after users already trust a “Run again” button.

  10. 2

    I like the distinction between “history” as a gallery and history as a way to continue working. ~

    That feels easy to miss when the first version is just saving outputs.

    The four use cases are a useful way to think about it: retrieval, reproduction, continuation, and variation. They sound similar at first, but they lead to very different product decisions.

    The variation example especially makes sense. Someone often doesn’t want the exact same result back—they want to keep the useful parts and change one thing.

    It’s also a good reminder that a simple feature can still be valuable without solving the entire workflow. A gallery can be useful on its own; it just shouldn’t be mistaken for full generation history.

  11. 1

    The retrieval vs. continuation distinction is the part that usually gets skipped. It's easy to ship a grid of past outputs and call it "history," because technically the data is all there — but the moment a user actually wants to pick back up where they left off, the grid stops helping and the guesswork starts.

    The point about reproducibility being harder than it looks is underrated too. A visible prompt captures maybe half of what actually produced a result — model version, hidden system instructions, and generation settings can all quietly shift the output even when the saved prompt looks identical to last time.

    Variation seems like the real unlock buried in here. Most users aren't trying to recreate a result exactly — they're trying to keep 90% of it and change one specific thing. That's a fundamentally different interface problem than "show me my old stuff," and it's probably why so many generative products still feel like a search box instead of a workspace.

    1. 1

      The “keep 90%, change one thing” framing is very close to what I’m seeing too.

      That’s why variation is starting to look more important than exact replay. A user may want the same idea and composition, but try a different style, placement, proportion, or level of detail.

      A gallery can’t really express that relationship between versions. It probably needs some notion of “derived from this result/state.”

  12. 1

    The interesting product-language problem here is that “history” is doing too much semantic work. You’ve already uncovered four different user intentions, but the user still encounters one familiar label that hides those distinctions. That matters beyond UX: the label shapes what users expect the product to let them do. Someone looking for “history” may be satisfied by retrieval, while someone wanting to continue or vary a generation experiences the same feature as incomplete. I’d be curious whether naming the actions by intent—retrieve, rerun, continue, vary—would change how users perceive the feature before you change the underlying product at all.

    1. 1

      That’s a really interesting angle. I’ve been thinking mostly about the underlying jobs, but the naming may be an easier thing to test first.

      “History” can imply a passive archive, while actions like continue, rerun, or vary make the possible next step much clearer.

      It might be a good way to learn which intent users actually have before building all of the deeper workflow.

  13. 1

    This maps to something I ran into with money instead of generations. Users ask a dashboard "what's my history" but actually mean at least two different things: what did I bill (facturado) and what did I actually collect (cobrado). Early on I showed one blended number because it read as simpler, and people made decisions off it that didn't match reality, planning around money that was invoiced but not yet in hand. Splitting them into two figures that never merge felt like a downgrade in polish. It fixed the actual complaint. On the verified-vs-inferred question in the comments: I didn't have usage data to lean on, I just noticed I personally kept asking the dashboard two different questions and getting one answer. Not as rigorous as your four-way breakdown, but the fix is the same shape: stop treating an aggregate as if it answers questions it can't distinguish between.

    1. 1

      That’s a great parallel. The “invoiced vs actually collected” example makes the distinction really concrete.

      I like the way you put it: the problem starts when one aggregate is expected to answer questions it can’t distinguish between. That’s basically what made me rethink “history” as one feature too.

      1. 2

        The part that surprised me after splitting them: people still asked for the single number back. The request is real, but what they actually want is a projection, not a total, and that's a different feature with different error bars. I kept the two figures separate and added the forward-looking view as its own thing rather than merging them to satisfy the ask. Might be worth watching for the same pressure with history. Once you split it into four jobs, someone will ask for one view that does all four, and the temptation is to build a combined screen that quietly re-merges what you just separated. The split only holds if you give the merge request its own home.

        1. 1

          That’s a good warning. I can already see how easy it would be to solve the “too many separate actions” problem by putting everything back into one overloaded screen.

          The part I like in your example is giving the merged need its own job instead of pretending the underlying distinctions disappeared.

          If I get to that point, I’d probably want one place to access the options, but still keep the actions and resulting states distinct.

  14. 1

    The branch model seems like the cleanest way to separate these jobs. Save an immutable recipe for each generation, then let retry or variation create a child run instead of overwriting the original. I would also label the actions by intent, like rerun, continue, and make a variation, instead of hiding all three under history. That gives users a clearer choice and gives you much better data on why they wanted the old result.

    1. 1

      I like this a lot. Especially labeling the action by intent instead of hiding everything behind “history.”

      A child run also feels cleaner than mutating the original because it preserves what actually happened while still allowing continuation or variation. And you’re right that the action itself would give much better behavioral data about why someone returned to the old result.

  15. 1

    The distinction between retrieval, reproduction, continuation and variation is useful.

    Have users actually shown you which of those they’re trying to do after returning to an old generation, or is that still something you’re inferring from the workflow?

    1. 1

      Good question. There is some behavioral validation behind it.

      I’ve seen users stay with the same idea and change one variable at a time — placement, proportion, style, etc. — then generate again. So the continuation/variation side is coming from observed behavior, not just theory.

      What I haven’t validated yet is whether these four buckets map cleanly to how users themselves would describe the need. That part is still my product framing.

      1. 1

        That distinction between observed behavior and the framework you build around it is interesting.

        I’d like to continue the conversation outside the thread. What’s the best email to reach you on?

  16. 1

    What I enjoyed most about this was watching one feature request split into several different user intentions.

    It reminded me that users usually describe problems using the language they have, not the language the product eventually needs. "History" sounds like one capability until you start asking what someone is actually trying to do with it.

    I think that's one of the easiest traps in product design: building exactly what users asked for, instead of uncovering the different jobs hidden inside the request.

    1. 1

      Exactly. “History” sounds like one feature until you ask what job the user is actually trying to complete. That distinction is what changed how I think about it.

Trending on Indie Hackers
I Just Discovered My Analytics Numbers Are Mostly Fake. Here Is Why. User Avatar 96 comments Co-founders suck… User Avatar 82 comments I built an AI that finds the right product for your customers User Avatar 41 comments I built a tool to find people already talking about problems your product solves User Avatar 34 comments Solo-built Pistly for months. Launching on PH this week and I still don't know if the market wants it. User Avatar 34 comments