From afb4d557c4968eca8d0c5f39564b473a6bed072c Mon Sep 17 00:00:00 2001 From: retoor Date: Tue, 6 Oct 2026 03:57:12 +0200 Subject: [PATCH] Fold L-13 to L-19 status into lesson log; add content master brief --- CONTENT_MASTER.md | 554 ++++++++++++++++++++++++++++++++++++++++++++ DEVELOPMENT_FLOW.md | 42 ++-- pyproject.toml | 2 +- 3 files changed, 576 insertions(+), 22 deletions(-) create mode 100644 CONTENT_MASTER.md diff --git a/CONTENT_MASTER.md b/CONTENT_MASTER.md new file mode 100644 index 0000000..14936fc --- /dev/null +++ b/CONTENT_MASTER.md @@ -0,0 +1,554 @@ + +# Content master handover: Molodetz + +Audience: the coworker agent that takes over **content** for production +Molodetz. This is not an engineering or DPP-template brief. + +| Fact | Value | +|------|-------| +| Production host | `https://molodetz.nl` | +| Staging host | `https://staging.app.molodetz.nl` (proxies `127.0.0.1:19847`) | +| Test host | `https://molodetz-test.app.molodetz.nl` (proxies `127.0.0.1:19849`) | +| Local/dev | `http://127.0.0.1:8088` (`make dev`) | +| Git remote | `https://retoor.molodetz.nl/retoor/ad.git` (private Gitea) | +| Local clone | `/workspace/retoor/molodetz-app` | +| Package | `molodetz` | +| Language on site | English only | +| Owner / sole public author today | `retoor` | +| Old brand | Absurd Development (git repo name `ad`; public product name Molodetz) | + +`DEVELOPMENT_FLOW.md` in this repo is the multi-agent **engineering lesson +log** that may feed the DPP template. It is **not** the content bible. +Content work stays here and on the live site. Do not fold Molodetz-specific +voice, memes, or intro text into `/workspace/dpp/dptemplate.md`. + +Production admin password and `SECRET_KEY` live with the operator / secrets +store (compose `PROD_*` env). **Never** put production passwords, API keys, +or `.env` values in this file, in posts, in captions, or in git. + +## 0. Repository and code changes + +Private Gitea repository: + +``` +https://retoor.molodetz.nl/retoor/ad.git +``` + +- Clone, commit, and push code (gallery wiring, captions in `gallery.py`, + generator scripts if promoted into the repo) against that remote on + branch `main` unless ordered otherwise. +- Auth for git: Gitea user credentials from the **operator secrets store** + (same class of secret as `~/.config/gitea/retoor.molodetz.nl.env` on the + build box). Use a one-shot credential helper or `GIT_ASKPASS`; **never** + embed the password in the remote URL, in `.git/config`, in this file, or + in chat logs. +- Content-only work that only needs admin UI/API does not require a push. + Changing published gallery files without updating `GALLERY` in git will + drift; prefer commit+push for media registry changes. +- Markdown files (including this one) need explicit user confirmation + before commit. + +## 0.1 Machine-readable docs for agents + +Before calling write APIs, load the live docs so samples stay in sync with +the registry (`molodetz/docs_api/*`, built by `molodetz/docs_export.py`): + +| Doc | Production URL | Notes | +|-----|----------------|-------| +| Docs home | `https://molodetz.nl/docs` | Sidebar + prose | +| API groups (HTML) | `https://molodetz.nl/docs/api/content`, `.../join`, `.../account`, `.../admin` | Admin group requires an admin session | +| Full download (markdown) | `https://molodetz.nl/docs/download.md` | Auto-generated: every endpoint with method, path, auth, fields, curl, request JSON, response sample | +| Full download (HTML) | `https://molodetz.nl/docs/download.html` | Same content, HTML | +| OpenAPI | `https://molodetz.nl/swagger` | Framework OpenAPI UI (`/openapi.json`) | +| Auth prose | `https://molodetz.nl/docs/api` | How sessions and API keys work | + +Replace the host with staging or test URLs when working on those +environments. Downloads omit the Admin group unless the request is +authenticated as admin. + +### Auth (Account group) + +No JWT. No OAuth. Every HTML route also returns JSON when called with +`Accept: application/json`. + +1. **Session cookie** (64 hex): `POST /auth/login` with + `username`, `password`, optional `remember`. Cookie lasts 7 days + (30 with remember). Sample success: + `{"ok": true, "redirect": "/admin", "data": null}`. +2. **`X-API-KEY`**: after login, `GET /profile/api-key` (member+). Sample + shape `{"api_key": "..."}`. Destructive renew: + `POST /profile/api-key/regenerate`. +3. Also accepted by the app (see docs prose): `Authorization: Bearer` and + `Basic` with the same credential resolution as the API key path. + +Log out: `POST /auth/logout`. Accept terms if gated: +`POST /terms/accept` (member). + +### Public read (Content group) + +Use these to verify what the site shows after you publish. All `auth=public`. + +| Method | Path | Purpose | Response sample (registry) | +|--------|------|---------|----------------------------| +| GET | `/` | Home + featured flyer counts | `{"posts": [], "flyer_count": 4, "meme_count": 22}` | +| GET | `/roll` | Roll feed; query `before` cursor | `{"title": "Roll", "topic": "roll", "posts": [], "next_cursor": null}` | +| GET | `/standard` | Standard feed | `{"title": "Standard", "topic": "standard", "posts": []}` | +| GET | `/posts/{slug}` | One post | `{"post": {"title": "...", "topic": "roll"}, "author": {"username": "retoor"}}` | +| GET | `/flyers` | Flyer gallery | `{"kind": "flyer", "title": "Flyers", "items": []}` | +| GET | `/memes` | Meme gallery | `{"kind": "meme", "title": "Memes", "items": []}` | +| GET | `/people` | People list | `{"people": [{"username": "retoor", "post_count": 5}]}` | +| GET | `/people/{username}` | Profile + posts | `{"person": {"username": "retoor"}, "posts": []}` | +| GET | `/health` | Liveness | `{"status": "ok", "version": "..."}` | + +There is **no** public or admin HTTP API to upload a new meme/flyer file. +Galleries are files under `molodetz/static/media/` plus rows in +`molodetz/gallery.py` synced on boot. Content master still uses Content +GET to confirm captions and order after deploy. + +Example: + +```bash +curl -H 'Accept: application/json' https://molodetz.nl/roll +curl -H 'Accept: application/json' https://molodetz.nl/flyers +``` + +### Join group (visitor intake; admin triage separately) + +| Method | Path | Auth | Body / notes | +|--------|------|------|--------------| +| GET | `/join` | public | Form; sample `{"submitted": false}` | +| POST | `/join` | public | Required `name`, `contact`; optional `repo_url`, `message`. Sample: `{"ok": true, "redirect": "/join?ok=1", "data": {"uid": "0190..."}}` | + +Join rows are **never public**. Content master does not invent join +stories. Admins list and update them under Admin (below). + +### Admin group (content master write path) + +Requires admin role (retoor). Prefer session cookie after UI login, or +admin API key via `X-API-KEY`. Always send `Accept: application/json` +for machine clients. + +#### Posts + +| Method | Path | Purpose | Body fields (registry) | +|--------|------|---------|------------------------| +| GET | `/admin/posts` | List | | +| GET | `/admin/posts/new` | New form | | +| POST | `/admin/posts` | Create | required `title`, `body`; `topic` example `roll` (also `standard`); `status` example `draft` (`published`); optional `is_placeholder` | +| GET | `/admin/posts/{uid}/edit` | Edit form | path `uid` | +| POST | `/admin/posts/{uid}` | Save | path `uid` + same fields as create | +| POST | `/admin/posts/{uid}/publish` | Toggle publish | path `uid` | +| POST | `/admin/posts/{uid}/delete` | Soft delete | path `uid` | + +Create sample response: +`{"ok": true, "redirect": "/admin/posts", "data": {"uid": "..."}}`. + +Example create (password never stored here; use operator secrets): + +```bash +curl -X POST https://molodetz.nl/admin/posts \ + -H 'Accept: application/json' \ + -H 'Content-Type: application/json' \ + -H "X-API-KEY: $MOLODETZ_API_KEY" \ + -d '{"title":"A title","body":"Text in **markdown**.","topic":"roll","status":"draft"}' +``` + +#### Home intro (settings) + +| Method | Path | Purpose | +|--------|------|---------| +| GET | `/admin/settings` | Read settings including `site_intro` | +| POST | `/admin/settings` | Save settings (form/JSON; include `site_intro` markdown) | + +#### Join triage + +| Method | Path | Purpose | Body | +|--------|------|---------|------| +| GET | `/admin/joins` | List requests | | +| POST | `/admin/joins/{uid}/status` | Set status | `status` in `open`, `contacted`, `accepted`, `declined` | +| POST | `/admin/joins/{uid}/check` | Fetch repo link title via guarded outbound client | | + +#### Trash (restore mistaken deletes) + +| Method | Path | Body | +|--------|------|------| +| GET | `/admin/trash` | | +| POST | `/admin/trash/restore` | required `stamp` | +| POST | `/admin/trash/purge` | required `stamp` (destructive) | + +Other admin endpoints (services, backups, users, audit, stats) are +operations, not day-to-day content. See `/docs/api/admin` or +`download.md` while logged in as admin for the full list. Do not schedule +backups or purge trash unless ordered. + +### Source of truth + +Endpoint tables above are copied from `molodetz/docs_api/content.py`, +`join.py`, `account.py`, and `admin.py` as of this handover. If the +registry changes, **trust live** `https://molodetz.nl/docs/download.md` +over this file and update the handover when you next touch it. + +--- + +## 1. What the site is built for + +Molodetz is a **community blog roll**: calm elite serif UI, slightly +provocative content, markdown posts in SQLite, two galleries (flyers and +memes), a people list, and a real join form. + +Purpose, as stated in the home intro (`site_intro`, seeded from +`molodetz/seed/intro.md`) and in `SELECTION.md`: + +- Hostile, data-backed elite intro: floor = current skill; the site does + not flatter mediocrity. +- Help people **reach elite level**, especially by learning to use AI as a + craft tool, not as a substitute for judgement. +- Divide honestly: classic hand-coders who refuse AI, vibe "coders" who + refuse to learn to code, and the **elite twice** path (classic craft + + AI). +- Offer a path: take the profession seriously; ship; read every line the + machine writes. +- Side product context in the intro: a development environment for the + elite (vim/tmux-class toolkit plus AI skill). Do not invent product + claims beyond what retoor publishes. + +`SELECTION.md` one-liner: "Calm elite serif community blog roll for +retoor. English. Posts, flyers, memes, people, join intent." + +--- + +## 2. Intentions + +- Fight mediocrity. A pass is the floor, not an ambition (seed roll post + and retired Dutch assets said the same thing; English site keeps it). +- Divide and offer a path. Look at the person next to you: replaced or + replacing. +- Promote **elite programmers who embraced AI** and kept craft: + "elite twice" (know the code, use the machine, read every line, ship). +- Roast **classic programmers who refuse AI** on principle (vim/tmux + purity, "I don't need autocomplete", compilers-made-us-lazy recycling). +- Roast **vibe coders** who refuse to learn to code / do not take the + profession seriously (prompt-and-pray, tab-tab-accept-all, README with + badges and zero tests, course before craft). +- Facts over feelings. Pride backed by data is allowed; apology spirals + are not the brand. + +--- + +## 3. Tone + +| Do | Do not | +|----|--------| +| Harsh, rude, elitist when earned | Soft HR copy, engagement bait | +| Facts, never invented lies | Fake testimonials, fake members | +| Based, conservative craft ethos | Hype, hustle-culture emptiness | +| Pro-AI **and** pro classic elite; especially the combo | Pro-AI that excuses ignorance of code | +| Proud and specific | God-complex apology spiral | +| English | Dutch on the public site | +| Short punchy posts and one-point flyers | Em-dashes, emoji decoration, slurs | + +Banned characters in shipped prose and captions: em dash (U+2014) and +en dash (U+2013). Use commas, periods, semicolons, or hyphens. + +--- + +## 4. What NOT to do + +- Invent members, join success stories, or co-authors. People today is + effectively **retoor only** until real people join and are approved. +- Selfie / photo of retoor as gallery art. +- DevPlace, Nigel, or other named private individuals in memes or posts. +- JWT, OAuth-as-flex, framework catalogue flex, payment/ad SDK vibes in + content (engineering also forbids JWT; content should not romanticize + it). +- Dutch copy on public pages, captions, or new assets. Old Dutch media + files may remain on disk under `RETIRED_GALLERY` but must stay + unpublished. +- Em-dashes in UI copy, posts, captions, alt text, or image text. +- Commit secrets, `.env`, production passwords, API keys, or dump them + into CONTENT docs. +- Weaken factual claims in the intro without retoor's order. Do not + "tone down" the hostile frame into a friendly corporate blog unless + ordered. +- Fold Molodetz voice into the DPP template or treat `DEVELOPMENT_FLOW.md` + as a place to store meme recipes. + +--- + +## 5. Content surfaces + +| Surface | URL | What it is | Who edits | +|---------|-----|------------|-----------| +| Home intro | `/` | `site_intro` setting (markdown). Seed: `molodetz/seed/intro.md`. Hostile elite manifesto. | Admin Settings | +| Roll | `/roll` | Short notes, newest first. Topic `roll`. Seed placeholders exist. | Admin Posts | +| Standard | `/standard` | Standards / taste posts. Topic `standard`. Seed: "Thirty-one choices. You have zero." (DPP-ish stack choices, no JWT, no JS framework). | Admin Posts | +| Flyers | `/flyers` | Portrait gallery. Kind `flyer`. Featured flyer on home = first flyer by gallery position (`flyer-elite-twice.jpg`). | Files + `molodetz/gallery.py` sync | +| Memes | `/memes` | Square gallery. Kind `meme`. Classic / vibe / elite sets + a few older English leftovers. | Files + gallery sync | +| People | `/people`, `/people/retoor` | Authors with posts. | Real accounts only; do not invent | +| Join | `/join` | "I'm in" form; stored for admins, never public. | Visitors submit; admin triage at `/admin/joins` | +| Docs | `/docs` | Operator/API docs. Not a content marketing channel. Contributing/component docs were removed on purpose. | Engineering | + +### Seed posts (English; from `molodetz/content.py`) + +| Topic | Title | Role | +|-------|-------|------| +| standard | Thirty-one choices. You have zero. | Stack / taste standard | +| roll | A pass is not an ambition. | Floor / mediocrity | +| roll | Finished is a feature. | Ship | +| roll | The assistant is not a senior. | AI without judgement | +| roll | No course. | Anti weekend-ebook path | + +Roll seeds are created as **placeholders** (`is_placeholder`); bodies may +carry a carried-over note. Prefer replacing placeholders with real retoor +stories over deleting the point. + +### Live gallery (published English set) + +**Flyers** (1080x1350), order matters (home featured = first): + +1. `flyer-elite-twice.jpg` - Elite twice. +2. `flyer-taste.jpg` - Taste does not autocomplete. +3. `flyer-demo.jpg` - A demo is not a product. +4. `flyer-hands.jpg` - Your hands are not the craft. + +**Memes** (1080x1080): six classic, six vibe, six elite (`meme-classic-*`, +`meme-vibe-*`, `meme-elite-*`), plus older English leftovers +`meme-compiles.jpg`, `meme-senior.jpg`, `meme-trophy.jpg`, +`meme-todo.jpg` (legacy 1280x720 era; keep or retire only on order). + +**Retired on disk, unpublished** (`RETIRED_GALLERY` in `gallery.py`): old +Dutch flyers/memes/posters/takes (`flyer-keuzes.jpg`, `meme-vloer.jpg`, +`poster-*`, `take-*`, `story.jpg`, ...). Soft-deleted in DB. Do not +re-publish. + +Captions/alt live in `GALLERY` tuples and sync into `media_items` on boot. +English only. Captions must not start with SQL keywords that trip +sqlglot string lint (`SELECT`, `INSERT`, ...). + +--- + +## 6. How to publish + +Prefer the admin HTML UI for interactive work. Prefer the Admin API +(section 0.1) for scripts and agent automation. Both share the same +handlers. + +### Login + +1. Open `https://molodetz.nl/auth/login` (or local/staging equivalent). +2. Username: `retoor` (production admin username from env bootstrap). +3. Password: **production admin password is with the operator / secrets + store, never in the repo.** Staging/test may use compose defaults + documented in `README.md`; production uses `PROD_ADMIN_PASSWORD`. +4. Session cookie, 7 days (30 with remember). No JWT. + +### Edit home intro + +1. `/admin/settings` +2. Field `site_intro` (markdown textarea). +3. Save. Home re-renders from settings (landing cache TTL applies). + +### Create / edit / publish a post + +1. `/admin/posts` or `/admin/posts/new` +2. Title, markdown body, topic `roll` or `standard`, status draft or + published. +3. Publish/unpublish from the posts list. +4. Soft delete goes to trash; restore from `/admin/trash` if needed. + +### Join requests + +1. `/admin/joins` for open requests. +2. Status and optional repo-link check (outbound stealth client). +3. Do not invent acceptances in public content. + +### Galleries (flyers / memes) + +1. Add JPEG under `molodetz/static/media/` with an English filename. +2. Register in `GALLERY` in `molodetz/gallery.py` with kind + English + caption (alt text). +3. Put new flyers in the desired order; index 0 is the home featured + flyer. +4. Deploy / restart so `sync_gallery()` runs (bootstrap). Dedupe uses + perceptual hash distance `<= 2`; near-duplicate layouts are skipped. +5. Soft-retire old files via `RETIRED_GALLERY` + `retire_media`; keep + bytes on disk unless ordered otherwise. +6. Visual-check every new image (open the PNG/JPG; spelling, crop, + legibility). Build a contact sheet when shipping a set + (`out/memes-sheet.jpg` practice). + +Engineering owns compose, molohttp, and data trees +(`./data/production` is holy). Content master does not refresh staging +from production or touch molohttp unless explicitly ordered. + +--- + +## 7. Meme / flyer production recipe + +Generator scratch (not committed): +`/workspace/tmp/molodetz-app-20261005/memes/` +(`lib.py`, `parts.py`, `classic.py`, `vibe.py`, `elite.py`, `flyers.py`, +`gen.py`). Prefer regenerating with Pillow so **all text is drawn**, never +baked into a copyrighted meme photo. + +### Dimensions and output + +| Kind | Size | Format | +|------|------|--------| +| Meme | 1080 x 1080 | JPEG, quality ~93, subsampling 0 (`lib.save`) | +| Flyer | 1080 x 1350 | same | + +### Fonts (from `lib.py` `FONT_FILES`) + +Base dirs: + +- Google: `/usr/share/fonts/truetype/sand-box/google/` +- Custom: `/usr/share/fonts/truetype/sand-box/custom/` +- DejaVu: `/usr/share/fonts/truetype/dejavu/` + +| Key | File | Typical use | +|-----|------|-------------| +| `anton` | Anton/Anton-Regular.ttf | Meme titles, impact headers | +| `archivo` | Archivo Black/ArchivoBlack-Regular.ttf | Dense meme body | +| `mono` / `monob` / `monosb` | IBM Plex Mono Regular/Bold/SemiBold | Terminals, keys | +| `space` | Space Mono/SpaceMono-Bold.ttf | **wordmark `molodetz`** | +| `serif` / `serifi` | DM Serif Display Regular/Italic | Flyer headlines | +| `caslon` / `caslonb` / `casloni` | Libre Caslon Text | Flyer body / italics | +| `sans` / `sanssb` / `sansb` / `sansblack` | Source Sans Pro | UI labels, flyer header | +| `sym` / `symb` | DejaVu Sans | Glyphs (e.g. crown) | +| `marker` | Permanent Marker | Occasional accent | + +Text fitting: `block()` / `fit()` must succeed; never ship cropped +letters. Uppercase via `upper=True` when the layout calls for it. + +### Wordmark + +- Literal text: `molodetz` (lowercase). +- Font: Space Mono Bold, default size **24**, pad **30**. +- Default corner: **bottom-right** (`corner="br"`); flyers draw bottom-right + with ink; some dark memes use a dark plate behind the mark or + bottom-left when a layout conflicts. +- Alpha ~170-230 depending on background contrast. + +### Flyer palette and layout (`flyers.py`) + +- Paper cream `PAPER = (243, 238, 227)` +- Ink `(28, 26, 24)`, muted `(110, 102, 92)`, accent red `(178, 48, 36)` +- Margin `M = 96` +- Header: spaced `MOLODETZ` (Source Sans Bold 26) + `No. N of 4` (Caslon italic) +- Motif, then DM Serif headline, short red rule, Caslon body +- Footer: `ONE POINT PER SHEET` + wordmark +- One point per sheet. Calm but cutting. English. + +Current four points: elite twice; taste does not autocomplete; a demo is +not a product; your hands are not the craft. + +### Meme palettes and targets + +Roughly one third each: + +1. **Classic (refuse AI)** - dark terminals, cream starter packs, charts + where keystrokes rise and features stay flat, certificates of manual + labour, multi-generation refusal. Colors: near-black gradients, cold + greys, warning red `(255, 95, 86)`, occasional yellow band. +2. **Vibe (refuse craft)** - Win95-style prompt-and-pray, pink/purple + galaxy "vibe debugging", starter packs (TAB, sk-live-, localhost), + weekend-to-course arcs, Drake-style NAH/YEP priorities. +3. **Elite (embrace AI + craft)** - gold `GOLD = (255, 196, 60)`, dark + navy/black panels, crown glyph, build logs that review every line, + choose-your-fighter with elite column highlighted, curves that keep + climbing, achievement unlocked. + +Classic formats allowed as **layouts only** (two-panel, galaxy brain, +Drake approve/reject, starter pack, fake terminal, fake error, fake +chart, certificate). Draw backgrounds programmatically or with +non-copyrighted generation. No stolen meme template photos. + +Dev-culture references encouraged: vim/tmux, Stack Overflow, works on my +machine, README-driven, 10x, git blame, compiler errors, prompt-and-pray, +tab-tab-tab accept-all, punch cards, etc. + +### Content bans on assets + +No slurs. No real named individuals. No em-dashes in image text. No +DevPlace/Nigel. No selfie. English filenames and English captions. + +### Wiring and QA + +1. Generate into scratch `out/`, visual-read every file, fix spelling. +2. Copy into `molodetz/static/media/`. +3. Update `GALLERY` captions; retire Dutch leftovers in `RETIRED_GALLERY`. +4. Contact sheet of the full new set (filenames under each tile). +5. Restart / sync; confirm `/flyers`, `/memes`, home featured flyer. +6. Keep pairwise phash distance clearly above 2 so sync does not drop + "duplicates". + +--- + +## 8. Consistency checklist (every new asset or post) + +- [ ] English only; no Dutch UI copy. +- [ ] No em-dash or en-dash characters. +- [ ] Tone matches section 3 (harsh when earned, factual, pro elite+AI). +- [ ] Targets the right enemy or the elite path; does not punch down with + slurs or real names. +- [ ] Post: correct topic (`roll` vs `standard`); markdown renders; no + secret material. +- [ ] Meme 1080x1080 or flyer 1080x1350; wordmark present; text fully + visible (no clip). +- [ ] Caption/alt English, honest, not SQL-keyword-leading. +- [ ] `GALLERY` updated or post published via admin; retired files not + re-listed. +- [ ] Visually verified (Read/open the image or preview the post). +- [ ] No commit of `.env`, passwords, or private join PII. + +--- + +## 9. Success criteria for the content master + +1. Home intro stays hostile, data-backed, and accurately retoor's voice + unless retoor orders a rewrite. +2. Roll accumulates real finishing/AI/craft stories; placeholders shrink. +3. Standard stays taste-and-standards, not tutorial spam. +4. Galleries keep the three-way thesis (classic roast / vibe roast / + elite twice) with consistent visual system and English captions. +5. Featured flyer remains the thesis sheet (elite twice) unless retoor + reorders deliberately. +6. Join queue is handled as private admin work; public site never fakes + community size. +7. No Dutch, no em-dashes, no secrets, no invented people in anything + shipped. +8. New assets are reproducible from the Pillow recipe (or an equal + programmatic successor), not one-off unreproducible binaries without + text sources. +9. Content changes do not require inventing engineering; when gallery + code or settings must change, coordinate with engineering and keep + tests green on their side. +10. Coworker can ship a new meme set or post week using only this + document, admin login (secrets from operator), and the generator + path above. + +--- + +## Quick reference paths + +| Item | Path / URL | +|------|------------| +| Git remote | `https://retoor.molodetz.nl/retoor/ad.git` | +| App repo (local) | `/workspace/retoor/molodetz-app` | +| Production | `https://molodetz.nl` | +| Staging | `https://staging.app.molodetz.nl` | +| Test | `https://molodetz-test.app.molodetz.nl` | +| Docs download | `https://molodetz.nl/docs/download.md` | +| OpenAPI | `https://molodetz.nl/swagger` | +| API registry (code) | `molodetz/docs_api/` | +| Download builder | `molodetz/docs_export.py` | +| Intro seed | `molodetz/seed/intro.md` | +| Gallery registry | `molodetz/gallery.py` | +| Media files | `molodetz/static/media/` | +| Seed posts | `molodetz/content.py` `SEED_POSTS` | +| Feature selection | `SELECTION.md` | +| Compliance vs DPP | `COMPLIANCE.md` (engineering; not tone) | +| Engineering lessons (not content) | `DEVELOPMENT_FLOW.md` | +| Meme generator scratch | `/workspace/tmp/molodetz-app-20261005/memes/` | +| Deploy notes | `README.md`, `molohttp_deploy.md` | diff --git a/DEVELOPMENT_FLOW.md b/DEVELOPMENT_FLOW.md index 53af238..9e892d1 100644 --- a/DEVELOPMENT_FLOW.md +++ b/DEVELOPMENT_FLOW.md @@ -391,9 +391,9 @@ No lesson content may be deleted when the format evolves. |--------------|-------| | date | 2026-10-05 | | agent | muse-spark | -| status | proposed | -| dpp_template | no | -| dpp_reason | proposed; Grok folds into template deploy section | +| status | folded | +| dpp_template | yes | +| dpp_reason | folded into dptemplate 28.1 (restart reload servers after pull, verify version) and 35.5 step 4 | File-watching dev servers restart on the first changed file they notice. A version-control update writes files one by one, so the restart can @@ -412,9 +412,9 @@ Production targets without reload are unaffected but still verify. |--------------|-------| | date | 2026-10-05 | | agent | muse-spark | -| status | proposed | -| dpp_template | no | -| dpp_reason | proposed; agent operating rule, Grok decides the template fold | +| status | conflict | +| dpp_template | yes | +| dpp_reason | partial fold into dptemplate 35.1 (forward only, research-backed details); stop-and-ask kept per 0.1, 35.1, 35.2; CONFLICTS item 5 | Standing instruction from retoor, recorded as a clear rule: @@ -433,9 +433,9 @@ Standing instruction from retoor, recorded as a clear rule: |--------------|-------| | date | 2026-10-05 | | agent | muse-spark | -| status | proposed | -| dpp_template | no | -| dpp_reason | proposed; Grok folds into template docs section | +| status | folded | +| dpp_template | yes | +| dpp_reason | folded into dptemplate 23 (route coverage closure, safe probes), 27.3, 30 step 6, 33.1 item 13, 33.2 item 17 | The suite enforces documentation coverage: `test_every_product_route_is_documented` fails when any product route @@ -455,9 +455,9 @@ probes must answer anon with anything but 401 or 403. |--------------|-------| | date | 2026-10-05 | | agent | muse-spark | -| status | proposed | -| dpp_template | no | -| dpp_reason | proposed; Grok folds into template forms section | +| status | folded | +| dpp_template | yes | +| dpp_reason | folded into dptemplate 5.2 (public form re-render set, no password echo), 30 step 3, 33.2 item 17 | `RequestValidationError` on an HTML form post only re-renders the form for paths the `validation_handler` in `main.py` knows @@ -474,9 +474,9 @@ friendly messages, or the form silently eats validation errors. |--------------|-------| | date | 2026-10-05 | | agent | muse-spark | -| status | proposed | -| dpp_template | no | -| dpp_reason | proposed; Grok folds into template auth section | +| status | folded | +| dpp_template | yes | +| dpp_reason | folded into dptemplate new 6.7 single-use tokens plus 0.3 row, 29, 30 step 3, 33.2 item 16 | The member-invite pattern is the template for future token features (password reset, email change, one-time links): @@ -495,9 +495,9 @@ The member-invite pattern is the template for future token features |--------------|-------| | date | 2026-10-05 | | agent | muse-spark | -| status | proposed | -| dpp_template | no | -| dpp_reason | proposed; Grok folds into template test section | +| status | folded | +| dpp_template | yes | +| dpp_reason | folded into dptemplate 26.3 (assert intent, not closed sets) and 33.2 item 18 | API tests share one server database per session. A test that asserts an exact set of rows (observed: `/people` usernames equal to the two @@ -514,9 +514,9 @@ assertions are only valid for tables no test ever extends. |--------------|-------| | date | 2026-10-05 | | agent | muse-spark | -| status | proposed | -| dpp_template | no | -| dpp_reason | proposed; Grok folds into template make section | +| status | folded | +| dpp_template | yes | +| dpp_reason | folded into dptemplate 27.1 (venv stamp keyed on dependency inputs, not version) | The Makefile venv stamp depends on `pyproject.toml`, so a version-only bump makes the next `make` target rebuild the whole diff --git a/pyproject.toml b/pyproject.toml index 32ed4d6..38335da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "molodetz" -version = "1.0.19" +version = "1.0.20" description = "Molodetz, a calm community blog roll." readme = "README.md" requires-python = ">=3.12"