vault backup: 2026-05-05 11:17:44

This commit is contained in:
Vadym Samoilenko 2026-05-05 11:17:44 +01:00
parent 201137b96f
commit 26a5c0ca3c
10 changed files with 439 additions and 5 deletions

View file

@ -59,3 +59,9 @@ tags: [daily]
- 11:12 (<1min) | `3m-portal`
- **Asked:** Set up a global communication rule to respond in Russian to users and English for everything else, and document git worktrees usage in Obsidian.
- **Done:** Established language preference rule and created documentation for git worktrees workflow with Claude integration for multi-branch repositories.
- 11:14 | `3m-portal`
- **Asked:** Set global communication rules with Russian for user and English for everything else | Added communication rules to system prompt, documented git worktrees usage in Obsidian | system_prompt.md, Obsidian_Git_Workflow.md
- **Done:** | Global communication rules + git worktrees documentation | Updated system prompt for language rules, created Obsidian guide for git worktrees and multi-branch workflows | system_prompt.md, Obsidian_Git_Workflow.md
- 11:16 | `obsidian-vault`
- **Asked:** Create a global communication rule to respond in Russian to users but use English for everything else, and stop occasionally responding in Ukrainian.
- **Done:** Documented git worktrees workflow for running multiple Claude sessions in parallel and configured Claude to suggest this approach when a repository has multiple branches.

View file

@ -20,20 +20,20 @@ This 3-hop pattern works for hundreds of articles without vector search.
|-------|-------------|----------|
| [[wiki/obsidian-rag/_index\|obsidian-rag/]] | Karpathy's LLM wiki method — Obsidian RAG, setup, vs true RAG | 3 |
| [[wiki/projects-overview/_index\|projects-overview/]] | All 42 Oliver Agency projects — grouped by server (optical-web-1, optical-dev, baic, box-cli) | 1 |
| [[wiki/tech-patterns/_index\|tech-patterns/]] | Recurring tech stacks: FastAPI, React/Vite, Next.js, Azure AD, AI, Box, One2Edit, Redis/Celery, cost-tracker | 21 |
| [[wiki/tech-patterns/_index\|tech-patterns/]] | Recurring tech stacks: FastAPI, React/Vite, Next.js, Azure AD, AI, Box, One2Edit, Redis/Celery, cost-tracker | 22 |
| [[wiki/architecture/_index\|architecture/]] | Cross-cutting architectural patterns: Docker Compose, multi-agent AI, GCP timeout, RAG, hotfolder, optical-dev deploy, cost-tracker, new-project checklist, troubleshooting playbooks, ADR log, Cloud Run Jobs | 11 |
| [[wiki/client-knowledge/_index\|client-knowledge/]] | Per-client notes for Ford, H&M, L'Oréal, Barclays, Ferrero, 3M | 6 |
| [[wiki/concepts/_index\|concepts/]] | Atomic knowledge extracted from Claude Code sessions | 93 |
| [[wiki/concepts/_index\|concepts/]] | Atomic knowledge extracted from Claude Code sessions | 98 |
| [[wiki/connections/_index\|connections/]] | Cross-cutting insights linking 2+ concepts: FastAPI+Azure AD+Docker trinity, AI→cost-tracker, Apache+Vite basePath, GCP→REST polling, Box+hotfolder, Docker DNS+AdGuard, Celery prefork×faster_whisper memory stacking | 10 |
| [[wiki/qa/_index\|qa/]] | Filed answers to queries (saved with `--file-back`) | 0 |
| [[wiki/homelab/_index\|homelab/]] | Self-hosted infra: Proxmox install, IOMMU/PCI passthrough, hypervisor setup, budget builds, HP Elitedesk G3, Homarr API + Apps + Boards + Certificates + Integrations + Settings + Tasks + AdGuard + Clock + Docker Stats + Docker Integration + Download Client + Firewall + Proxmox Integration + Radarr + Readarr + Sonarr + Bookmarks + Calendar + Icons + App Widget + Weather + GitHub + Nextcloud + qBittorrent + RSS Feed + Speedtest Tracker + System Health Monitoring + System Resources + Services Map + Media Stack | 42 |
| [[wiki/homelab/_index\|homelab/]] | Self-hosted infra: Proxmox install, IOMMU/PCI passthrough, hypervisor setup, budget builds, HP Elitedesk G3, Homarr API + Apps + Boards + Certificates + Integrations + Settings + Tasks + AdGuard + Clock + Docker Stats + Docker Integration + Download Client + Firewall + Proxmox Integration + Radarr + Readarr + Sonarr + Bookmarks + Calendar + Icons + App Widget + Weather + GitHub + Nextcloud + qBittorrent + RSS Feed + Speedtest Tracker + System Health Monitoring + System Resources + Services Map + Media Stack | 43 |
| [[wiki/web-agency/_index\|web-agency/]] | AI-assisted website building & selling: Claude Code, Nanobanana 2, Kling, LaunchPath MCP | 9 |
| [[wiki/dotfiles/_index\|dotfiles/]] | Linux terminal ricing: Kitty, Fish, WezTerm CLI, modern Rust CLI tools, LazyVim, unified themes, Tabby | 21 |
| [[wiki/agent-sdk/_index\|agent-sdk/]] | Claude Agent SDK (formerly Claude Code SDK) — build autonomous AI agents in Python and TypeScript | 30 |
| [[wiki/llm-models/_index\|llm-models/]] | LLM model catalogs — OpenAI and Claude/Anthropic models, IDs, context, pricing | 2 |
| [[wiki/claude-code/_index\|claude-code/]] | Claude Code product docs — install, capabilities, surfaces, MCP, hooks, scheduling, multi-agent, plugins, skills, channels, error recovery, LM Studio local | 30 |
| [[wiki/reports/_index\|reports/]] | Weekly and monthly summaries — generate: `uv run python scripts/report-generator.py --weekly` | 1 |
| [[wiki/infrastructure/_index\|infrastructure/]] | Server inventory: all 10 SSH hosts — optical, optical-dev, optical-prod, baic, librechat, modocmms, box-cli, aimpress, pve | 10 |
| [[wiki/infrastructure/_index\|infrastructure/]] | Server inventory: all 10 SSH hosts — optical, optical-dev, optical-prod, baic, librechat, modocmms, box-cli, aimpress, pve | 11 |
| [[wiki/testing/_index\|testing/]] | Web app testing: functional, performance, security, UI types; TDD/BDD/Agile methodologies; Selenium/Cypress/Playwright/JMeter/OWASP ZAP tools | 1 |

View file

@ -110,5 +110,10 @@
| [[wiki/concepts/mongodb-schema-validator-migration-verification]] | MongoDB `collMod` migration marked "applied" without actually running — verify with `getCollectionInfos`; fix with direct `collMod` + `validationLevel: moderate` | daily/2026-04-30.md | 2026-04-30 |
| [[wiki/concepts/git-worktrees-parallel-claude]] | Git worktrees for parallel Claude Code sessions — isolate branches, exact commands, when to suggest, Claude Code `isolation: "worktree"` | manual | 2026-05-05 |
| [[wiki/concepts/docker-compose-restart-no-code-reload]] | `docker compose restart` does not rebuild the image — must run `build && up -d` after any .py change; service name typos fail silently | daily/2026-05-01.md | 2026-05-01 |
| [[wiki/concepts/mongodb-unwind-preservenullandemptyarrays]] | Typo `preserveNullAndEmpty` (wrong) vs `preserveNullAndEmptyArrays` (correct); MongoDB 7.0 rejects unknown `$unwind` options with error 28811; older versions silently ignored them | daily/2026-05-01.md | 2026-05-01 |
| [[wiki/concepts/fastapi-response-model-silent-field-strip]] | FastAPI `response_model` silently strips fields not declared in the Pydantic schema — data exists in DB but never reaches the JSON response | daily/2026-05-01.md | 2026-05-01 |
| [[wiki/concepts/react-useref-event-handler-state]] | React `useState` setters are async — `pointerMove` sees stale state set in `pointerDown`; fix with `useRef` for drag flags; also: `act()` in RTL tests, `useMemo` declaration order | daily/2026-05-01.md | 2026-05-01 |
<!-- Articles added automatically by compile.py -->
<!-- Format: | [[concepts/slug]] | One-line summary | daily/YYYY-MM-DD.md | date | -->

View file

@ -0,0 +1,89 @@
---
title: "docker compose restart Does Not Reload Code in Built Images"
aliases: [docker-restart-stale-code, docker-compose-build-required, docker-restart-vs-build]
tags: [docker, docker-compose, python, deployment, gotcha]
sources:
- "daily/2026-05-01.md"
created: 2026-05-01
updated: 2026-05-01
---
# `docker compose restart` Does Not Reload Code in Built Images
When a Docker Compose service uses `build:` context (image is baked from source at build time), `docker compose restart <service>` restarts the existing container from the cached image — it does **not** rebuild. Any Python code changes since the last `docker compose build` are completely invisible to the running container.
## Key Points
- `docker compose restart` only recycles the container process; the image layer is unchanged
- After any `.py` file change, the full sequence is: `docker compose build <service> && docker compose up -d <service>`
- Common symptom: bug fix applied and restart done, but the bug persists — the stale image is still running
- Service name precision matters: `docker compose restart backend` fails silently if the actual service is named `api`
- `docker compose logs <service>` will still show the old code running with no error
- Hot-reload (Uvicorn `--reload`) only works if the source directory is **volume-mounted** into the container, not just baked in
## Details
### The Wrong Pattern
```bash
# ❌ Code change deployed, restart done — but stale image still runs
vim app/routes/users.py
docker compose restart api
# Bug still present. No error. Container appears healthy.
```
### The Correct Pattern
```bash
# ✅ Rebuild the image, then recreate the container
docker compose build api && docker compose up -d api
```
### Diagnosing Stale Image
To confirm whether the running container has the latest code:
```bash
# Check when the image was last built
docker images | grep <project_name>
# Exec into the container and inspect the file directly
docker compose exec api cat /app/routes/users.py
```
### Volume-Mount Alternative (Dev Only)
For development, mount the source directory so Uvicorn's `--reload` works without rebuilding:
```yaml
services:
api:
build: .
command: uvicorn app.main:app --reload --host 0.0.0.0
volumes:
- ./app:/app/app # live mount; changes are visible immediately
```
> [!warning] Not for Production
> Volume mounts bypass the baked image in production — secrets and build artifacts may differ between environments.
### Service Name Gotcha
```bash
# ❌ Silently does nothing if the service is named "api" not "backend"
docker compose restart backend
# ✅ Verify service names first
docker compose ps
docker compose restart api
```
## Related Concepts
- [[wiki/tech-patterns/fastapi-python-docker]] — FastAPI + Docker Compose deployment pattern this applies to
- [[wiki/architecture/optical-dev-server-deploy]] — optical-dev deploy workflow where build-before-up is the standard
- [[wiki/concepts/python-service-deployment-dotenv]] — full Python service deploy checklist (includes rebuild step)
## Sources
- [[daily/2026-05-01.md]] — Sessions 12:09 and 19:07: code fix applied, `docker compose restart` run, bug persisted; root cause traced to stale baked image

View file

@ -0,0 +1,90 @@
---
title: "FastAPI Response Model Silently Strips Fields Not in Schema"
aliases: [fastapi-field-strip, pydantic-response-model-missing-field, fastapi-silent-omit]
tags: [fastapi, pydantic, api, gotcha, debugging]
sources:
- "daily/2026-05-01.md"
created: 2026-05-01
updated: 2026-05-01
---
# FastAPI Response Model Silently Strips Fields Not in Schema
When a FastAPI route has a `response_model`, Pydantic serializes the return value using **only** the fields declared in that model. Any extra fields — even if they exist in the MongoDB document and are present in the Python object — are silently dropped from the JSON response. No warning is raised, no error is logged.
## Key Points
- FastAPI's `response_model` is a whitelist: fields not declared are silently excluded from the response
- The data exists in the database and in the Python layer — it never reaches the HTTP response
- Frontend receives `undefined` for the missing field and typically falls back to a default/null state, masking the bug
- **Diagnostic first step**: when API data appears "missing", check the Pydantic response model schema before investigating the database or service layer
- Fix: add the missing field to the response model, or use `response_model=None` to disable filtering (not recommended in production)
- `response_model_exclude_none=True` does not cause this — that only removes `None` values that ARE in the schema
## Details
### Minimal Reproduction
```python
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class TaskResponse(BaseModel):
id: str
title: str
# ← 'failure' and 'error' fields are NOT declared here
@app.get("/tasks/{task_id}", response_model=TaskResponse)
async def get_task(task_id: str):
# DB returns: {"id": "abc", "title": "Export", "failure": "timeout", "error": "Connection refused"}
doc = await db.tasks.find_one({"_id": task_id})
return doc
# Response JSON: {"id": "abc", "title": "Export"}
# 'failure' and 'error' are silently stripped
```
### The Fix
```python
class TaskResponse(BaseModel):
id: str
title: str
failure: str | None = None # ← add missing fields
error: str | None = None
```
### Debugging Workflow
```
Bug: frontend shows no error message for failed tasks
1. Check Pydantic response model — is 'failure' field declared? ← START HERE
→ No → add it → bug fixed
2. If field IS in model, check service layer returns it
3. If service returns it, check MongoDB document structure
4. If MongoDB has it, check field name spelling (camelCase vs snake_case)
```
> [!warning] Silent Failure Pattern
> This is one of the most deceptive FastAPI bugs: the data pipeline is fully correct, but the response contract silently discards fields. Unlike missing DB documents (which raise errors), this produces valid 200 responses with incomplete data.
### When to Use `response_model=None`
```python
# Only for debugging or internal endpoints — disables all output validation
@app.get("/debug/tasks/{task_id}", response_model=None)
async def debug_task(task_id: str):
return await db.tasks.find_one({"_id": task_id})
```
## Related Concepts
- [[wiki/concepts/pydantic-v2-alias-id-gotcha]] — another Pydantic field serialization gotcha where field names differ from JSON keys
- [[wiki/concepts/pydantic-model-dict-interface]] — Pydantic model/dict boundary issues that cause silent data loss
- [[wiki/tech-patterns/fastapi-python-docker]] — FastAPI deployment context where these issues surface
## Sources
- [[daily/2026-05-01.md]] — Session 17:13: frontend showed `undefined` for task failure reason; root cause traced to `failure` and `error` fields missing from the Pydantic `TaskResponse` model

View file

@ -0,0 +1,86 @@
---
title: "MongoDB $unwind preserveNullAndEmptyArrays Typo Breaks on 7.0"
aliases: [mongodb-unwind-typo, preserveNullAndEmpty-wrong, mongodb-28811-error]
tags: [mongodb, aggregation, python, fastapi, gotcha, version-compatibility]
sources:
- "daily/2026-05-01.md"
created: 2026-05-01
updated: 2026-05-01
---
# MongoDB `$unwind` `preserveNullAndEmptyArrays` Typo Breaks on 7.0
The `$unwind` aggregation stage option is `preserveNullAndEmptyArrays` (full word). The common typo `preserveNullAndEmpty` (truncated) was silently ignored by older MongoDB versions but is **rejected with error code 28811** in MongoDB 7.0, which treats unknown `$unwind` options as fatal errors.
## Key Points
- Correct spelling: `preserveNullAndEmptyArrays` — not `preserveNullAndEmpty`
- MongoDB 7.0 raises `OperationFailure` code **28811** for unknown `$unwind` options; older versions silently ignored them
- A pipeline that worked in development (older Mongo) breaks in production after a MongoDB upgrade
- FastAPI wraps the root exception in `anyio.EndOfStream` — the real `OperationFailure` is in the second "During handling of the above exception" block in the traceback
- Stack path to root cause: HTTP route → `service.aggregate(pipeline)``OperationFailure` code 28811
## Details
### The Typo
```python
# ❌ WRONG — silently accepted by MongoDB < 7.0, fatal on 7.0+
pipeline = [
{
"$unwind": {
"path": "$items",
"preserveNullAndEmpty": True # truncated — unknown option
}
}
]
```
```python
# ✅ CORRECT
pipeline = [
{
"$unwind": {
"path": "$items",
"preserveNullAndEmptyArrays": True
}
}
]
```
### Reading the FastAPI / anyio Traceback
When this error occurs through FastAPI, the traceback is misleading:
```
anyio.EndOfStream: ...
During handling of the above exception, another exception occurred:
...
pymongo.errors.OperationFailure: Unrecognized option to $unwind: preserveNullAndEmpty, full error: {'code': 28811, ...}
```
> [!tip] Diagnosis Rule
> When you see `anyio.EndOfStream` in a FastAPI route, always scroll to the **second** exception block — the first is just the ASGI transport wrapper. The real error code is in `OperationFailure`.
### Why It Went Unnoticed
| Environment | MongoDB Version | Behaviour |
|---|---|---|
| Local dev | 5.x / 6.x | Unknown options ignored — pipeline runs |
| Production | 7.0 | Unknown options → fatal error code 28811 |
The error only surfaced after a server-side MongoDB upgrade, making it appear to be an unrelated regression.
### Defensive Practice
When writing aggregation pipelines with multiple options, cross-reference against the [official `$unwind` docs](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unwind/) rather than relying on runtime silence. MongoDB 7.0's stricter validation is the correct behaviour.
## Related Concepts
- [[wiki/concepts/fastapi-mongodb-role-migration]] — FastAPI + MongoDB aggregation pipeline patterns
- [[wiki/concepts/mongodb-schema-validator-migration-verification]] — MongoDB 7.0 stricter validation affects schema validators too
- [[wiki/tech-patterns/fastapi-python-docker]] — FastAPI + Docker environment where version mismatches surface
## Sources
- [[daily/2026-05-01.md]] — Sessions 18:57 and 19:07: `OperationFailure` code 28811 on production after MongoDB 7.0 upgrade; local dev on older Mongo never raised the error

View file

@ -5,7 +5,7 @@ tags: [browser, html, vtt, accessibility, react, javascript]
sources:
- "daily/2026-04-29.md"
created: 2026-04-29
updated: 2026-04-29
updated: 2026-05-01
---
# Native `<track>` Element Requires Blob URL, Not `data:` URI
@ -117,6 +117,43 @@ And this is the second line.
The string must start with `WEBVTT` on the first line (no BOM, no extra whitespace before it).
## Gotchas
### `<track default>` + Custom JS Overlay = Double Captions
The `default` attribute on `<track>` tells the browser to **auto-activate** the text track, causing the browser's native caption renderer to display cues. If a custom JS overlay is also rendering captions from the same track, both renderers fire simultaneously and captions appear doubled.
```jsx
// ❌ BROKEN — native renderer + JS overlay both show captions
<track kind="subtitles" src={trackUrl} srcLang="en" default />
// ✅ CORRECT — remove 'default'; JS overlay is the sole renderer
<track kind="subtitles" src={trackUrl} srcLang="en" />
```
When using a custom caption overlay, always omit the `default` attribute and activate/read the track programmatically via the `TextTrack` API.
### VTT Cue Settings Are Not Auto-Applied to Custom Overlays
WebVTT cue settings (`line:`, `position:`, `size:`, `align:`) are instructions for the **browser's native renderer**. A custom JS overlay that reads `cue.text` does not automatically honour these settings — it receives the raw text string only.
```javascript
// cue.text = "Hello world" — no position data here
// cue.line, cue.position, cue.align ARE accessible, but the overlay
// must explicitly read them and apply CSS positioning
track.addEventListener("cuechange", () => {
const cues = track.activeCues;
for (const cue of cues) {
// Must read cue.line / cue.position and apply manually:
overlayEl.style.top = cue.line !== "auto" ? `${cue.line}%` : "90%";
overlayEl.textContent = cue.text;
}
});
```
Silently ignoring cue settings while not stripping them causes subtle positioning bugs — text appears at the default position regardless of VTT metadata.
## Related Concepts
- [[wiki/tech-patterns/python-ai-agents]] — VTT is often generated by AI models (Gemini, Whisper) in accessibility pipelines

View file

@ -0,0 +1,116 @@
---
title: "React useRef for Event Handler State — Avoid Stale useState in Pointer Events"
aliases: [react-useref-drag-state, pointer-event-stale-state, useref-event-handler]
tags: [react, hooks, useref, usestate, events, drag, testing]
sources:
- "daily/2026-05-01.md"
created: 2026-05-01
updated: 2026-05-01
---
# React `useRef` for Event Handler State — Avoid Stale `useState` in Pointer Events
React 18 `useState` setters are asynchronous — state updates are batched and do not take effect until the next render. Event handlers registered on the same element fire in the same tick, which means a `pointerMove` handler that runs immediately after `pointerDown` will read the **pre-update** (stale) state value set in `pointerDown`. The solution is to use `useRef` for flags that event handlers must read synchronously.
## Key Points
- `useState` setters are async: `setDraggingIndex(i)` in `pointerDown` is still `null` when `pointerMove` fires in the same event tick
- `useRef` holds a mutable `.current` value that is visible synchronously to all code in the same render cycle
- Use `useRef` for drag-in-progress flags, selected index tracking, and any state that event handlers must read without waiting for a re-render
- React Testing Library `fireEvent` does **not** flush state synchronously — wrap state-dependent assertions in `act()`
- `useMemo` hooks must be declared **after** all `useState` variables they reference (TypeScript TS2448: "used before declaration")
## Details
### The Stale State Bug
```tsx
// ❌ BROKEN — pointerMove sees stale null because useState is async
function DraggableList({ cues }) {
const [draggingCueIndex, setDraggingCueIndex] = useState<number | null>(null);
const handlePointerDown = (index: number) => (e: React.PointerEvent) => {
setDraggingCueIndex(index); // schedules update — NOT yet applied
};
const handlePointerMove = (e: React.PointerEvent) => {
if (draggingCueIndex === null) return; // ← always null! fires in same tick
// drag logic never executes
};
}
```
### The Fix: useRef for Synchronous Access
```tsx
// ✅ CORRECT — ref is readable immediately in the same event tick
function DraggableList({ cues }) {
const draggingCueIndexRef = useRef<number | null>(null);
const [draggingCueIndex, setDraggingCueIndex] = useState<number | null>(null);
// ↑ useState still used for re-render trigger (visual feedback)
const handlePointerDown = (index: number) => (e: React.PointerEvent) => {
draggingCueIndexRef.current = index; // synchronous, readable immediately
setDraggingCueIndex(index); // triggers re-render for UI update
e.currentTarget.setPointerCapture(e.pointerId);
};
const handlePointerMove = (e: React.PointerEvent) => {
if (draggingCueIndexRef.current === null) return; // ✓ correct value
// drag logic executes correctly
};
const handlePointerUp = () => {
draggingCueIndexRef.current = null;
setDraggingCueIndex(null);
};
}
```
### Testing: act() Requirement
```tsx
// ❌ BROKEN — assertion runs before state flush
fireEvent.pointerDown(element, { pointerId: 1 });
expect(screen.getByTestId("dragging-indicator")).toBeInTheDocument();
// Fails: state not yet applied
// ✅ CORRECT — wrap in act() to flush state updates
import { act } from "@testing-library/react";
act(() => {
fireEvent.pointerDown(element, { pointerId: 1 });
});
expect(screen.getByTestId("dragging-indicator")).toBeInTheDocument();
```
### useMemo Declaration Order (TS2448)
```tsx
// ❌ TypeScript error TS2448: Block-scoped variable 'items' used before its declaration
const sortedItems = useMemo(() => [...items].sort(), [items]); // references items
const [items, setItems] = useState<string[]>([]); // declared after
// ✅ Always declare useState before useMemo that depends on it
const [items, setItems] = useState<string[]>([]);
const sortedItems = useMemo(() => [...items].sort(), [items]);
```
### Decision Guide: useState vs useRef
| Use case | Correct hook |
|---|---|
| Value that triggers a re-render (visual state) | `useState` |
| Value read synchronously in event handlers | `useRef` |
| Value that needs both (drag index) | Both — `useRef` for sync read, `useState` for render |
| DOM node reference | `useRef` |
## Related Concepts
- [[wiki/concepts/zustand-async-hydration]] — another async state access pattern where data is not available synchronously on first render
- [[wiki/tech-patterns/react-vite-typescript]] — React + Vite + TypeScript stack where this pattern applies
- [[wiki/concepts/websocket-react-token-guard]] — React `useEffect` + async state dependency management
## Sources
- [[daily/2026-05-01.md]] — Sessions 15:31 and 19:07: drag-to-reorder feature broke because `pointerMove` saw stale `draggingCueIndex`; fixed by introducing `draggingCueIndexRef`; also discovered `useMemo` ordering TS2448 and `act()` requirement in tests

View file

@ -1,6 +1,10 @@
# Build Log
## [2026-05-05T12:00:00+01:00] compile | 2026-05-01.md
- Articles created: [[wiki/concepts/docker-compose-restart-no-code-reload]], [[wiki/concepts/mongodb-unwind-preservenullandemptyarrays]], [[wiki/concepts/fastapi-response-model-silent-field-strip]], [[wiki/concepts/react-useref-event-handler-state]]
- Articles updated: [[wiki/concepts/native-track-blob-url]]
## [2026-04-30T23:59:00+01:00] compile | 2026-04-30.md (pass 3)
- Source: daily/2026-04-30.md
- Articles created: [[wiki/concepts/react-query-enabled-falsy-value]], [[wiki/concepts/mongodb-cross-collection-id-confusion]], [[wiki/concepts/browser-sequential-download-blocking]], [[wiki/concepts/mongodb-schema-validator-migration-verification]]

View file

@ -35,6 +35,7 @@ Recurring technology stacks used across Oliver Agency projects. Each article cov
| [[wiki/tech-patterns/websocket-keepalive-terminal-close\|WebSocket Keepalive + Terminal Close Codes]] | Bidirectional 20s keepalive + terminal close codes (4001/4003/4004/4403) to prevent reconnect storms through Apache mod_proxy_wstunnel | video-accessibility, mod-comms |
| [[wiki/tech-patterns/pydantic-empty-string-coercion\|Pydantic Empty String → None Coercion]] | field_validator with mode='before' to treat "" as absent optional field — prevents 400 on CC-only or AD-only payloads | video-accessibility |
| [[wiki/tech-patterns/vtt-descriptive-transcript-regeneration\|VTT Edit → Descriptive Transcript Regeneration]] | Pattern for keeping descriptive_transcript.txt in sync when captions or AD VTTs are edited via PATCH /vtt | video-accessibility |
| [[wiki/tech-patterns/git-worktrees-parallel-claude-sessions\|Using Git Worktrees for Parallel Claude Development Sessions]] | Use git worktrees when you need to run multiple independent Claude sessions simultaneously on differ | — |
## Quick Decision Guide