A web app for transcribing scanned handwritten documents. The user uploads a PDF, the system reads every page with a multimodal model, and a human verifies the result quickly — then verified pages are fed back into the prompt as hints for the pages that follow.
All project documentation lives in docs/.
| Where | What for |
|---|---|
| Project board | Epics, user stories and tasks |
| docs/README.md | Documentation entry point, implementation order |
| docs/07-how-it-works.md | How the app works, in plain words |
| docs/08-template-gaps.md | What the template already has, what is a stub, what is missing |
| docs/09-open-questions.md | What is still undecided before implementation starts |
| diagrams/README.md | Seven diagrams with walkthroughs |
Handwritten archives: parish registers, medical records, diaries, ledgers. Ordinary OCR cannot read them at all, and specialised HTR requires dozens of hours of labelling first. The key idea is context learning: pages confirmed by a human are fed back into the model's prompt, so accuracy grows as the work goes on.
Details: docs/00-overview.md, docs/03-core-logic.md.
- NodeJS (22.x.x);
- npm (11.x.x);
- PostgreSQL (17.5)
9 tables, 3 views, 4 enums. The source of truth is
schema/schema.sql; it is applied through Knex
migrations in apps/backend/src/db/migrations/.
- Diagram: diagrams/06-database.mmd
- Rationale behind the decisions: docs/04-database.md
- Seed data: schema/seed.sql
A monolith: the API and the queue worker live in one process and can be split
apart with a single APP_MODE environment variable.
- Components and stack: docs/01-architecture.md
- Data path: docs/02-data-pipeline.md
- System overview: diagrams/01-overview.mmd
- API, 19 routes: docs/05-api.md
- React — a frontend library
- Redux + Redux Toolkit — a state manager
-
assets - static assets (images, global styles)
-
libs - shared libraries and utilities
2.1 components - plain react components
2.2 enums
2.3 helpers
2.4 hooks
2.5 modules - separate features or functionalities
2.6 types
-
modules - separate app features or functionalities
-
pages - app pages
-
db - database data (migrations, seeds)
-
libs - shared libraries and utilities
2.1 enums
2.2 exceptions
2.3 helpers
2.4 modules - separate features or functionalities
2.5 types
-
modules - separate app features or functionalities
As we are already using js on both frontend and backend it would be useful to share some contracts and code between them.
- Zod — a schema validator
- Create and fill all .env files. These files are:
- apps/frontend/.env
- apps/backend/.env
You should use .env.example files as a reference.
-
Install dependencies:
npm install. -
Install pre-commit hooks:
npx simple-git-hooks. This hook is used to verify code style on commit. -
Run database. You can run it by installing postgres on your computer.
-
Apply migrations:
npm run migrate:dev -w apps/backend -
Run backend:
npm run start:dev -w apps/backend -
Run frontend:
npm run start:dev -w apps/frontend
<project-prefix>-<issue-number>: <ticket-title>
Examples:
TSA-6: Add header componentTSA-12: Update header stylesTSA-16: Remove header component
<type>/<project-prefix>-<issue-number>-<short-desc>
Examples:
feat/TSA-6-add-headerfix/TSA-12-header-styleschore/TSA-16-remove-header
<project-prefix>-<issue-number>: <modifier> <description>
Modifiers
+(add)*(edit)-(remove)
Examples:
TSA-6: + header componentTSA-12: * header stylesTSA-16: - header component
The backlog has three levels, linked through GitHub sub-issues:
Epic: <Area> an umbrella for one product area
As <Role>, I want <X> so that <Y> a user story, what the user gets
[BE] / [FE] <what we build> the implementation tasks
Examples:
Epic: Authentication & Access ControlAs a Visitor, I want to sign in with my email and password so that I can reach my documents[BE] Sign-in flow[FE] Sign-in flow
Prefixes for tasks: [BE] backend, [FE] frontend, [Setup] project setup,
[QA] quality assurance.
Note that an issue title and its pull request title are not the same: the
issue reads [FE] Sign-in flow, while the pull request must follow 7.1 —
TSA-1: Add sign-in flow to the frontend. Danger checks the pull request title,
never the issue title.
.github/workflows/ci.yml runs on every pull request and on every push to
main, with two jobs:
- lint —
npm run lint, which fans out toeditorconfig-checker,ls-lint,tsc --noEmit,eslint,prettier --check,knip, and each workspace's own lint script - build —
npm run buildforshared,backendandfrontend
A new push to the same pull request cancels the previous run.
.github/workflows/deploy.yml runs on every push to main and ships the
backend to AWS:
- assumes an IAM role through GitHub OIDC — no AWS keys are stored in the repository or in secrets
- builds the backend image for
linux/arm64, because the instance is Graviton - pushes it to ECR under both
latestand the commit SHA - triggers a redeploy on the instance through SSM, targeting the
Project=transcriptatag
The project runs a single environment — there is no dev/staging split, so a
push to main goes straight to it.