Files
ad/COMPLIANCE.md

10 KiB

Compliance: Molodetz against /workspace/dpp/dptemplate.md

Status date: 2026-10-05. Template read in full and never edited. Selection: SELECTION.md. Proof runs: out/pytest-full.log (full suite), out/check-ui.log (six gates), out/dpp-gates.txt (DPP tooling dpp.gates.run_gates, all PASS), out/locust-smoke.log (51 requests, 0 failures), make coverage (76% including the server subprocess).

Legend: MET, PARTIAL (works, but narrower than the template text), NOT MET (missing, with the reason).

Section 1.1 Backend choices

Choice Status Where / how
Python >= 3.12 MET pyproject.toml requires-python = ">=3.12"; venv runs 3.13
FastAPI + Jinja2, server-rendered first MET molodetz/main.py; one Jinja2Templates in molodetz/templating.py (gate 1)
uvicorn (standard extras), reload in dev, workers in prod MET uvicorn[standard]; make dev (--reload --reload-dir molodetz --backlog 4096), make prod (--workers, --backlog 8192, proxy headers, pinned static version)
SQLite via dataset, sync by design, no ORM/migrations MET molodetz/database/core.py: dataset.connect with NullPool, exact connect_args and PRAGMA list; schema sync in database/init_db.py; no threadpool around DB calls (backup DB writes moved out of to_thread)
passlib PBKDF2-SHA256, no JWT MET molodetz/utils/auth.py hash_password/verify_password; sessions in sessions table
httpx (HTTP/2) only through the stealth factory; curl_cffi impersonation MET molodetz/stealth.py (only file that builds httpx clients) + molodetz/curl_transport.py; httpx[http2]; SSRF guard molodetz/net_guard.py; used by link_check.py (admin repo-link check)
mistune (GFM) server-side, emoji as shortcode source MET molodetz/rendering.py; molodetz/emoji_builder.py writes static/js/generated/EmojiMap.js from the emoji library
Pillow, imagehash MET molodetz/gallery.py: webp thumbnails as blobs in data/uploads, perceptual-hash dedupe
pypdf MET molodetz/cli/commands.py posts import reads .pdf; unit test tests/unit/cli/commands.py
sqlglot MET molodetz/database/sql_lint.py parses every raw SQL literal; molodetz system sql-lint; tests/unit/database/sql_lint.py
defusedxml MET routers/seo.py sitemap self-check, link_check.py XML titles; tests parse sitemap with it
beautifulsoup4 + lxml MET rendering.py (media pass, plain text), docs_search.py, link_check.py
uuid_utils (uuid7) MET database/core.py generate_uid(); blob sharding on the uuid7 tail
brotli, zstandard MET services/backup.py archive codecs (zstd default, brotli option)
aiofiles MET routers/admin/backups.py streams the backup download
python-dotenv, one load_dotenv() MET only in molodetz/config.py
locust MET locustfile.py, make locust / make locust-headless
playwright (direct API) + Chromium MET tests/conftest.py fixtures, no pytest-playwright plugin
pytest, no xdist, no pytest-playwright MET pyproject.toml addopts -p no:xdist

Section 1.2 Frontend choices

Choice Status Where / how
Pure ES6 modules, one class per file, OO, relative imports, no NPM/bundler MET molodetz/static/js/**; Application.js imports components/index.js first; window.app
marked (live content only) MET static/vendor/marked.esm.js, used only by dp-content in the editor preview
DOMPurify MET static/vendor/purify.es.mjs; fail-closed in utils/ContentRenderer.js; e2e asserts script/javascript:/onclick are stripped
highlight.js MET static/vendor/highlight.es.min.js via dp-code
Scripts loaded with type="module" MET templates/base.html and page blocks
Hand-written token-driven CSS MET static/css/variables.css tokens + per-area sheets; breakpoints exactly 360/480/768/1024 (gate 2)
Optional libs chromadb, weasyprint, cairosvg, faker MET (not used) their modules are not selected and the libs are not dependencies

Section 1.3 Bans

Ban Status Evidence
No JWT, OAuth/social login, payments, ads, tracking MET session cookie + API key only; swagger_ui_oauth2_redirect_url=None; no external scripts (CSP in main.py); test tests/unit/main.py
No JS framework, bundler, transpiler MET no package.json; vendored ESM files only
No async DB driver, no threadpool around DB MET sync dataset; asyncio.to_thread only for PBKDF2, archive build, DNS
No bare HTTP client MET gate 3: client constructions only in stealth.py and curl_transport.py
No hardcoded /static/ MET gate 5 on templates; static_url() in molodetz/assets.py (also used for the OG image); API test checks rendered HTML

Section 33.1 Structural gates

# Gate Status Where
1 Template env in one file MET molodetz/gates.py, tests/unit/gates.py, make check-ui
2 Breakpoints equal closed set MET same
3 HTTP client allowlist MET same (allowlist: stealth.py, curl_transport.py)
4 Client-render marker allowlist MET same (only admin/post_form.html)
5 No bare /static/ MET same
6 No manual is-loading outside LoadingButton.js MET same
7 No em-dash characters or entities MET gates.gate_em_dashes + DPP gate_emdash
8 json_error status-first (AST) MET gates.gate_json_error_order + DPP gate
9 Reportable/unreportable partition MET tests/unit/constants.py (reportable set is empty because section 18 is not selected)
10 Documented auth equals real auth for anon/member/admin MET tests/api/docs/api.py over every endpoint in molodetz/docs_api; plus tests/unit/docs_api/registry.py (every product route is documented)
11 Index plans use the index, no temp b-tree MET tests/unit/database/init_db.py (EXPLAIN QUERY PLAN for the main read paths)
12 Full suite green, three tiers, one pass MET out/pytest-full.log

Section 33.2 Architectural gates

# Gate Status Note
1 Four faces PARTIAL HTML, JSON (respond(..., model=)), docs registry: yes. Assistant face skipped because section 16 is not selected
2 Soft-delete reads/inserts MET deleted_at IS NULL filters; inserts write deleted_at/deleted_by
3 Cache versions MET cache_state version sync for auth/settings; display caches have TTL env pins
4 Role writes invalidate admin cache MET database/users.set_role
5 File mutations guard writability MET blob writer and backup paths under DATA_PATHS
6 Outbound through the guarded factory MET stealth.guarded_async_client
7 Side effects through funnels MET utils/audit.py, utils/notifications.py, services/queue.py
8 Destructive assistant tools gated N/A no assistant
9 Personal data disclosed MET templates/privacy.html covers join requests, sessions, visit counts
10 Events catalogued MET events.md
11 Section 31 features N/A no value, state-machine or multi-path features (31 not selected)
12 Production data untouched PARTIAL the app only writes its own data/; hooks/prod_guard.py exists but cannot be wired into this agent runtime
13 Tree matches SELECTION.md MET DPP gate_selection_vs_tree PASS

Other conventions checked

  • Layout (section 2): routers and tests/api, tests/e2e mirror URL paths; tests/unit mirrors module paths; cli/main.py; routers/__init__.py does not aggregate; the admin package aggregates on its base router.
  • Config (section 3): DATA_PATHS registry, ensure_data_dirs(), APP_VERSION via tomllib, BOOT_ID/STATIC_VERSION, make prod exports MOLODETZ_STATIC_VERSION. The pre-commit hook bumps the patch version (seen working: 1.0.0 to 1.0.4).
  • Secrets: the admin password comes only from .env (mode 600, generated randomly, not in git). .env.example has no secrets.
  • Browser tests use wait_until="domcontentloaded" on every goto and wait_for_url, and check for horizontal overflow on mobile.
  • Makefile (27.1): venv stamp, PYTHONDONTWRITEBYTECODE=1, dev/prod/test matrix, check-ui, preflight, coverage*, locust*, prune/prune-dry-run, tree/tree-loc/zip/delete-pyc/clean.

Not met or narrowed (honest list)

  1. Docker, nginx sidecar, instance, docker-* and deploy make targets: not shipped. Section 28.2 is not selected (bare metal only), and deploy would push to a remote, which was not requested.
  2. Assistant face (8.2 item 3) and action catalogue: skipped (section 16 not selected).
  3. Presence is the CORE subset and works by polling /presence/roster (20 s) plus server-side touch throttling. There is no WebSocket relay; the rest of section 17 is deferred in SELECTION.md.
  4. There is no public signup. The only account is the env-bootstrapped admin (retoor), so the birth-date/terms signup flow and its shared e2e helper do not exist. Terms acceptance exists (/terms/accept, TermsGate.js).
  5. Votes, reports, comments, maturity partials and the report dialog are absent (sections 18 and 20.2 not selected). The JobPoller, OptimisticAction, StickyScrollPane and emoji picker front-end modules are not built, because no selected feature needs them.
  6. Mutation routes parse bodies with a shared json_or_form(request, Model) helper (form or JSON into one Pydantic model) instead of Annotated[Model, Form()]. Validation is equivalent; the signature style differs.
  7. The repo-link check on join requests runs inline in the admin request, not as a durable job (section 14.4 not selected).
  8. The notification type registry has 6 types (join, backup, system and similar), not the about 20 the template lists for bigger products.
  9. .coveragerc uses parallel = True. The template text says "no parallelism", but subprocess coverage of the server (also required there) only works with per-process data files that are then combined. With parallel = False the server data was overwritten (47% instead of 76%).
  10. hooks/prod_guard.py (28.3) is present but not wired into any agent hook runner on this box.
  11. Web lock (web.lock) path is defined but unused: there is a single web role and services run under service.lock. A suspension check exists in auth, but there is no UI to suspend accounts.
  12. fuser is not installed on the box, so locust-run port cleanup is skipped there (the - prefix ignores it).