Chris TonjesandClaude Opus 4.7 ba756586aa Phase 0 milestone 0.2: vendor OWC framework + YPMS pii_audit
Brings in a working generic-CRUD framework plus its dependencies from OWC
(../owc-system/src/owc/, snapshot 2026-04-20). Every vendored module carries
a header comment naming the source; the OWC → ypms rename is the only
mechanical change.

Vendored modules:

- framework/base.py           TimestampMixin, SoftDeleteMixin, VersionedMixin
- framework/pagination.py     Page[T] with to_dict()
- framework/version_history.py  Snapshot table
- framework/crud.py           CRUDService with audit emission
- framework/bulk.py           BulkOps (BulkResult / BulkUpdateItem)
- framework/router.py         make_router() factory (kept without
                              `from __future__ import annotations` for
                              FastAPI type introspection — see the module
                              docstring)
- audit/models.py             AuditLog
- audit/service.py            record()
- security/models.py          Permission
- security/rbac.py            can() + require_permission() dependency
                              (DOES_SYSTEM_ADMIN as the super-role)
- users/models.py             User, Role, UserRole (YPMS adds
                              mfa_verified and assurance_level; drops
                              OWC-specific claimant_id)
- auth/deps.py                get_current_user() placeholder — real JWT
                              verify lands in 0.3

YPMS-specific additions on top of the vendored framework:

- framework/pii_audit.py      emit_pii_read() — NIST AU-14 / source RFP
                              C.5.6.9 #5. OWC doesn't need this because
                              its case-management surface is staff-only;
                              YPMS must audit reads of minors' PII, SSN,
                              banking, FERPA-covered OSSE records, and
                              CFSA foster/ward flags.

Bugfix vs. upstream OWC:

- framework/crud.py           Pagination `next_cursor` was previously the
                              peek-ahead row's id, which combined with
                              `where id > cursor` on the next page
                              silently dropped one row per boundary.
                              Caught by test_list_pagination_forwards_cursor.
                              Fixed here; noted in the module header so
                              re-vendoring OWC surfaces it upstream.

Migrations:

- 0002_framework_tables.py    Baseline for users, roles, user_roles,
                              permissions, audit_log, version_history.
                              Immutable once merged per CONTRIBUTING.md.
- migrations/env.py           Imports the new model modules so
                              autogenerate compares against them.

Tests (26 passing, 93.10% coverage, floor 80%):

- tests/framework/test_crud.py         CRUDService lifecycle + snapshots
                                       + audit emission (7 tests)
- tests/framework/test_bulk.py         BulkOps happy paths + failure
                                       reporting (4 tests)
- tests/framework/test_pagination.py   Page.to_dict serializer contract
                                       (2 tests)
- tests/framework/test_pii_audit.py    emit_pii_read row shape,
                                       anonymous case (2 tests)
- tests/security/test_rbac.py          can() + admin bypass + role union
                                       (4 tests)
- tests/test_readyz.py                 /readyz smoke (1 test)
- tests/test_logging.py                sanitize_for_log contract (3 tests)

Green locally: ruff clean, mypy clean (with a targeted override on
framework/router.py and framework/crud.py for TypeVar limitations that
OWC also accepts).

Advances YPMS-POAM-040. Prepares milestone 0.3 (JWT auth) and 0.4 (RBAC
seed) to plug into an already-live framework.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-09-30 16:55:56 -04:00

YPMS — Youth Program Management System

DC Department of Employment Services / Office of Youth Programs — in-house build replacing seven legacy tools (MBSYEP intake, TMS timekeeping, PF3 admin, manual payroll scripts, MicroStrategy, and two supporting apps) with a single Python / FastAPI / Postgres system on Azure Government.

Status: Pre-development. Phase 0 (foundations) in flight. System owner + ISSO + AO: Chris Tonjes, DOES CIO. Full context: OVERVIEW.md, ARCHITECTURE.md, PHASE0_PLAN.md.

Quickstart (local)

Requires Docker Desktop (or equivalent). Ports 8000 (app), 5433 (Postgres), 8081 (Adminer) must be free.

cp .env.example .env
docker compose up --build

App at http://localhost:8000. OpenAPI docs at http://localhost:8000/docs.

curl http://localhost:8000/healthz    # → {"status":"ok"}
curl http://localhost:8000/readyz     # → {"status":"ok"} once db is up

Adminer (DB explorer) at http://localhost:8081. Connect with:

  • System: PostgreSQL
  • Server: db
  • Username / Password / Database: ypms / ypms / ypms

Stop with Ctrl+C; wipe local DB with docker compose down -v.

Tests

Local (no Docker required for the tests):

python -m venv .venv && source .venv/bin/activate
pip install -e '.[dev]'
pytest

Coverage:

pytest --cov=src/ypms --cov-report=term-missing

Tests use SQLite in-memory for speed. Postgres-specific behaviour gets its own Postgres-only fixture when a migration or query needs it (see ARCHITECTURE.md §4.8).

Repo shape

Full tree in ARCHITECTURE.md §5.1. High points:

  • src/ypms/main.py — FastAPI factory
  • src/ypms/settings.py — pydantic-settings config
  • src/ypms/db.py — SQLAlchemy engine, session, Base
  • src/ypms/logging.py — structlog + sensitive-field sanitizer
  • src/ypms/middleware.py — request id
  • src/ypms/framework/ — vendored from OWC, milestone 0.2
  • src/ypms/security/ — auth + RBAC + column encryption, milestones 0.3–0.4
  • src/ypms/youth/ — Registrant entity (vertical slice), milestone 0.5
  • Other entity packages — 501 stubs, milestone 0.6
  • migrations/ — Alembic, immutable-once-merged
  • tests/ — pytest, SQLite by default

Adding an entity

Once the framework lands in milestone 0.2, adding a new entity follows the OWC add-entity pattern (model → schemas → router → main.py registration → migration → permissions seed entry → test → coverage doc). Full instructions land here at milestone 0.2 completion.

Contributing

See CONTRIBUTING.md for the hard rules. Highlights: migrations are immutable once merged, every state-mutating route emits an audit row, framework/security modules must not carry domain logic, no --no-verify or # type: ignore without a written reason.

Compliance

License / usage

Internal DOES system. Not for external distribution.

S
Description
ypms build
Readme
497 KiB
0 Stars 1 Watchers 0 Forks
Languages
Python 99.3%
Dockerfile 0.4%
Mako 0.3%