Build TextCortex

YESback to the verdict

0%0 of 13 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 local writing workspace whose hard problems are never losing the paragraph a rewrite replaced, tracking which spans a model wrote through every subsequent edit, and exporting in a way that carries that record with the text.

estimated effort one sittingthe files for this build are in the project pack

RuntimeNext.js 15 with TypeScript, running locally, single userStorageSQLite beside the application, with every version retainedEditorA plain text or rich text editor with offset-addressable contentModelOne call per instruction run, with the run recordedProvenanceSpans recorded at insertion, remapped on every edit, exported alongside the text

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 application and the test suite.

    Get it nodejs.org or your version manager. open ↗

    Verify node --version prints v22 or higher

  2. API keypay per run; drafting a few thousand words a week is small change

    Why One call per instruction run, and the only part of this build that leaves the machine or costs anything.

    Get it Create a key with your preferred provider and set a spend limit in their console as well as in the application.

    Verify Running an instruction produces a new version and a run row with token counts

  3. decidefree

    Why The disclosure obligation attaches to publication rather than to generation, and the editorial review exemption only helps if somebody actually reviews it.

    Get it Read the current transparency article, decide which side of it your use sits on, and write that decision into the README. open ↗

    Verify The README states the decision and the date it was made

  4. have readyfree

    Why Provenance remapping only breaks on real editing, where text is moved, pasted over and partly rewritten, and a synthetic test document never does any of that.

    Get it Take something you actually drafted with a model and rebuild it in the application.

    Verify Every paragraph shows an origin and none of them is unknown by accident

Data model

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

- documents(id, title, created_at, updated_at, current_version_id)
- versions(id, document_id, ordinal, body, created_at, reason, parent_version_id) · every version is kept and reason says why it exists
- spans(id, version_id, start_offset, end_offset, origin, model_id, instruction_id, at) · origin is typed, generated, edited or unknown
- instructions(id, name, body, created_at, archived_at) · reusable prompts, archived rather than edited in place
- runs(id, version_id, instruction_id, model_id, input_hash, output_hash, tokens_in, tokens_out, cost_minor, status, at)
- snippets(id, name, body) · the boilerplate you paste often, kept out of the prompt library so it is not confused with instructions
- No row in this schema is ever overwritten with generated text. A generation creates a new version whose parent is the one you had.

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
MODEL_API_KEYsecretrequiredsk-...redactedOne call per instruction run. Set a provider-side spend limit as well.
MODEL_IDrequireda text model identifierRecorded on every run and every generated span, because output differs by model.
DATABASE_PATHrequired./data/writing.dbDocuments, versions, spans, instructions and runs. Back this up.
MONTHLY_COST_CEILINGrequired4.00Generation stops here and the editor keeps working.
EXPORT_DIRrequired./exportsMarkdown plus the companion provenance file for each export.

The build, in order

  1. A document that cannot lose the paragraph you liked

    Every change that a model makes creates a new version and the previous body survives byte for byte.

    1. Append only, at the storage layer rather than by convention.

      Files src/lib/versions.tstests/versions.test.ts

    2. So the history reads as a story rather than a stack.

      Files src/lib/schema.sql

    3. Going back should not destroy what you are going back from.

      Files src/lib/restore.tstests/restore.test.ts

    done when · tick each as it passes
    watch out
    • Applying a rewrite by replacing the document text and trusting the editor's undo stack means the paragraph you had before the rewrite is gone the moment the page reloads, and losing work to the assistant is the fastest way to stop using it. Make every generation a new version row with a parent, keep the previous body untouched, and give the save path no way to write over an existing version.
  2. Knowing which words are yours

    Provenance is recorded at insertion and survives every subsequent edit.

    1. A diff between versions is a guess about who typed what.

      Files src/lib/spans.tstests/spans.test.ts

    2. The span has to follow the words, not the position.

      Files src/lib/remap.tstests/remap.test.ts

    3. Better a gap you can see than a claim you cannot support.

      Files src/components/Provenance.tsx

    done when · tick each as it passes
    watch out
    • Storing one flag saying the document was AI generated is useless by the second paragraph, because half of it is now yours and the half that is not is scattered through it; recomputing provenance later by diffing versions is guesswork dressed as a record. Record the origin at the moment of insertion with offsets, remap those offsets on every keystroke batch, and make unknown a real state you can see.
  3. Instructions you can reproduce, and a bill that stops

    A rewrite that worked last week can be run again with the same instruction and the same model.

    1. An instruction referenced by old spans is history, not configuration.

      Files src/lib/instructions.tstests/instructions.test.ts

    2. Reproducibility and the bill come from the same table.

      Files src/lib/runs.ts

    3. A writing tool should not stop being a text editor.

      Files src/lib/cost.tstests/cost.test.ts

    done when · tick each as it passes
    watch out
    • Keeping the prompt in the code and tweaking it until the output looks right means the rewrite that worked last week cannot be reproduced and you cannot tell whether the model changed or you did. Store instructions as rows, archive instead of editing, reference the instruction identifier on every run and every generated span, and record the model identifier next to it.
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 ai writing.