Skip to content

feat: Korean-aware ingestion with resolvable citations — closes #3 - #20

Merged
mikim merged 2 commits into
mainfrom
feat/ingestion-rework
Jul 14, 2026
Merged

feat: Korean-aware ingestion with resolvable citations — closes #3#20
mikim merged 2 commits into
mainfrom
feat/ingestion-rework

Conversation

@mikim

@mikim mikim commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What

Replaces the blank-line-split ingestion stub with a real, deterministic, Korean-aware segmentation pipeline, closing every acceptance criterion of epic #3.

Closes #3

Acceptance criteria → implementation

Criterion How
Real segmentation (not just blank-line split), Korean + headings Section split on Markdown #, 제N장/절/편/부/과, numbered 1. / 1.1 / 2), roman I., and 가. 나. headings; paragraph split within sections. Zero deps, fully deterministic.
Citation offsets that resolve back to the source Hard invariant, tested on every unit: rawText.slice(citationStart, citationEnd) === content. Source text is never normalized/rewritten (CRLF-safe).
Persist with correct sourceId (no pending-source-id) Units no longer fabricate ids at all. New persistIngestionResult() in @study-os/db writes StudySource + StudyUnits in one transaction with real foreign keys — integration-tested against the migrated Postgres in CI, incl. rollback atomicity.
Validation: reject empty input; deterministic ordering Empty userId/title/rawText → IngestionValidationError; 500-unit cap; orderIndex by document position; determinism test.
Unit tests over a Korean fixture 16 tests: 제N장 fixture, heading conventions, span resolution, CRLF, whitespace, truncation, validation.

Notes

  • The heading-detection test caught a real bug during development (1.1 배경 without a trailing dot wasn't recognized) — fixed with a dedicated multi-level pattern.
  • CI order changed: migrate + seed now run before tests, so DB integration tests hit the migrated database. Locally without DATABASE_URL they skip cleanly.
  • PDF ingestion remains M3 by design (text-only first).

Verified

37 unit tests pass locally (+2 DB integration tests that run in CI); lint/typecheck/build/API-smoke all green.

🤖 Generated with Claude Code

mikim and others added 2 commits July 14, 2026 16:56
… citations

Replaces the blank-line-split stub with a real, deterministic
segmentation pipeline and closes every acceptance criterion of the
ingestion epic:

- Heading-aware segmentation: Markdown (#), Korean chapter conventions
  (제N장/절/편/부/과), numbered (1. / 1.1 / 2)), roman (I.), and 가나다
  (가. 나.) headings split the document into sections; paragraphs split
  within sections. Zero dependencies, fully deterministic.
- Citation offsets on every unit with the hard invariant
  rawText.slice(citationStart, citationEnd) === content — the source is
  never normalized or rewritten, so citations always resolve, including
  under CRLF line endings.
- No more fabricated ids: units no longer carry a placeholder
  "pending-source-id"; assigning real ids is the persister's job.
- New @study-os/db persistIngestionResult(): atomic transaction writing
  StudySource + StudyUnits with real foreign keys and citation offsets;
  integration-tested against the migrated Postgres in CI (skipped
  locally without DATABASE_URL), including a rollback-atomicity test.
- Validation: empty userId/title/rawText rejected, 500-unit cap,
  title truncation at 80 chars.
- 16 ingestion unit tests over Korean fixtures (span resolution,
  heading conventions, CRLF, determinism, validation).
- CI: migrate + seed moved before tests so DB integration tests run
  against the migrated database.

Closes #3

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… import resolves

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mikim
mikim merged commit f6de8e6 into main Jul 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Build document-to-study-unit ingestion pipeline

1 participant