Build Resurf

KINDAback to the verdict

0%0 of 16 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A reading capture tool whose real problem is the return path: saving is easy and everyone does it, but a bookmark folder nobody opens is not a memory aid, so the engineering is in surfacing the right thing at a moment when it is useful rather than at a scheduled time nobody wants.

estimated effort weekendthe files for this build are in the project pack

CaptureA browser extension and a hotkey, both storing the readable textStorageLocal SQLite with full text searchExtractionReadable content extraction at capture time, kept verbatimSurfacingContext-triggered rather than scheduledRuntimeA local application with no account and no sync by default

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the local application and builds the extension.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. installfree

    Why Capturing the whole page markup stores navigation and advertisements, which pollutes search and wastes space.

    Get it A maintained readability extraction library, tested against pages you actually read.

    Verify A news article, a documentation page and a long blog post all extract cleanly

  3. have readyfree, and it takes a month

    Why Resurfacing quality cannot be evaluated on a synthetic set, since the whole question is whether an old item is genuinely relevant to a new one, and only you can judge that.

    Get it Use the capture for a month before building phase 4, so there is something real to surface.

    Verify At least two hundred captures with some highlights exist

  4. decidefree

    Why Phase 4 is the whole product and a wrong answer produces an annoying tool, so decide what triggers you will support before implementing any of them.

    Get it Note the last five times you wished you could find something you had read, and what you were doing at the time. Those are your triggers.

    Verify A written note lists at least three trigger situations from your own experience

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- captures(id, url, title, text, excerpt, captured_at, source, reading_time_ms) · text stored at capture, not fetched later
- highlights(id, capture_id, text, note, at) · what you actually marked, which is a much stronger signal than the capture
- topics(id, label) and capture_topics(capture_id, topic_id, confidence) · derived, never asked for at capture
- surfaces(id, capture_id, trigger_kind, triggered_at, acted_on, dismissed) · what was shown, why, and what you did
- dismissals(capture_id, count, last_at) · dismissing the same thing three times means stop
- Everything findable is derived after capture, because any field asked for at save time is a decision that slows saving down until saving stops.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DB_PATHrequired~/.local/share/resurf/captures.dbEverything you have read. A record of what you were thinking about.
CAPTURE_PROMPTrequiredneverPresent as a constant so the answer is visible. A prompt is a decision and decisions end habits.
DISMISS_LIMITrequired3How many dismissals before an item stops resurfacing. Three is an answer.
SCHEDULED_DIGESTrequiredneverPresent as a constant. A scheduled digest of old links is unread within a week.
COMPRESS_AFTER_DAYSrequired180Old text is compressed rather than deleted, since deleting removes what resurfacing runs on.
SYNC_REMOTEoptionals3://my-reading/archiveOptional and off by default. Absent means one machine, which is a fine way to run.

The build, in order

  1. Capturing without deciding anything

    Saving something takes one action and no thought.

    1. No tags, no folder, no note. The absence is the design.

      Files extension/capture.ts

    2. Later means a page that changed or disappeared.

      Files src/extract.ts

    3. The text is on screen. Capture should not need a network.

      Files src/capture.ts

    done when · tick each as it passes
    watch out
    • Any field offered at capture, even an optional one, is a decision, and a decision at capture time is what turns a fast habit into a slow one that stops within a month. Capture with no prompt at all, and derive everything findable afterwards.
  2. Highlights, which mean far more than captures

    What you marked is distinguished from what you merely saved.

    1. With surrounding context, so a small change does not orphan them.

      Files src/highlights.ts

    2. People save everything and highlight only what landed.

      Files src/signals.ts

    3. Leaving the page to add a note is a decision, and decisions stop habits.

      Files extension/highlight.ts

    done when · tick each as it passes
    watch out
    • Treating a highlight the same as a capture wastes the strongest signal in the system, since people save far more than they read and highlight only what actually landed. Weight highlights heavily in everything downstream and count them separately.
  3. Finding it when you go looking

    Full text search across everything you read finds it instantly.

    1. Titles and URLs find nothing. You remember phrases.

      Files src/search.ts

    2. Never ask at capture. That was phase one's whole point.

      Files src/topics.ts

    3. Ten thousand captures. Slower than typing means it is not the default way in.

      Files src/search.ts

    done when · tick each as it passes
    watch out
    • Searching only titles and URLs finds almost nothing, because you remember a phrase from the middle of an article rather than its headline. Index the full captured text, which you have precisely because it was stored at capture time.
  4. Bringing things back at a useful moment

    Something you saved reappears when it is relevant, not on a schedule.

    1. The related page is the moment the old one becomes interesting.

      Files src/surface.ts

    2. So you can tell whether resurfacing works rather than assuming.

      Files src/surfaces.ts

    3. Three dismissals is an answer.

      Files src/dismissals.ts

    done when · tick each as it passes
    watch out
    • A daily digest of old saves is unread within a week, because a scheduled reminder arrives when you are doing something else and old links are not interesting out of context. Trigger on context: reading a related page is the moment the old one is worth seeing.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more bookmarks.