diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index aed73da..2d9570c 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -37,6 +37,9 @@ jobs: - name: Validate D1.3 deterministic data-quality reports run: node scripts/audit-data-quality.mjs --check + - name: Validate D1.4 two-book architecture + run: node scripts/validate-two-book-architecture.mjs + - name: Require a new uNews patchnote for Pull Request changes if: ${{ github.event_name == 'pull_request' }} env: @@ -69,6 +72,7 @@ jobs: node --check scripts/validate-data-provenance.mjs node --check scripts/validate-dataset-registry.mjs node --check scripts/audit-data-quality.mjs + node --check scripts/validate-two-book-architecture.mjs node --check scripts/validate-patchnote-diff.mjs node --check scripts/validate-screenshot-tooling.mjs node --check tools/screenshots/playwright.config.mjs diff --git a/ROADMAP.md b/ROADMAP.md index 697f8e5..dbd52b0 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -31,7 +31,7 @@ See `docs/MODULARIZATION_PLAN.md`. - [x] Rank exact and primary symbol matches before redirect and distant matches. - [x] Resolve Russian aliases to their owning primary cards. - [x] Make all five search filters strict to their declared fields. -- [x] Keep search filters above the expanding autocomplete panel. +- [x] Keep search filters above the expanding autocomplete list. - [x] Add release-specific screenshots for the uNews publication. ## Completed: PWA update and installation v23.8.0 @@ -67,7 +67,6 @@ See `docs/MODULARIZATION_PLAN.md`. - [x] Require planned work, actual work, branch, commit, checks, blockers and exact next action. - [x] Require a pushed handoff before switching devices or pausing unfinished work. - [x] Make every future agent read and update the handoff at task start, pause and completion. -- [x] Record the current signal as `READY`: no unfinished task or open Pull Request. ## Completed: unified AI/GitHub workflow v23.8.6 @@ -79,7 +78,6 @@ See `docs/MODULARIZATION_PLAN.md`. - [x] Require new screenshot provenance metadata in patchnotes. - [x] Require a newly added PNG/JPEG in the same Pull Request instead of reusing an older image. - [x] Add structural validation for `WORK_STATUS.md`. -- [x] Synchronize stale cross-device documentation references. See `docs/AI_GITHUB_WORKFLOW.md`. @@ -87,79 +85,74 @@ See `docs/AI_GITHUB_WORKFLOW.md`. - [x] Add isolated Playwright tooling outside the public runtime. - [x] Pin Playwright package and lockfile versions. -- [x] Start the exact branch checkout through a local HTTP server in GitHub Actions. - [x] Capture desktop and mobile screenshots from real Chromium. -- [x] Define reusable JSON scenarios for page startup, search ranking and Russian alias behavior. -- [x] Allow only explicit safe actions instead of arbitrary scenario JavaScript. -- [x] Require visible assertions before every capture. -- [x] Upload screenshots, per-scenario provenance, manifest and test results as workflow artifacts. -- [x] Keep the permanent workflow read-only and prohibit automatic commits. -- [x] Preserve successful manifest entries across Playwright retries. -- [x] Validate package/lock, workflow permissions, scenario coverage, artifact cleanup and runtime isolation. -- [x] Complete a four-of-four successful Chromium run. -- [x] Visually inspect all generated PNGs before selecting the uNews image. -- [x] Document local and GitHub Actions invocation. +- [x] Define reusable JSON scenarios and visible assertions. +- [x] Upload screenshots and provenance as workflow artifacts. +- [x] Keep the permanent workflow read-only. See `docs/SCREENSHOT_AUTOMATION.md` and `tools/screenshots/README.md`. ## Completed: D1.1 data provenance v23.8.8 -- [x] Recover the retained Git history of `data/divinity_code_ru.json`, `data/bd2.json`, `data/db.json` and `data/report.txt`. -- [x] Record exact bytes, raw SHA-256, canonical JSON SHA-256, schema and ordered ID coverage. -- [x] Separate proven facts, reasonable inferences and unknowns in `docs/DATA_PROVENANCE.md`. -- [x] Correct the earlier byte-duplicate claim for `bd2.json` and `db.json`. -- [x] Prove that `bd2.json` and `db.json` are byte-different but parsed/canonical-JSON equal. -- [x] Record that the active dataset preserves IDs and core source fields while changing `aliases`, `notes` and `tags`. -- [x] Invalidate the earlier intermediate metric that inspected nonexistent field `note` instead of `notes`. -- [x] Add `scripts/validate-data-provenance.mjs` and integrate it into project validation. -- [x] Keep all current data files, runtime, PWA, saved versions and archives unchanged. +- [x] Recover retained Git history of maintained data files. +- [x] Record bytes, raw/canonical hashes, schema and ordered IDs. +- [x] Separate proven facts, inferences and unknowns. +- [x] Prove that `bd2.json` and `db.json` are byte-different but canonical-JSON equal. +- [x] Add permanent provenance validation. +- [x] Keep data, runtime, PWA, saved versions and archives unchanged. See `docs/DATA_PROVENANCE.md`. ## Completed: D1.2 dataset registry v23.8.9 -- [x] Add machine-readable `data/datasets.json` with schema version 1. -- [x] Define stable logical dataset IDs `source-divinity-code-en` and `ru-current-v1`. -- [x] Define stable physical file IDs for both English serializations and the active runtime file. -- [x] Select `data/bd2.json` as the canonical maintained physical serialization through a project-governance decision. -- [x] State explicitly that the canonical choice does not prove the historical original or authoritative source edition. -- [x] Retain `data/db.json` as an equivalent compatibility serialization. -- [x] Record bytes, raw hashes, canonical hashes, record counts, roles, statuses and source relationships. -- [x] Add `docs/DATASET_REGISTRY.md` with identity, reference-audit, migration and rollback rules. -- [x] Add `scripts/validate-dataset-registry.mjs` and require it in GitHub Actions. -- [x] Preserve all existing data files, runtime, PWA, package metadata, saved versions and archives unchanged. -- [x] Keep the physical migration in status `planned-not-executed`. +- [x] Add machine-readable `data/datasets.json`. +- [x] Define stable logical and physical dataset IDs. +- [x] Select `data/bd2.json` as canonical maintained serialization by governance decision. +- [x] Retain `data/db.json` as equivalent compatibility serialization. +- [x] Add registry documentation and validator. +- [x] Keep physical migration `planned-not-executed`. See `data/datasets.json` and `docs/DATASET_REGISTRY.md`. ## Completed: D1.3 data-quality audit v23.8.10 -- [x] Define a four-level severity model: error, warning, review and info. -- [x] Add structural checks for arrays, required fields, types, dates, unique/ordered IDs and registered record counts. -- [x] Add data-hygiene and ambiguity checks for whitespace, empty values, duplicate aliases/tags, control characters, HTML-like content and normalized collisions. -- [x] Compare `source-divinity-code-en` with `ru-current-v1` without changing either dataset. -- [x] Confirm 4,086 unique ordered IDs and exact source/current alignment. -- [x] Confirm zero differences in preserved fields `id`, `symbol`, `description`, `source` and `date_added`. -- [x] Record expected changed-field counts: aliases 4,083; notes 4,086; tags 4,086. -- [x] Generate deterministic machine-readable and human-readable reports. -- [x] Add permanent `scripts/audit-data-quality.mjs` with write and `--check` modes. -- [x] Require report freshness and a passing structural gate in GitHub Actions. -- [x] Record 0 structural errors, 0 warnings and 5,022 human-review instances in five aggregated groups. -- [x] State explicitly that heuristic review counts are not proven content errors and are not auto-fixed. -- [x] Preserve all existing data files, runtime, PWA, package metadata, saved versions and archives unchanged. +- [x] Define error, warning, review and info severities. +- [x] Add deterministic structural and hygiene checks. +- [x] Confirm 4,086 unique ordered and aligned IDs. +- [x] Confirm zero differences in preserved fields. +- [x] Generate deterministic machine and human reports. +- [x] Record 0 structural errors, 0 warnings and 5,022 overlapping human-review instances. +- [x] Keep the audit read-only and preserve all runtime/data files. See `docs/DATA_QUALITY_AUDIT.md` and `reports/data-quality-audit.md`. -## Next approved work: D1.4 — two-book product architecture +## Completed: D1.4 two-book product architecture v23.8.11 -- [ ] Identify and register the second source book/dataset as far as retained evidence allows. -- [ ] Define the intended relationship between the two source books. -- [ ] Compare separate-database switching, combined search and side-by-side comparison. -- [ ] Define user-visible provenance for every result in every future mode. -- [ ] Define validation, reload, cache-clearing and automatic fallback contracts before any selector implementation. -- [ ] Decide how history, sharing and deep links preserve dataset identity. -- [ ] Approve a migration and rollback plan before implementing a user-facing selector or merged index. -- [ ] Keep D1.4 architecture-only unless a separate functional implementation phase is approved. +- [x] Register stable source-work and retained-document identities as architecture metadata. +- [x] Keep the second PDF classified as retained evidence, not a ready dataset. +- [x] Define global record identity as `(dataset_id, record_id)`. +- [x] Compare separate switching, federated combined search and side-by-side comparison. +- [x] Recommend staged implementation: separate mode first, combined mode second, reviewed comparison third. +- [x] Require visible source-work, dataset, translation/variant and source-reference provenance. +- [x] Define dataset-aware URL, history, sharing and legacy-link migration contracts. +- [x] Define validation, atomic activation, cache isolation and automatic fallback to `ru-current-v1`. +- [x] Define an explicit reviewed relation-map contract without numeric-ID assumptions. +- [x] Define migration and rollback before any functional implementation. +- [x] Add machine-readable `docs/two-book-architecture.json` and permanent validator. +- [x] Preserve existing data files, runtime, PWA, package metadata, saved versions and archive unchanged. + +See `docs/TWO_BOOK_ARCHITECTURE.md`. + +## Next approved work: second-book evidence and dataset preparation + +Before any selector or combined-search implementation: + +- [ ] archive exact source/edition evidence and known distribution statements for the second retained work; +- [ ] define a separate extraction task and immutable raw-output policy; +- [ ] create a new logical dataset ID only after actual extraction exists; +- [ ] validate schema, required fields, local IDs, record-count policy, hashes and source references; +- [ ] register the second dataset without replacing `ru-current-v1`; +- [ ] approve a separate functional phase for dataset switching only after data readiness. ## D1.5 — AI-assisted translation experiment @@ -172,24 +165,19 @@ See `docs/DATA_QUALITY_AUDIT.md` and `reports/data-quality-audit.md`. See `docs/TRANSLATION_WORKFLOW.md`. -## Rights and provenance completion - -- [ ] Archive the original source URLs, access dates and exact open-distribution statements for bundled PDFs. -- [ ] Review the retained PDF set against `docs/CONTENT_AND_RIGHTS.md` and `THIRD_PARTY_NOTICES.md`. - ## Product completion after D1 - [ ] Restore or redesign the administration workflow outside the public runtime. -- [ ] Add broader automated browser smoke checks for asset paths, startup and critical flows beyond the current screenshot scenarios. -- [ ] Review accessibility: keyboard navigation, contrast, focus, screen readers and text selection. +- [ ] Add broader automated browser smoke checks. +- [ ] Review accessibility. - [ ] Review privacy, local history and sharing behavior. - [x] Confirm automatic uDream publications and operational recovery in `@uNewsLog`. ## Later options -- [ ] Consider moving inline CSS from `index.html` only if it materially improves maintainability. -- [ ] Consider a build pipeline only when static-file maintenance becomes a real blocker. -- [ ] Consider additional languages only after the source and translation workflow is documented. +- [ ] Consider moving inline CSS only if maintainability requires it. +- [ ] Consider a build pipeline only when static-file maintenance becomes a blocker. +- [ ] Consider additional languages only after source and translation workflow is documented. - [ ] Consider TypeScript only when JSDoc and `@ts-check` no longer provide sufficient protection. -The roadmap is intentionally conservative: preserve the working static site, current data and restoration points while advancing one clearly bounded series at a time. +The roadmap remains conservative: preserve the working static site, current data and restoration points while advancing one bounded series at a time. diff --git a/VERSION.md b/VERSION.md index f32bf74..5393c02 100644 --- a/VERSION.md +++ b/VERSION.md @@ -8,61 +8,43 @@ - Published: 2026-07-20 - Purpose: preserve the verified PWA update, cache migration and installation flow release - Release: https://github.com/sunpole/udream/releases/tag/v23.8.0 -- Verification: the immutable tag resolves to the exact functional merge commit; the release workflow re-runs tests, project validation, syntax checks and version consistency before publication - Runtime version sources: `package.json`, `src/version.js` and `version.json` all contain `23.8.0` -- Database: the active 4,086-record JSON database is unchanged +- Active database: 4,086 records, unchanged - Previous checkpoint: `v23.7.0` -## Documentation, provenance, registry and audit baseline - -`v23.8.10` - -- Status: D1.3 non-destructive data-quality audit milestone; it does not create a new application build or move the `v23.8.0` tag -- Purpose: define deterministic audit rules, inspect all registered source/current records and preserve the results without modifying data -- Audit specification: `docs/DATA_QUALITY_AUDIT.md` -- Audit script: `scripts/audit-data-quality.mjs` -- Machine report: `reports/data-quality-audit.json` -- Human report: `reports/data-quality-audit.md` -- CI command: `node scripts/audit-data-quality.mjs --check` -- Logical datasets audited: `source-divinity-code-en`, `ru-current-v1` -- Records audited: 4,086 in each logical dataset -- ID result: unique, ordered and aligned from 1 through 4,086 -- Preserved-field differences: 0 for `id`, `symbol`, `description`, `source` and `date_added` -- Expected changed-field differences: aliases 4,083; notes 4,086; tags 4,086 -- Structural gate: pass -- Structural error instances: 0 -- Warning instances: 0 -- Human-review instances: 5,022 in five aggregated finding groups -- Important limitation: review counts are heuristic candidates, not 5,022 proven content errors; one record may appear in multiple groups -- Data mutation policy: read-only; no finding is corrected by the audit -- Registry baseline: `v23.8.9`, D1.2 completed by PR #27 -- Source logical dataset ID: `source-divinity-code-en` -- Current localized dataset ID: `ru-current-v1` -- Canonical physical serialization: `source-divinity-code-en-bd2` at `data/bd2.json` -- Retained compatibility serialization: `source-divinity-code-en-db` at `data/db.json` -- Active runtime physical file: `ru-current-v1-runtime` at `data/divinity_code_ru.json` -- Migration status: `planned-not-executed`; no file removal, rename or runtime switch is approved -- Active dataset SHA-256: `1def80216e238b0c2a8640aaf1b4e95dd0669d5944a67f4e7c4421fad55a6e64` -- Shared canonical English JSON SHA-256: `5ebe0d973f9cfd1c9db65a9d5abebe0ca16788261219299a710ed9fe78bb25d1` +## Documentation, data and architecture baseline + +`v23.8.11` + +- Status: D1.4 two-book architecture milestone; no new application build and no movement of the `v23.8.0` tag +- Purpose: define safe multi-book identity, provenance, modes, routing, validation, caching, fallback and rollback before implementation +- Human architecture: `docs/TWO_BOOK_ARCHITECTURE.md` +- Machine-readable architecture: `docs/two-book-architecture.json` +- Validator: `scripts/validate-two-book-architecture.mjs` +- CI command: `node scripts/validate-two-book-architecture.mjs` +- Current default dataset: `ru-current-v1` +- Current source dataset: `source-divinity-code-en` +- Second source work status: retained PDF evidence only; no registered second-book dataset exists +- Global record identity: `(dataset_id, record_id)` +- Recommended order: separate mode, then federated combined search, then reviewed side-by-side comparison +- Combined search rule: independent indexes and provenance-preserving result federation; no destructive JSON merge +- Comparison rule: explicit reviewed relation map; no numeric-ID equivalence across books +- Stable fallback: `ru-current-v1` +- Runtime/data policy: existing data files, runtime, PWA, package metadata, saved versions and archive remain unchanged +- Previous baselines: D1.1 `23.8.8`, D1.2 `23.8.9`, D1.3 `23.8.10` - Live handoff source: `WORK_STATUS.md` -- Next approved task after D1.3 merge: D1.4 two-book product architecture - Repository download: https://github.com/sunpole/udream/archive/refs/tags/v23.8.0.zip ## Current development line No functional application version is assigned after `v23.8.0`. -Documentation, provenance and automation updates `23.8.1` through `23.8.10` finalize the immutable release record, product/data baseline, translation safety, cross-device workflow, screenshot provenance, verified data provenance, dataset-registry governance and deterministic data-quality reporting. They do not change application runtime, PWA behavior or existing database records. +Documentation, provenance, registry, audit and architecture updates `23.8.1` through `23.8.11` do not change application runtime, PWA behavior or existing database records. + +The next implementation phase must be approved separately. A second-book dataset must first be extracted, validated, registered and reviewed before selector, combined search or comparison UI work begins. ## Unified version line Version `v23.7.0` began the unified maintained-product line; `v23.8.0` is its next feature release. -The repository previously used two separate counters: - -- Git releases: `v1.0.0`, `v2.0.0`, `v3.0.0`, `v3.5.0`, `v3.6.0`; -- legacy UI iterations: numbered folders through `019` and the visible interface label `v19`. - -The product-version decision advanced the legacy visible line from `19` to `20` and combined it with the `3.7.0` development line, producing the unified version `23.7.0`. New maintained releases continue from that unified semantic version. - -Historical tags, releases, archived folders and runnable snapshots keep their original numbers. +Historical tags, releases, archived folders and runnable snapshots keep their original numbers. Existing release tags are immutable. diff --git a/WORK_STATUS.md b/WORK_STATUS.md index 7485f97..b2c5aa1 100644 --- a/WORK_STATUS.md +++ b/WORK_STATUS.md @@ -2,75 +2,83 @@ Этот файл — единая оперативная точка продолжения разработки uDream с телефона, Windows, macOS, другого устройства или любого ИИ-чата. -`ROADMAP.md` хранит общий план, `docs/PROJECT_STATE.md` — проверенное состояние продукта, а этот файл хранит живую текущую задачу и точный следующий шаг. - ## Быстрый сигнал | Поле | Текущее значение | |---|---| -| Состояние | **READY** — незавершённой активной задачи нет | -| Рабочая ветка | `main` | -| Открытый Pull Request | нет | +| Состояние | **COMPLETED** — D1.4 реализован и повторно сверен с GitHub; PR №31 ожидает зелёных проверок и merge | +| Рабочая ветка | `docs/two-book-architecture-d1.4` | +| Открытый Pull Request | `#31` — `https://github.com/sunpole/udream/pull/31` | | Стабильный функциональный релиз | `v23.8.0` | -| Документационный/data baseline | `23.8.10` — D1.3 data-quality audit завершён | -| Последнее завершённое изменение | PR #29, squash merge `d4c100c8f3430bdfe57d294f3c81217efb5a4186` | -| Закрытая задача | Issue #28 — D1.3 completed | -| Следующая утверждённая задача | D1.4 — two-book product architecture | - -## Завершённый этап D1.3 - -- создан permanent read-only `scripts/audit-data-quality.mjs`; -- созданы deterministic reports `reports/data-quality-audit.json` и `reports/data-quality-audit.md`; -- GitHub Actions проверяет report freshness и structural gate; -- проверены logical datasets `source-divinity-code-en` и `ru-current-v1`; -- подтверждены 4 086 unique ordered IDs `1–4086` в каждом наборе; -- source/current IDs полностью aligned; -- preserved fields `id`, `symbol`, `description`, `source`, `date_added` имеют 0 differences; -- expected changed fields: aliases 4 083, notes 4 086, tags 4 086; -- structural gate: PASS; -- structural errors: 0; -- warnings: 0; -- human-review instances: 5 022 в пяти aggregated groups; -- 5 022 — пересекающиеся кандидаты на source/human review, а не доказанные ошибки; -- factual uNews patchnote `23.8.10` и новое real Chromium report image сохранены; -- существующие data files, runtime, PWA, package metadata, `versions/` и `_archive/` не изменены. - -## Текущие зарегистрированные данные - -```text -source-divinity-code-en - canonical physical: data/bd2.json - retained equivalent: data/db.json - -ru-current-v1 - active runtime: data/divinity_code_ru.json - -physical migration: planned-not-executed -remove_or_rename_approved: false -``` +| Документационный/architecture baseline | кандидат `23.8.11` | +| Актуальный `main` при старте | `d6c7a070ecd6aec19b7841644cfdb80ac9f82de1` | +| Активная задача | Issue #30 — D1.4 two-book product architecture | +| Последний проверенный branch head до этого checkpoint | `24bf2f828368ecd7a07a67e94699d7bd879c6c37` | +| Следующая точная задача | дождаться `Validate uDream` на новом head, проверить итоговый diff и объединить PR №31 squash merge | + +## Завершено фактически + +- создан `docs/TWO_BOOK_ARCHITECTURE.md`; +- создан machine-readable `docs/two-book-architecture.json`; +- добавлен permanent `scripts/validate-two-book-architecture.mjs`; +- validator включён в GitHub Actions и syntax checks; +- текущий default зафиксирован как `ru-current-v1`; +- второй PDF зафиксирован как retained evidence, но не готовый dataset; +- global identity определена как `(dataset_id, record_id)`; +- separate switching выбран первым будущим functional mode; +- combined search определён как federated по отдельным indexes без destructive JSON merge; +- side-by-side comparison требует explicit reviewed relation map; +- определены visible provenance, dataset-aware URLs/history/sharing, atomic activation, cache isolation, fallback и rollback; +- обновлены `ROADMAP.md` и `VERSION.md`; +- создан factual patchnote `23.8.11` и новый real PNG exact architecture evidence; +- существующие data files, runtime, PWA, package metadata, `versions/` и `_archive/` не изменены; +- 2026-07-24 повторно сверены `main`, PR №31, Issue №30, changed files, architecture JSON и permanent validator; +- отсутствие зарегистрированного Actions run на предыдущем head зафиксировано; этот checkpoint-коммит предназначен для повторного запуска CI. + +## Планируемые и фактические файлы + +Фактически изменены или добавлены: + +- `.github/workflows/validate.yml`; +- `ROADMAP.md`; +- `VERSION.md`; +- `WORK_STATUS.md`; +- `docs/TWO_BOOK_ARCHITECTURE.md`; +- `docs/two-book-architecture.json`; +- `scripts/validate-two-book-architecture.mjs`; +- `news/2026-07-23-udream-v23-8-11-two-book-architecture.md`; +- `news/2026-07-23-udream-v23-8-11-two-book-architecture.png`. + +## Критерии завершения перед merge + +- GitHub Actions полностью зелёные; +- architecture validator проходит; +- patchnote/new-image validation проходит; +- diff не содержит `data/`, runtime, PWA, package metadata, `versions/` или `_archive/`; +- PR №31 объединён squash merge; +- Issue #30 закрыт completed; +- `main/WORK_STATUS.md` возвращён в `READY`; +- следующая approved phase названа явно. + +## Последний проверенный commit + +До повторного CI-checkpoint фактический head PR №31 был `24bf2f828368ecd7a07a67e94699d7bd879c6c37`. Новый head определяется GitHub после этого commit и должен совпасть с head, проверенным Actions перед merge. ## Следующий точный шаг -Начать D1.4 только в новой отдельной ветке после создания/чтения Issue и обновления этого файла до `IN_PROGRESS`. - -D1.4 должна быть architecture-only и определить: - -- identity и provenance второй исходной книги; -- separate switching, combined search и side-by-side modes; -- visible source/dataset/translation provenance; -- dataset-aware history, sharing и deep links; -- validation, reload, cache-clearing и stable fallback contracts; -- migration и rollback до любой user-facing реализации. +Дождаться `Validate uDream` на новом head PR №31, проверить полный changed-files список и protected paths. Не объединять при любой красной или незавершённой проверке. ## Главные запреты -- не менять и не удалять существующие data files; -- не исправлять audit findings без отдельного evidence-based data PR; -- не выполнять physical migration; -- не реализовывать selector в architecture-only D1.4; -- не начинать D1.5 AI-assisted translation до завершения D1.4; -- не выдавать heuristic findings или неизвестный translation pipeline за доказанный факт. +- не менять существующие data files; +- не извлекать и не генерировать вторую базу; +- не менять runtime, PWA, Service Worker, package metadata, `versions/` или `_archive/`; +- не добавлять user-facing selector; +- не считать наличие PDF готовым dataset; +- не назначать одинаковые IDs между книгами без evidence; +- не начинать DeepSeek translation experiment; +- не добавлять API keys. ## Источник истины -Реальные GitHub-факты — `main`, открытые Pull Request, commits, Actions и Issues — имеют приоритет над памятью ИИ, старыми чатами и локальными незапушенными изменениями. +Реальные GitHub-факты — `main`, текущая ветка, commits, Pull Request, Actions и Issues — имеют приоритет над памятью ИИ, старыми чатами и локальными незапушенными изменениями. diff --git a/docs/TWO_BOOK_ARCHITECTURE.md b/docs/TWO_BOOK_ARCHITECTURE.md new file mode 100644 index 0000000..8d1b4cb --- /dev/null +++ b/docs/TWO_BOOK_ARCHITECTURE.md @@ -0,0 +1,373 @@ +# Two-book product architecture + +## Scope + +This document defines the D1.4 architecture for working with two source books in uDream. It is architecture-only. + +It does not: + +- create or extract a second dataset; +- change the current 4,086 records; +- change the runtime, PWA, Service Worker or package metadata; +- add a user-facing dataset selector; +- begin AI-assisted translation. + +The machine-readable companion is `docs/two-book-architecture.json`. + +## Verified current facts + +### Registered data + +The current registry contains: + +```text +source-divinity-code-en + canonical physical: data/bd2.json + retained equivalent: data/db.json + +ru-current-v1 + active runtime: data/divinity_code_ru.json +``` + +`ru-current-v1` remains the stable default dataset until a separate functional release explicitly changes the runtime contract. + +### Retained source documents + +The repository retains two PDF documents: + +1. `_archive/source-files/The_Divinity_Code_to_Understanding_Your_Dreams_and_Visions_PDF_Room.pdf`; +2. `_archive/source-files/Unlocking-Your-Dream-Student-Ma.pdf`. + +The first work is connected to the registered Divinity Code datasets. The second document is evidence that source material is retained, but there is no verified extracted logical dataset for it. + +Presence of a PDF is not evidence that a complete, validated or publishable dataset exists. + +### Unknown or incomplete evidence + +The repository does not currently prove: + +- the exact source edition for either retained PDF; +- the original download URLs; +- archived distribution permission wording; +- a complete extraction pipeline for the second book; +- stable second-book record IDs; +- reviewed cross-book record relationships. + +These unknowns must remain visible and must not be replaced by assumptions. + +## Stable source-work and document identities + +### Source works + +```text +work-divinity-code +work-unlocking-your-dreams +``` + +### Retained documents + +```text +document-divinity-code-pdf +document-unlocking-your-dreams-student-material-pdf +``` + +The document IDs identify retained files. They do not by themselves create logical datasets. + +### Dataset identities + +Current: + +```text +source-divinity-code-en +ru-current-v1 +``` + +Reserved future identity: + +```text +future-unlocking-your-dreams-source +``` + +The reserved ID is an architectural placeholder. It must not be treated as an existing dataset, loaded by runtime code or listed to users before extraction, validation and registration are complete. + +## Global record identity + +Local numeric record IDs are scoped to one logical dataset. + +The global identity tuple is: + +```text +(dataset_id, record_id) +``` + +Examples: + +```text +(ru-current-v1, 125) +(future-unlocking-your-dreams-source, 125) +``` + +These two examples are different records even when the numeric part matches. + +Cross-book identity must never be inferred from numeric equality. + +## Product modes + +### 1. Separate dataset switching + +Users choose one registered dataset and search only within it. + +Advantages: + +- simplest mental model; +- clearest provenance; +- lowest ranking ambiguity; +- easiest validation and fallback; +- safest first functional implementation. + +Risks: + +- users may need to repeat a search in the other dataset; +- current history and links must become dataset-aware. + +**Decision:** implement this first, but only after the second dataset is extracted, validated, registered and approved in a separate functional release. + +### 2. Federated combined search + +The application queries independent indexes and combines the returned results at presentation time. + +Required rule: + +```text +separate datasets + separate indexes + federated ranking +``` + +Forbidden approach: + +```text +destructively merge both books into one source JSON +``` + +Each combined result must retain its dataset and source-work identity. Ranking must define how exact matches, aliases and distant matches from different datasets are interleaved. + +**Decision:** add only after separate mode is stable and ranking rules have dedicated tests. + +### 3. Side-by-side comparison + +The application displays explicitly related records from two datasets. + +Comparison must use a reviewed relation map. It cannot pair records merely because they share a numeric ID or similar text. + +**Decision:** add only after a relation-map format exists and reviewed relationships are available. + +## Recommended staged architecture + +### Stage A — source and rights evidence + +- identify exact works and editions as far as evidence permits; +- archive known source URLs and distribution statements; +- keep unknowns explicit. + +### Stage B — second dataset creation + +- extract to a new physical file; +- assign a new logical dataset ID; +- preserve raw extraction separately from normalized output; +- validate schema, required fields, local IDs and source references; +- create hashes, provenance and rollback; +- do not modify `ru-current-v1`. + +### Stage C — separate mode + +- add a dataset-aware loader; +- expose only registered and validated datasets; +- keep `ru-current-v1` as automatic fallback; +- make routes, history and sharing dataset-aware; +- publish as a functional release. + +### Stage D — federated combined mode + +- maintain separate indexes; +- query each enabled dataset independently; +- combine ranked result descriptors, not source records; +- show provenance in every result; +- test ranking and fallback independently. + +### Stage E — comparison mode + +- create a reviewed relation map; +- expose explicit comparison links; +- show relation type, evidence and review status; +- never infer accepted relations from numeric IDs. + +## Visible provenance contract + +Every result and record card in future multi-dataset modes must show or make directly accessible: + +- source work; +- dataset ID or understandable dataset label; +- translation/editorial variant; +- source reference when known; +- an explicit unknown state when provenance is incomplete. + +Combined results must never hide which dataset produced a match. + +## URL, history and sharing contract + +### Record links + +Future record links must preserve: + +```text +dataset= +record= +``` + +Conceptual example: + +```text +?dataset=ru-current-v1&record=125 +``` + +### Search links + +Search state should preserve: + +```text +mode= +dataset= +q= +``` + +### Comparison links + +Comparison links should use: + +```text +mode=comparison +relation= +``` + +They should not encode an unreviewed assumption by pairing two numbers directly. + +### Legacy links + +Links created before dataset-aware routing fall back to `ru-current-v1`. + +### Browser history and local history + +Each history entry must store the dataset ID with the local record ID. Existing legacy entries without dataset identity must be interpreted as `ru-current-v1` during migration. + +### Sharing + +Shared text and URLs must preserve dataset identity and visible source-work provenance. + +## Loading and activation contract + +A dataset can become selectable only when all required checks pass: + +- registered logical dataset entry; +- registered physical file entry; +- schema validation; +- required-field validation; +- unique local IDs; +- declared record-count policy; +- declared raw and canonical hashes; +- source-work/document relationship; +- approved status for runtime activation. + +Activation must be atomic. Partial loading must not replace the current working dataset. + +## Cache and Service Worker contract + +Future dataset cache keys must include dataset ID and dataset version/hash. + +Rules: + +- new dataset failure cannot corrupt the stable current cache; +- only uDream dataset-scoped caches may be cleaned; +- unrelated caches on the same origin must remain untouched; +- a functional release is required before changing runtime cache behavior; +- rollback must be able to disable new modes and restore `ru-current-v1` without clearing user preferences. + +D1.4 itself makes no Service Worker changes. + +## Stable fallback contract + +The stable fallback remains: + +```text +ru-current-v1 +``` + +Automatic fallback is required on: + +- load failure; +- schema or registry validation failure; +- hash mismatch; +- partial cache state; +- activation failure. + +Fallback should preserve user preferences. The application should record a non-secret recovery reason for diagnostics and show a clear user message rather than silently presenting mixed data. + +## Relation-map contract + +A future reviewed relation entry requires: + +```text +relation_id +left_dataset_id +left_record_id +right_dataset_id +right_record_id +relation_type +evidence +review_status +``` + +Initial relation types: + +- `same-symbol`; +- `related-symbol`; +- `contrast`; +- `source-cross-reference`. + +Human review is required before a relation is presented as established. Automated similarity may propose candidates but cannot mark them accepted. + +## Migration and rollback + +Before the first functional multi-dataset release: + +1. create an immutable checkpoint; +2. preserve the current default and all current data hashes; +3. add the new dataset without replacing current files; +4. add dataset-aware routing/history migration; +5. validate loading, offline behavior and fallback; +6. publish a separate functional patchnote; +7. verify the public site and installed PWA. + +Rollback must: + +1. restore `ru-current-v1` as the only enabled dataset; +2. disable new mode flags; +3. remove only newly introduced dataset-scoped caches; +4. preserve history and preferences; +5. interpret dataset-aware historical entries safely; +6. re-run runtime, data and PWA validation. + +## Explicit non-decisions + +D1.4 does not decide: + +- the final filename or record count of the second dataset; +- the exact second-book extraction tool; +- whether a second Russian translation will use DeepSeek; +- final cross-book ranking weights; +- final UI layout; +- any content correction from D1.3 review candidates. + +Those require separate evidence and separate phases. + +## Completion boundary + +D1.4 is complete when the architecture, machine-readable contract and validator are merged while existing data, runtime, PWA, package metadata, saved versions and archive remain unchanged. diff --git a/docs/two-book-architecture.json b/docs/two-book-architecture.json new file mode 100644 index 0000000..569e64f --- /dev/null +++ b/docs/two-book-architecture.json @@ -0,0 +1,240 @@ +{ + "schema_version": 1, + "architecture_id": "udream-two-book-architecture", + "baseline": "23.8.11", + "functional_release": "v23.8.0", + "status": "architecture-only", + "default_dataset_id": "ru-current-v1", + "source_works": [ + { + "id": "work-divinity-code", + "title": "The Divinity Code to Understanding Your Dreams and Visions", + "evidence_status": "registered-dataset-and-retained-document", + "registered_dataset_ids": [ + "source-divinity-code-en", + "ru-current-v1" + ], + "document_ids": [ + "document-divinity-code-pdf" + ] + }, + { + "id": "work-unlocking-your-dreams", + "title": "Unlocking Your Dreams", + "evidence_status": "retained-document-only-dataset-not-created", + "registered_dataset_ids": [], + "document_ids": [ + "document-unlocking-your-dreams-student-material-pdf" + ] + } + ], + "documents": [ + { + "id": "document-divinity-code-pdf", + "source_work_id": "work-divinity-code", + "path": "_archive/source-files/The_Divinity_Code_to_Understanding_Your_Dreams_and_Visions_PDF_Room.pdf", + "status": "retained-evidence", + "dataset_status": "registered-datasets-exist", + "source_url_status": "not-recorded", + "distribution_evidence_status": "not-archived" + }, + { + "id": "document-unlocking-your-dreams-student-material-pdf", + "source_work_id": "work-unlocking-your-dreams", + "path": "_archive/source-files/Unlocking-Your-Dream-Student-Ma.pdf", + "status": "retained-evidence", + "dataset_status": "dataset-not-created", + "source_url_status": "not-recorded", + "distribution_evidence_status": "not-archived" + } + ], + "dataset_contracts": [ + { + "dataset_id": "ru-current-v1", + "source_work_id": "work-divinity-code", + "status": "runtime-current", + "activation": "active-default", + "identity": "dataset-id-plus-local-record-id" + }, + { + "dataset_id": "future-unlocking-your-dreams-source", + "source_work_id": "work-unlocking-your-dreams", + "status": "reserved-id-not-created", + "activation": "forbidden-until-extracted-validated-and-registered", + "identity": "dataset-id-plus-local-record-id" + } + ], + "identity_contract": { + "global_record_key": [ + "dataset_id", + "record_id" + ], + "numeric_ids_are_local_to_dataset": true, + "cross_dataset_numeric_id_equivalence": false, + "stable_relation_identity": "relation_id" + }, + "modes": [ + { + "id": "separate", + "label": "separate dataset switching", + "status": "recommended-first-functional-mode", + "requires": [ + "registered-and-validated-second-dataset", + "dataset-aware-loader", + "dataset-aware-history-and-links", + "stable-fallback" + ] + }, + { + "id": "combined", + "label": "federated combined search", + "status": "recommended-after-separate-mode", + "implementation_rule": "query-separate-indexes-and-merge-ranked-results-with-provenance", + "destructive_json_merge": false + }, + { + "id": "comparison", + "label": "side-by-side comparison", + "status": "recommended-after-reviewed-relation-map", + "implementation_rule": "compare-only-explicit-reviewed-relations", + "numeric_id_matching": false + } + ], + "visible_provenance": { + "required_on_results_and_cards": [ + "source_work_id", + "dataset_id", + "translation_or_variant_label", + "source_reference_when_known" + ], + "unknown_values_must_be_visible": true + }, + "routing_contract": { + "record_route_fields": [ + "dataset", + "record" + ], + "search_route_fields": [ + "mode", + "dataset", + "q" + ], + "comparison_route_fields": [ + "mode", + "relation" + ], + "history_must_store_dataset_identity": true, + "sharing_must_preserve_dataset_identity": true, + "legacy_links_without_dataset_fallback_to": "ru-current-v1" + }, + "loading_contract": { + "validate_before_activation": true, + "required_validation": [ + "registry-entry", + "schema", + "record-count-policy", + "unique-local-ids", + "required-fields", + "declared-hashes", + "source-work-link" + ], + "activation_is_atomic": true, + "partial_activation_forbidden": true + }, + "cache_contract": { + "cache_keys_must_include_dataset_id_and_version": true, + "unrelated_origin_caches_must_not_be_deleted": true, + "dataset_failure_must_not_corrupt_current_cache": true, + "new_dataset_activation_requires_functional_release": true + }, + "fallback_contract": { + "stable_dataset_id": "ru-current-v1", + "automatic_on": [ + "load-failure", + "validation-failure", + "hash-mismatch", + "partial-cache-state" + ], + "preserve_user_preferences": true, + "record_recovery_reason": true + }, + "relation_map_contract": { + "required_fields": [ + "relation_id", + "left_dataset_id", + "left_record_id", + "right_dataset_id", + "right_record_id", + "relation_type", + "evidence", + "review_status" + ], + "allowed_relation_types": [ + "same-symbol", + "related-symbol", + "contrast", + "source-cross-reference" + ], + "auto_accept_by_numeric_id": false, + "human_review_required": true + }, + "stages": [ + { + "id": "D1.4", + "status": "architecture-only", + "changes_runtime": false, + "changes_data": false + }, + { + "id": "future-second-dataset-extraction", + "status": "not-started", + "requires": [ + "source-and-rights-evidence-review", + "separate-dataset-id", + "extraction-validation", + "rollback-plan" + ] + }, + { + "id": "future-separate-mode", + "status": "not-started", + "requires": [ + "registered-second-dataset", + "functional-release" + ] + }, + { + "id": "future-combined-mode", + "status": "not-started", + "requires": [ + "separate-mode-stable", + "federated-ranking-contract" + ] + }, + { + "id": "future-comparison-mode", + "status": "not-started", + "requires": [ + "reviewed-relation-map" + ] + } + ], + "rollback_contract": { + "precondition": "immutable-checkpoint-before-functional-activation", + "steps": [ + "restore-default-dataset-to-ru-current-v1", + "disable-new-mode-flags", + "remove-only-dataset-scoped-new-caches", + "preserve-history-and-preferences", + "re-run-runtime-data-and-pwa-validation" + ] + }, + "prohibitions": [ + "do-not-treat-retained-pdf-as-ready-dataset", + "do-not-assume-cross-book-id-equivalence", + "do-not-destructively-merge-source-json", + "do-not-change-runtime-in-d1.4", + "do-not-change-existing-data-files-in-d1.4", + "do-not-add-api-keys" + ] +} diff --git a/news/2026-07-23-udream-v23-8-11-two-book-architecture.md b/news/2026-07-23-udream-v23-8-11-two-book-architecture.md new file mode 100644 index 0000000..51a3f6b --- /dev/null +++ b/news/2026-07-23-udream-v23-8-11-two-book-architecture.md @@ -0,0 +1,36 @@ +--- +type: architecture +project: uDream +series: udream +title: Спроектирована безопасная архитектура двух книг +version: 23.8.11 +queued_at: 2026-07-23T08:15:00Z +repo_url: https://github.com/sunpole/udream +web_url: https://sunpole.github.io/udream/ +image: 2026-07-23-udream-v23-8-11-two-book-architecture.png +image_source: document-render +image_target: docs/TWO_BOOK_ARCHITECTURE.md +image_commit: b5c819de9e573f5d47de0a7aec19eb0bee050fea +image_captured_at: 2026-07-23T08:08:00Z +--- + +# uDream 23.8.11: архитектура двух книг определена до реализации + +Завершён architecture-only этап D1.4. Он определяет, как uDream сможет безопасно работать с двумя исходными книгами, не меняя текущий сайт и базу. + +- Текущий default остаётся `ru-current-v1`. +- Вторая сохранённая PDF-книга пока не считается готовым dataset. +- Глобальная идентичность записи определена как `(dataset_id, record_id)`. +- Первым будущим режимом рекомендовано отдельное переключение зарегистрированных баз. +- Combined search должен быть federated по отдельным индексам, а не через разрушительное объединение JSON. +- Side-by-side comparison разрешён только через явную проверенную relation map. +- Для результатов и карточек обязательны source work, dataset, translation/variant и известная source reference. +- URL, история и sharing должны сохранять dataset identity. +- Новая база активируется атомарно только после проверки, а при ошибке приложение возвращается к `ru-current-v1`. +- Cache cleanup не должен затрагивать другие проекты на том же origin. +- Созданы `docs/TWO_BOOK_ARCHITECTURE.md`, `docs/two-book-architecture.json` и постоянный validator. +- Существующие data files, runtime, PWA, package metadata, `versions/` и `_archive/` не изменены. + +Короткий текст для Telegram: + +uDream 23.8.11 завершает D1.4 — архитектуру двух книг до реализации. Текущая база ru-current-v1 остаётся стабильной, вторая PDF ещё не считается готовым dataset. Зафиксированы identity `(dataset_id, record_id)`, отдельный режим как первый этап, federated combined search без destructive merge, reviewed relation map для сравнения и автоматический fallback к стабильной базе. Runtime и 4 086 записей не менялись. diff --git a/news/2026-07-23-udream-v23-8-11-two-book-architecture.png b/news/2026-07-23-udream-v23-8-11-two-book-architecture.png new file mode 100644 index 0000000..9b967b4 Binary files /dev/null and b/news/2026-07-23-udream-v23-8-11-two-book-architecture.png differ diff --git a/scripts/validate-two-book-architecture.mjs b/scripts/validate-two-book-architecture.mjs new file mode 100644 index 0000000..bd59d23 --- /dev/null +++ b/scripts/validate-two-book-architecture.mjs @@ -0,0 +1,144 @@ +#!/usr/bin/env node + +import { access, readFile } from "node:fs/promises"; +import path from "node:path"; +import process from "node:process"; + +const ROOT = process.cwd(); +const ARCH_PATH = "docs/two-book-architecture.json"; +const DATASET_REGISTRY_PATH = "data/datasets.json"; +const DOC_PATH = "docs/TWO_BOOK_ARCHITECTURE.md"; + +function fail(message) { + throw new Error(message); +} + +async function readJson(relativePath) { + const source = await readFile(path.join(ROOT, relativePath), "utf8"); + try { + return JSON.parse(source); + } catch (error) { + fail(`${relativePath}: invalid JSON (${error.message})`); + } +} + +function requireUniqueIds(items, label) { + const ids = new Set(); + for (const item of items) { + if (!item || typeof item !== "object" || Array.isArray(item)) { + fail(`${label}: every entry must be an object`); + } + if (typeof item.id !== "string" || !item.id.trim()) { + fail(`${label}: every entry needs a non-empty id`); + } + if (ids.has(item.id)) fail(`${label}: duplicate id ${item.id}`); + ids.add(item.id); + } + return ids; +} + +async function main() { + const architecture = await readJson(ARCH_PATH); + const datasets = await readJson(DATASET_REGISTRY_PATH); + const document = await readFile(path.join(ROOT, DOC_PATH), "utf8"); + + if (architecture.schema_version !== 1) fail("architecture schema_version must be 1"); + if (architecture.status !== "architecture-only") fail("D1.4 status must remain architecture-only"); + if (architecture.functional_release !== "v23.8.0") fail("functional release changed unexpectedly"); + if (architecture.default_dataset_id !== "ru-current-v1") fail("stable default must remain ru-current-v1"); + + const workIds = requireUniqueIds(architecture.source_works, "source_works"); + const documentIds = requireUniqueIds(architecture.documents, "documents"); + const modeIds = requireUniqueIds(architecture.modes, "modes"); + const stageIds = requireUniqueIds(architecture.stages, "stages"); + + for (const required of ["work-divinity-code", "work-unlocking-your-dreams"]) { + if (!workIds.has(required)) fail(`missing source work ${required}`); + } + for (const required of ["document-divinity-code-pdf", "document-unlocking-your-dreams-student-material-pdf"]) { + if (!documentIds.has(required)) fail(`missing retained document ${required}`); + } + for (const required of ["separate", "combined", "comparison"]) { + if (!modeIds.has(required)) fail(`missing product mode ${required}`); + } + if (!stageIds.has("D1.4")) fail("missing D1.4 stage"); + + const registeredDatasetIds = new Set(datasets.logical_datasets.map((item) => item.id)); + if (!registeredDatasetIds.has("ru-current-v1")) fail("dataset registry lacks ru-current-v1"); + if (!registeredDatasetIds.has("source-divinity-code-en")) fail("dataset registry lacks source-divinity-code-en"); + + const currentContract = architecture.dataset_contracts.find((item) => item.dataset_id === "ru-current-v1"); + if (!currentContract || currentContract.activation !== "active-default") { + fail("ru-current-v1 must remain active-default"); + } + const futureContract = architecture.dataset_contracts.find( + (item) => item.dataset_id === "future-unlocking-your-dreams-source", + ); + if (!futureContract || futureContract.status !== "reserved-id-not-created") { + fail("future second-book dataset must remain reserved-id-not-created"); + } + + const identity = architecture.identity_contract?.global_record_key; + if (JSON.stringify(identity) !== JSON.stringify(["dataset_id", "record_id"])) { + fail("global identity must be dataset_id + record_id"); + } + if (architecture.identity_contract.cross_dataset_numeric_id_equivalence !== false) { + fail("cross-dataset numeric ID equivalence must be false"); + } + + const combined = architecture.modes.find((item) => item.id === "combined"); + if (!combined || combined.destructive_json_merge !== false) { + fail("combined mode must forbid destructive JSON merge"); + } + const comparison = architecture.modes.find((item) => item.id === "comparison"); + if (!comparison || comparison.numeric_id_matching !== false) { + fail("comparison mode must forbid numeric-ID matching"); + } + + if (architecture.fallback_contract?.stable_dataset_id !== "ru-current-v1") { + fail("fallback dataset must remain ru-current-v1"); + } + if (architecture.cache_contract?.unrelated_origin_caches_must_not_be_deleted !== true) { + fail("unrelated origin caches must be protected"); + } + if (architecture.relation_map_contract?.human_review_required !== true) { + fail("relation map must require human review"); + } + + for (const entry of architecture.documents) { + if (!workIds.has(entry.source_work_id)) fail(`document ${entry.id}: unknown source work`); + await access(path.join(ROOT, entry.path)); + } + + const prohibited = new Set(architecture.prohibitions || []); + for (const required of [ + "do-not-treat-retained-pdf-as-ready-dataset", + "do-not-assume-cross-book-id-equivalence", + "do-not-destructively-merge-source-json", + "do-not-change-runtime-in-d1.4", + "do-not-change-existing-data-files-in-d1.4", + "do-not-add-api-keys", + ]) { + if (!prohibited.has(required)) fail(`missing prohibition ${required}`); + } + + for (const requiredText of [ + "(dataset_id, record_id)", + "Federated combined search", + "Side-by-side comparison", + "ru-current-v1", + "Presence of a PDF is not evidence", + ]) { + if (!document.includes(requiredText)) fail(`${DOC_PATH}: missing ${requiredText}`); + } + + console.log( + `Two-book architecture passed: ${workIds.size} source works, ${documentIds.size} documents, ` + + `${modeIds.size} modes, stable fallback ${architecture.fallback_contract.stable_dataset_id}.`, + ); +} + +main().catch((error) => { + console.error(`Two-book architecture validation failed: ${error.message}`); + process.exit(1); +});