Fold L-13 to L-19 status into lesson log; add content master brief
This commit is contained in:
@@ -0,0 +1,554 @@
|
||||
<!-- retoor <retoor@molodetz.nl> -->
|
||||
# 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` |
|
||||
+21
-21
@@ -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
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user