Adopt Rector as a require-dev tool alongside PHPStan/phpcs for safe, mechanical
refactoring:
- src/composer.json: add rector/rector ^2.0 (require-dev) + refactor/refactor:dry
composer scripts.
- build/rector.php: narrowly-scoped config over the PSR-4 class trees
(Core/Domain/Cli/Infrastructure). Skips the \TMDB lib, the streaming hot-path,
vendor, tmp/backups. Import-adding stays OFF (the check-procedural-use gate
relies on positional use imports). Behaviour-changing rules are disabled
(SafeDeclareStrictTypes, UseIdenticalOverEqualWithSameType); only the safe
deadCode + codeQuality prepared sets run (incl. the empty-if/else collapse).
- Makefile: `make rector` (dry-run, non-zero on pending changes) and
`make rector-fix` (apply). Both require dev-tools.
- docs/en/guides/refactoring.md + dev-workflow.md + mkdocs.yml nav: the
detect -> diff -> verify -> apply workflow.
No source code is changed by this commit — scaffolding only. `make rector-fix`
output will be reviewed separately.
Add a version selector to the docs and publish one snapshot per release instead
of a single rolling site, so readers can pick the docs matching their installed
version (the docs change release to release).
- mkdocs.yml: enable the Material version selector (extra.version.provider: mike,
alias: true).
- docs/requirements.txt: add mike==2.1.3.
- pages.yml: deploy with mike, triggered by a release TAG (semver) instead of
every push to main — publishing is tied to the release because docs/ru is only
regenerated then. Deploys `X.Y.Z` + the `latest` alias to the gh-pages branch
and sets latest as default. workflow_dispatch takes an explicit version.
- updates_checklist.md: note the tag-triggered versioned publish.
One-time setup (GitHub UI): Settings → Pages → Deploy from a branch → gh-pages.
Add a Developer Guide page covering the admin `?action=` JSON endpoints:
- The PSR-4 controllers under Admin\Ajax that replaced the retired ~4985-line
Views/admin/api.php (PR #173) — BaseAjaxController scaffolding
(ok/fail/gate/gateAny/requireXhr/json), LineStateTrait, per-area controllers,
route registration and the dispatchApi → AjaxController fallback order.
- The structured search JSON contract (PR #174): envelope, item shape,
self-describing actions, per-entity data, and the client-side card renderer;
plus the note that only the render path changed (matching is unchanged; a
missing live stream means a stale streams FULLTEXT index).
Wire it into the Developer Guide nav (+ ru nav_translations) and cross-link it
from HTTP Request Handling. English source only; docs/ru is regenerated before
release.
Move the generated site out of the repo root: site_dir -> build/site, with the
Pages upload path and .gitignore updated to match. build/ already holds tooling
config, so the build output no longer clutters the top-level listing.
Sidebar/tab labels live in mkdocs.yml nav, not in the translated Markdown, so
the ru site was showing English menu titles over Russian content. Add a
nav_translations map for the ru locale (mkdocs-static-i18n) covering every tab,
section and page title — "User Guide" → "Руководство пользователя", etc. English
nav is unchanged; strict build clean.
CI translation was slow, so move it out of GitHub Actions: docs/ru is now a
committed, generated tree refreshed LOCALLY before a release; CI only builds it.
- pages.yml: drop the setup-python/cache/translate steps — the workflow now just
installs the build toolchain and runs `mkdocs build --strict` on the committed
en+ru trees.
- .gitignore: stop ignoring docs/ru (now committed); keep site/ + .docs-cache/.
- Split deps: docs/requirements.txt = build only (mkdocs-material, static-i18n,
used by CI); tools/docs/requirements.txt = translators (local-only).
- Makefile: docs-venv installs both; docs-build/docs-serve no longer translate
(translation is the deliberate `make docs-translate` release step).
- updates_checklist.md: new "Regenerate translated documentation" step
(make docs-translate + docs-build, commit docs/ru with the release commit).
- Commit the generated docs/ru (37 files, translators/yandex).
Rule: edit ONLY docs/en; docs/ru is generated — never hand-edit it.
Replace the hand-maintained Docsify site (parallel docs/en + docs/ru trees
that had already drifted) with a MkDocs Material build where English is the
single source of truth and Russian is generated at build time.
Engine & structure
- mkdocs.yml: Material theme, site_url for the /XC_VM/ Pages subpath, and a
two-tab information architecture — User Guide (administration, API/Swagger,
UI translations, diagnostics, info/FAQ) vs Developer Guide (architecture,
workflow, security, integrations, build). Files are NOT moved — the split is
nav-only, so URLs and cross-links stay stable.
- mkdocs-static-i18n (folder mode): English at root, Russian under /ru/, with a
language switcher. `mkdocs build --strict` validates every link/anchor.
Translation pipeline (tools/docs/translate.py)
- Engine-agnostic via DOCS_TRANSLATE_PROVIDER: translators (free, no API key —
default), anthropic, deepl, or noop. Per-file sha256 cache so only changed
English files are re-translated. Markdown-safe: code, URLs, HTML tags and
glossary terms (XC_VM, FFmpeg, HLS, ...) are masked and never translated.
A file whose translation fails falls back to English so the build never breaks.
- docs/ru is generated and gitignored — never committed. It is produced locally
(`make docs-serve` / `docs-build`) and in CI.
CI & tooling
- pages.yml: build-then-upload (setup-python -> install -> restore .docs-cache
with restore-keys -> translate -> mkdocs build --strict -> deploy), replacing
the verbatim docs/ upload.
- Makefile: docs-venv / docs-translate / docs-build / docs-serve.
- docs/requirements.txt; .gitignore for docs/ru, site/, .docs-cache.
Migration details
- Removed Docsify control files (index.html, _navbar.md, _sidebar.md, .nojekyll)
and de-Docsify-ed body links in 6 English files (en-us/ aliases -> relative,
swagger _media paths, stripped ':ignore' link syntax).
- Preserved the two Russian-only planning docs (no English source) by moving
them into docs/adr/ as *.ru.md (repo-internal, excluded from the site).