Inspiration

Side projects get paused when life gets busy. Months later, opening the folder means reconstructing dozens of forgotten decisions before you can make any progress: what you were building, what is actually implemented, what you decided in passing notes, and why you stopped. That cost of returning is often what turns "paused" into "abandoned." Every developer has this folder.

A single prompt reads the README and believes it. Unfinished reads the README, checks it against the code and notes, and shows you where they disagree.

What it does

Unfinished reconstructs a paused software project and makes the first step back feel possible:

  1. Reconstructs what you were building and where each piece stands.
  2. Surfaces forgotten decisions, blockers, and contradictions between your notes and your code—side by side.
  3. Plans and replans around what has changed in your life, giving you one small, achievable task you can finish tonight and showing the exact diff when your constraints change.

Every finding links to clickable source lines in a read-only viewer. The app opens with a clearly labelled authored example ("Sunday Store") so anyone can explore immediately, and switches to live model inference when you click Reconstruct project. Example and live sessions are kept strictly separate in browser storage, and example data is never used as a silent fallback for failed live calls.

What makes it trustworthy

Every finding carries two independent labels answering two different questions:

  • Provenance — where did this come from?
    • explicit: directly stated in a single inspected source.
    • inferred: synthesized across clues, accompanied by wouldConfirm explaining what would verify the interpretation.
  • State — what does the static evidence show?
    • implementation_found: meaningful static implementation exists in code (a signature, pass, raise NotImplementedError, TODO, or test definition alone does not qualify).
    • contradicted: a documented completion claim disagrees with code or later notes; both sources are preserved side by side.
    • undecided: blocked on an open choice rather than code.
    • unverifiable: a note references a file absent from the inspected snapshot (such as loyalty.py). The app cites the existing note and never fabricates quotes from a missing file.
    • claimed: a documentary assertion that cannot be independently established from code.
    • not_found_in_inspected_files: requested capability has no implementation in the inspected set (only allowed when file extraction coverage is 100% complete).

Key trust boundaries enforced in code:

  • The model never writes source quotes. During extraction, the model selects 1-based line spans (startLine–endLine), and deterministic code copies the exact lines from the snapshot. During reconciliation, the model selects accepted recordIds from a project-specific schema enum, and code attaches their verified citations.
  • Static evidence is never confused with runtime execution. Project code and tests are never executed. When test files are cited, the UI notes that test definitions were observed, not run, and the review validator rejects any model output claiming tests passed.
  • Unverifiable findings cannot support restart tasks. Unsupported citations or oversized spans are rejected outright, never silently downgraded into inferences.

How we built it

  • Model & Inference: We use a single model—nvidia/nemotron-3-super-120b-a12b on Nebius Token Factory—via an OpenAI-compatible server adapter using response_format: { type: 'json_schema', strict: true }, temperature 1.0, and top_p: 0.95 per NVIDIA's model guidance. Per-file extraction runs with enable_thinking: false (up to 3 files concurrently), while cross-file reconciliation, independent source review, and planning run with enable_thinking: true, low_effort: true.
  • Five-stage pipeline:
    1. Per-file extraction: Nemotron proposes records (goal, decision, task, evidence, blocker) and line spans from line-numbered .py, .md, and .txt files. Code attaches canonical excerpts and verifies every span.
    2. Deterministic source checks: Static checks spot named-module completion claims beside explicit NotImplementedError stubs, unchecked Markdown tasks naming existing functions with non-placeholder bodies, and .py file references absent from the inspected set.
    3. Feature-level reconciliation + independent review: A synthesis call groups records by feature or decision, and a second independent review call audits the draft against all accepted records, checking that no decision or blocker records were dropped.
    4. Final citation & support verification: Deterministic code checks that every citation exists in the snapshot, belongs to the finding's referenced records, and meets the state rules.
    5. Planning & constraint replanning: Generates one small evening task (in_scope, at most 3 existing files, concrete done criteria). When the user adds a constraint (e.g., "I only want to support collection now."), the planner preserves all prior task IDs, moves incompatible work to out_of_scope with a changedReason, and computes a before/after diff.
  • Application & state architecture: Built as a single Next.js 16 (App Router) and React 19 TypeScript application with Three.js and GSAP for the interactive welcome folder scene. Each pipeline stage returns an HMAC-SHA256 signed checkpoint bound to the project's SHA-256 snapshot hash, stored locally in IndexedDB so refreshing or resuming an interrupted run never re-bills completed file extractions.

How we validated it

We evaluated the pipeline on two snapshots with different jobs, plus 26 unit tests (Vitest) and 8 end-to-end browser scenarios (Playwright):

  • 1. Prepared fixture (demo-shop — Sunday Store, 9 files read / 1 .svg skipped): Contains a misleading README (Checkout: complete while checkout.py raises NotImplementedError), an implemented subtotal() function behind an unchecked TODO, an unresolved international delivery decision, and a TODO referencing a nonexistent loyalty.py.
    • Recorded CLI run (eval/REPORT.md): 31,039 input / 6,989 output tokens (70.1s sum of call durations), 44 / 44 canonical source checks matched, and 3 / 4 expected semantic cases recalled on manual review (missing the international delivery decision as a distinct finding in that run).
    • Interactive browser live run: 28,471 input / 6,612 output tokens (40.0s wall-clock for full reconstruction; 5.0s and 3,630 / 759 tokens for live replanning), 49 / 49 canonical source checks matched, and 4 / 4 expected cases recalled—demonstrating both end-to-end responsiveness and natural run-to-run variance at temperature 1.0.
    • Changed-facts variant (demo-shop-variant): Mutates the README claim and replaces subtotal() with NotImplementedError in memory (23,201 / 7,559 tokens, 41 / 41 source checks), verifying that the old checkout contradiction disappears and a new basket contradiction appears.
  • 2. Independent public snapshot (pypa/sampleproject pinned to commit 621e4974ca25): 7 files read / 1 skipped, 15,360 input / 3,239 output tokens (28.0s), 16 / 16 canonical source checks matched. Because it has no ground-truth label set, we report coverage and inspect limitations rather than claiming a recall score.

Challenges we ran into

  • Quoted source fidelity: During early structured-output runs, a model response reproduced a newline inside a quoted Python excerpt as a literal n, and models occasionally drifted line numbers or paraphrased code. We eliminated that failure mode by having the model return only line spans and record IDs while deterministic code attaches the exact source text.
  • Token budgets with reasoning enabled: An 8,192-token limit truncated a reconciliation response when thinking tokens were enabled. Increasing max_tokens to 16,000, disabling thinking for per-file extraction while using low_effort reasoning for synthesis and planning, and adding a single schema-repair retry made the pipeline reliable within Vercel's function limits.
  • Preventing over-claiming on tests: Models naturally equate the presence of tests/test_basket.py with "verified working code." We added both prompt rules and a hard post-check regex in code that rejects any finding summary claiming tests were executed or passed.

What we learned

  • "Is this done?" is really two separate questions: where did the claim come from (explicit vs inferred) and what does the static evidence show right now (claimed, implementation_found, contradicted, undecided, unverifiable).
  • Let the model do semantic synthesis; let deterministic code own the quotes. Constraining the model to project-specific record ID enums and line spans gives you long-context reasoning without hallucinated citations.

What's next for Unfinished

  • Uploading local project folders and broader language support beyond .py, .md, and .txt
  • Using Git commit history and timestamps as evidence ("when did you pause, and what were you touching last?")
  • Improving deduplication when the reconciliation review pass repairs omitted decision or blocker records

Built With

Share this project:

Updates

Submission history