Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Tina4 Python — Agent Instructions

v3.13.152. 140 cataloged features, zero dependencies. Python 3.12+.
v3.13.153. 140 cataloged features, zero dependencies. Python 3.12+.

## AI Skills

Expand Down
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@ https://tina4.com/python/36-releases
This file records framework-specific changes. The release notes above remain the
authority for shipped versions.

## 3.13.152 - 2026-10-20
## 3.13.153 - 2026-10-21

### Fixed
- Skills: restore the bundled AI skills that earlier releases dropped —
tina4-seo and tina4-a11y (#117), and tina4-design's stop-and-wait approval
gates (#128). No framework code change; this release re-tags the restored
skills so the install bundle and its CDN fallback mirrors carry them again,
and the bundle generator now refuses to ship a skill set smaller than the one
already published.## 3.13.152 - 2026-10-20

### Fixed
- Database: the PostgreSQL adapter recovers after a dropped connection even
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The full discipline lives in `.claude/skills/tina4-maintainer/SKILL.md`; this bl

# Tina4 Python

Version 3.13.152 - Lightweight Python web framework. See https://tina4.com for full documentation.
Version 3.13.153 - Lightweight Python web framework. See https://tina4.com for full documentation.

## Build & Test

Expand Down Expand Up @@ -943,7 +943,7 @@ uv run tina4python test # Discovers @tests in src/**/*.py
- SSE/Streaming via `response.stream()` — Server-Sent Events support for real-time data push. Pass an async generator; framework handles chunked transfer encoding, `text/event-stream` content type, and connection keep-alive
- MCP server (`tina4_python.mcp`): built-in dev tools auto-start when MCP is a capability of the deployment. Developer API: `McpServer`, `@mcp_tool`, `@mcp_resource`. JSON-RPC 2.0 over SSE. **Security is a two-layer gate (v3.13.40):** `is_enabled()` is a pure capability gate (explicit `TINA4_MCP` wins, else `TINA4_DEBUG`; host-independent), and `is_request_allowed(remote_ip, has_valid_token)` authorises each request on the RAW socket peer (`request.remote_ip`, never X-Forwarded-For): loopback always; a remote caller needs `TINA4_MCP_REMOTE=true` AND a token matching `TINA4_MCP_TOKEN` (never `TINA4_API_KEY`, ADR-0078; sent as Authorization Bearer / X-MCP-Token; no configured token means remote is always denied). Every `/__dev` request (reads too), the MCP endpoints and the `/__dev_reload` socket also require a loopback Host (`localhost`, `127.0.0.1`, `[::1]` or `TINA4_HOST`) and refuse `Sec-Fetch-Site: same-site`/`cross-site` (ADR-0078). `/health` omits `version` outside debug. All MCP surfaces (REST shim, JSON-RPC, SSE) 404 a disallowed caller. `database_query` is SELECT/WITH-only and rejects stacked statements; the file tools are sandboxed to the project root. `is_localhost()` is informational only, not the gate. **`TINA4_DEV_ALLOWED_PEERS`** (opt-in, default none) is a comma-separated IP/CIDR allow-list of RAW socket peers (v4/v6, never a forwarded header) admitted to `/__dev` - the documented way to reach the dashboard from a Docker dev box whose requests arrive from the container-network gateway, e.g. `172.16.0.0/12` (#279). `/__dev/toolbar.css` and `/__dev/toolbar.js` are static and always load; a viewer the gate would refuse gets no toolbar injected
- Tests: 6,088 passing, 0 failures, **0 skipped** — measured 2026-09-17 on the lab (Ubuntu 24.04.4 LTS x86_64, Python 3.13.13, live services, `TINA4_REQUIRE_SERVICES=1`, 604s, run as root). **Firebird is NOT excluded.** The lab provisions a live Firebird 5 (`firebirdsql/firebird:5` on :3050, `TINA4_TEST_FIREBIRD_URL`) and those tests run. What IS deliberate is Firebird's absence from the `TINA4_REQUIRE_SERVICES` keyword gate in `tests/conftest.py`: GitHub CI does not provision Firebird, so a Firebird skip has to stay green *there*. Those are two different things and this line used to conflate them into "excluded by design", which read as "the Firebird tests do not run" — they do. **Nothing skips any more.** Reaching 0 skips now also needs the graph engines (Neo4j, Memgraph, ArangoDB, Ultipa, wired via `TINA4_TEST_NEO4J_URL`/`_MEMGRAPH_URL`/`_ARANGO_URL`/`_ULTIPA_URL` + the `graph` extra) and the lab Keycloak OIDC realm (`TINA4_REQUIRE_OIDC=1`), both added after the August baseline; without them `test_graph.py` (Feature 139) and the real OIDC contract test skip. The last skip was `tests/test_session_backend_failure.py` `[needs:no-dac-override]`: the suite runs as root, root holds `CAP_DAC_OVERRIDE`, so a write went straight through a 0400 file and no real `EACCES` was reachable — and as root the second `save()` returned `True`, so that skip was hiding a FAILING assertion, not merely an unrunnable one. The test now stops being root for the length of the failing write (`os.seteuid` to `nobody`; the saved uid stays 0, so a `finally` always restores it) and the kernel raises the genuine errno-13 the test asserts. Two parts of that are load-bearing: the log directory is handed to the same uid, because dropping the uid otherwise denies `Log.error` as well and the EACCES under test goes unrecorded (`_LogWriter.write` swallows `OSError` unless `TINA4_LOG_STRICT`); and the fixture root is its own 0755 `mkdtemp` rather than `tmp_path`, whose 0700 root-owned parents make every denial a directory traversal, which would pass for the wrong reason. Re-measure with `.venv/bin/python -m pytest tests/ -q` and quote the summary line, never the exit code
- Version: 3.13.152
- Version: 3.13.153

## Links

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "tina4-python"
version = "3.13.152"
version = "3.13.153"
description = "Tina4 Python v3 — Zero-dependency, lightweight web framework"
authors = [
{name = "Andre van Zuydam", email = "andrevanzuydam@gmail.com"}
Expand Down
2 changes: 1 addition & 1 deletion tina4_python/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def _resolve_version() -> str:
#
# test_version_constant.py now asserts this literal equals the pyproject
# version, so the release bump cannot leave it behind again.
return "3.13.152"
return "3.13.153"


__version__ = _resolve_version()
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading