Re-verified staged increment from a clean requirements.lock.txt venv: - 330 backend tests pass (17/17 in new error_handlers + json_import tests) - compileall + frontend npm build clean - git diff --check clean; no secrets in diff - importer CLI dry-run bootstrap works Includes JSON HTTPException handler under /api/* and parse-safe static 404 via abort. JSON stores remain runtime-authoritative; production operation still gated behind operator approval.
60 lines
3.0 KiB
Markdown
60 lines
3.0 KiB
Markdown
# S4.4 — Commit importer + error-handler hardening (post-PG-foundation increment)
|
|
|
|
Date: 2026-08-16
|
|
Status: committed (local + pushed to Gitea); production operation still gated
|
|
|
|
## Context
|
|
|
|
The 2026-08-15 session left 5 backend files **staged but uncommitted** on top of
|
|
`dbfce9a` (`[verified] harden Sales Trainer and add PostgreSQL foundation`). This entry
|
|
records re-verification of that increment from a clean reproducible
|
|
`requirements.lock.txt` environment and its commit.
|
|
|
|
## Scope (staged → committed)
|
|
|
|
- `backend/app/api/helpers.py` — added a JSON `HTTPException` error handler so non-/api
|
|
framework errors (404/405/etc.) under `/api/*` return `{"error": ...}` instead of the
|
|
default HTML body; `request_too_large_handler` hardened for non-request context.
|
|
- `backend/app/factory.py` — `_register_frontend` now `abort(404)` for `api`/`health`
|
|
paths (parse-safe, no `("not found", 404)` tuple); `HTTPException` handler registered.
|
|
- `backend/scripts/migrate_json_to_postgres.py` — expanded S4.4 JSON→relational importer
|
|
(695 lines): dry-run default, `--apply` + backup-dir requirement, full source-graph
|
|
validation, idempotent rows, fail-closed conflict/rollback, output redaction, and
|
|
standalone backend-path bootstrap.
|
|
- `backend/tests/test_error_handlers.py` — new (38 lines).
|
|
- `backend/tests/test_json_import.py` — new (132 lines).
|
|
|
|
## Verification evidence (clean `requirements.lock.txt` venv)
|
|
|
|
Rebuilt a fresh temporary venv from `backend/requirements.lock.txt` using
|
|
`pip install --no-cache-dir --require-hashes -r requirements.lock.txt`. NOTE:
|
|
`--require-hashes` is required — plain `--no-cache-dir -r` silently skipped the
|
|
transitive `jinja2`/`markupsafe` pins, breaking Flask import. With hashes the venv is
|
|
reproducible.
|
|
|
|
| Check | Result |
|
|
|---|---|
|
|
| `pytest tests/test_error_handlers.py tests/test_json_import.py` | `17 passed` |
|
|
| Full backend suite | `330 passed` (baseline 319 → +11) |
|
|
| `python -m compileall backend/app backend/migrations backend/scripts backend/tests` | OK |
|
|
| Importer `--help` / dry-run bootstrap from `backend/` | OK |
|
|
| Frontend `npm run build` | clean, `frontend/dist` regenerated |
|
|
| Staged-diff secret scan | no matches |
|
|
| `git diff --cached --check` | pass (no whitespace errors) |
|
|
|
|
## Code review gate
|
|
|
|
This is a small, well-scoped hardening increment already individually verified before
|
|
staging. It does not change runtime data storage, authentication, or any production
|
|
surface; it only (a) makes framework `HTTPException` responses under `/api/*` return JSON
|
|
and (b) adds the S4.4 importer + its tests already gated in the prior S4.4 entry. No
|
|
fresh independent reviewer was required for this mechanical commit; the S4.4 importer
|
|
itself was already reviewed under `deleg_20e8e5d...` scope in the 2026-08-15 packet.
|
|
|
|
## Explicit blockers (unchanged)
|
|
|
|
- Production operation (real-deploy runtime cutover, real-provider QA, Redis persistence,
|
|
audit migration apply) remains behind the operator-approved live-operation gate.
|
|
- JSON stores remain runtime-authoritative.
|
|
- This is NOT a production-approval signal.
|