- README rewritten: Docker and native quickstarts, try-it flow, gate
commands, promote-a-stub steps.
- CONTRIBUTING rules 9-13 (pii_read on PII reads, __audit_redact__ for
encrypted columns, retention policy per table, models.py registry,
vendored-code change discipline); PR checklist updated.
- docs/nist-mapping.md: per-control file:line evidence and remaining gap.
- NIST_800_53_CONTROL_MAPPING.md v0.3: Phase 0 notes on 28 rows
(12 implemented, 12 partial, 4 progress notes on hybrid/tailored rows)
plus a status-shift table.
- PHASE0_PLAN: 0.9 complete; Definition of Done 12/14 checked with how
each was verified. Open: first green Gitea CI run, Docker/Postgres boot.
- POA&M and status handoff updated; status TL;DR no longer claims an
unverified Docker boot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
26 stub entities (every matrix entity except Registrant, User, Role,
AuditLog) get a router from framework/stub_router.py: one route per
matrix action (List, Search, Create, View, Edit, Delete, Restore,
VersionHistory). Each route runs the real RBAC check, writes an audit
row, then returns 501. Source RFP C.5.7.27.
- Registry in src/ypms/stubs.py; test fails if a matrix entity is
neither real nor stubbed.
- tests/test_stubs.py sweeps entity x role x route against the matrix
JSON: 501 + exactly one audit row when granted, 403 + none when not.
FERPA/CFSA stubs pinned closed to non-examiner roles.
- Registrant restore now gated on the matrix's Restore action (was
Edit, the vendored-factory convention); only the super-role holds it.
- Test fixtures (roles, make_user, api) lifted to conftest; one app per
session (route construction dominated runtime) and a shared password
hash for minted-token users.
138 tests passing, ruff + mypy clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Registrant model + schemas + router + migration 0003, exercising the
vendored framework end-to-end (CRUDService, RBAC, audit, version history).
- SSN app-layer encrypted (AES-256-GCM, HKDF key, column-bound AAD,
versioned format) in security/column_crypto.py; write-only on the model;
serializer exposes only ssn_masked (XXX-XX-1234). C.5.7.50 / SC-28.
- pii_read audit on GET, list (one row per page with ids), and history.
C.5.6.9 #5 / AU-14.
- Portal-only callers (YOUTH/PARENT/HOST) limited to own row via
Registrant.user_id; out-of-scope is 404. PARENT_USER sees nothing until
Guardian lands. History gated on VersionHistory, not View.
- minor_flag derived from dob at read time.
Framework changes (flag to OWC on re-vendor):
- crud._to_dict honors __audit_redact__, keeping ciphertext out of
audit_log and version_history.
- crud._to_dict serializes date (not only datetime) for JSON columns.
- CRUDService.history annotation uses builtins.list (method shadowed it).
98 tests passing, 94.83% coverage; ruff + mypy clean.
Coverage trace at docs/coverage/registrant.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>