Commit Graph

5 Commits

Author SHA1 Message Date
aaa9a253ae Add import/export fidelity gate to CI (#69)
Some checks failed
CD / Deploy to Test (push) Blocked by required conditions
CD / Smoke tests against Test (push) Blocked by required conditions
CD / Promote to Int (push) Blocked by required conditions
CD / Build and push images (push) Failing after 27m51s
CI / Lint, typecheck, test (push) Successful in 3m11s
CI / Auth e2e pack (push) Successful in 4m0s
CI / Import/export fidelity gate (push) Failing after 36s
CI / Build container images (push) Has been skipped
Make the "structure-true best effort" fidelity contract (ADR 0009) an
objective, pipeline-gated suite so "best effort" cannot erode silently.

- New CI job "Import/export fidelity gate" (.gitea/workflows/ci.yml) runs
  the corpus suites against the pinned sidecar images the stages use
  (pandoc/core:3.6, gotenberg/gotenberg:8), started via docker run and
  reached over the host gateway. Small and separate so it stays well under
  five minutes; the suites self-skip in the main checks job (no sidecars).
- Export fidelity: fixtures/export corpus + gen-export-fixtures.mjs +
  export.fidelity.test.ts — exports Markdown to docx/odt through the real
  pinned pandoc and reads it back, snapshotting the round trip so a writer
  drift (ours or a version bump) fails the gate.
- PDF smoke: pdf.fidelity.test.ts renders a page through real Gotenberg and
  asserts the extracted text and a sane page count (pdf-parse, dev-only).
- Fidelity contract doc: fixtures/README.md defines "corpus green = fidelity
  acceptable" and the fixture-first bug process; per-corpus READMEs updated.

Because the snapshots are byte-exact and generated with the pinned tools,
bumping a sidecar without regenerating shifts the output and fails the suite
(AC3). The import corpus (#63) is folded into the same gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
2026-07-10 13:59:10 +02:00
546e8279ac Import .docx and .odt documents as new pages (#63)
All checks were successful
CD / Build and push images (push) Successful in 3m19s
CI / Lint, typecheck, test (push) Successful in 2m55s
CI / Auth e2e pack (push) Successful in 3m45s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 9s
CD / Smoke tests against Test (push) Successful in 1m13s
CD / Promote to Int (push) Successful in 11s
Uploading a Word/OpenOffice document to POST /ponds/:id/import enqueues a
conversion job (the #62 queue) that produces a new page in the pond; the
client polls GET /jobs/:id for the created resultPageId.

Pipeline (ImportService, ADR 0009): pandoc-server is stateless and hands
back a document's media no other way, so we convert in two passes —
docx/odt → html with embed-resources inlines every image as a data: URI,
then html → gfm produces clean structural Markdown with those data URIs
still inline. Embedded images are stored as pond files (with quota
accounting) and their references rewritten to file ids on the Markdown
text before parsing (the editor parser only admits png/jpeg/gif/webp data
URIs); an image whose bytes the upload pipeline rejects is dropped, not
fatal. The title comes from a leading top-level heading (removed from the
body) else the file name. The page is created from the resulting Yjs state.

The shared conversion worker routes import-kind jobs to the pipeline via a
token (breaking a module cycle), so import inherits the queue's locking,
retry, and restart-survival. Media stored during a failed attempt is rolled
back; a pond that runs out of storage fails the job with quota_exceeded.

- schema: ConversionJob gains pond_id / source_name / result_page_id
  (migration 20260710041215_import_pages_conversion); ConversionJobView
  gains resultPageId.
- PagesService.createWithState / yjs-content docToState build a page from a
  prepared document; FilesService.linkAttachmentsToPage links import media.
- fixtures/import/: representative .docx/.odt corpus (headings, lists,
  nested lists, tables, images, links, bold/italic) with expected-Markdown
  snapshots; scripts/gen-import-fixtures.mjs regenerates them.
- tests: import.service.db.test.ts drives the full pipeline with a fake
  converter (CI); import.fixtures.test.ts runs the real two-pass conversion
  over the corpus and a 50-page timing check against a reachable sidecar.
- i18n: import_unsupported_format (de+en). Limits documented (25 MiB input,
  60 s per pass).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
2026-07-10 07:34:43 +02:00
12035e2231 Add M2 fixtures and content regression pack (#32)
Some checks failed
CD / Build and push images (push) Successful in 2m2s
CI / Lint, typecheck, test (push) Successful in 1m44s
CI / Auth e2e pack (push) Failing after 1m50s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 8s
CD / Smoke tests against Test (push) Successful in 1m8s
CD / Promote to Int (push) Successful in 10s
Seed script extends the fixture matrix with a shared "Content Fixtures"
pond (owned by fixture-user): an "Every Element" page covering every
editor schema node and mark (#24), and a "Fixture Image" page with one
real, servable uploaded image. "Every Element" loads a checked-in Yjs
snapshot (prisma/fixtures/content-page.yjs) generated from a
human-readable Markdown source (content-page.md) via a deterministic
regeneration script (pinned Y.Doc clientID; refuses to write a
snapshot that isn't a fixed point of the Markdown round-trip).

New apps/web/e2e/content.spec.ts consolidates the M2 content
regression pack: page lifecycle, editor basics, image paste, trash,
and — the pack's actual regression pin — a byte-for-byte comparison of
the fixture page's exported Markdown against the checked-in fixture.
Verified this catches regressions: temporarily mutated
docToMarkdown's heading serializer, rebuilt, re-seeded, confirmed the
comparison failed, then reverted.

This pack now runs in CI (a second step in the existing auth-e2e job,
reusing its already-built-and-seeded stack) alongside the existing
local-only feature packs.

Closes #32
2026-07-08 13:14:48 +02:00
cad0e554b5 Exclude the CI pnpm store from lint and git
Some checks failed
CD / Build and push images (push) Successful in 37s
CI / Lint, typecheck, test (push) Failing after 57s
CI / Build container images (push) Has been skipped
CD / Deploy to Test (push) Successful in 7s
CD / Smoke tests against Test (push) Successful in 1m3s
CD / Promote to Int (push) Has been cancelled
The pnpm cache used by the CI workflow lives in .pnpm-store/ inside
the workspace; Prettier and ESLint must not descend into it.

Part of #8

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 00:25:34 +02:00
b16d23297e Scaffold pnpm monorepo with lint, format, and test tooling
pnpm workspace with apps/web, apps/api, apps/collab, and
packages/shared; strict TypeScript base config, repo-wide ESLint (flat)
+ Prettier, Vitest per package, and root scripts lint/typecheck/test/
build. @dorfteich/shared ships a first health-response helper consumed
by apps/api to prove workspace linking. Existing markdown docs are
reformatted once by the new Prettier setup.

Closes #1

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 19:06:27 +02:00