ci / test (push) Has been cancelled
- 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>
292 lines
25 KiB
Markdown
292 lines
25 KiB
Markdown
# YPMS — Phase 0 Plan
|
||
|
||
**Phase:** 0 — Foundations (runnable local skeleton)
|
||
**Depends on:** `ARCHITECTURE.md` v0.1, `SYSTEM_SECURITY_PLAN.md` v0.3, POA&M v0.1
|
||
**Stack:** Python 3.12 · FastAPI · SQLAlchemy 2.x · Alembic · Postgres 16 (SQLite in tests) · Docker Compose · pytest — matches OWC
|
||
**No frontend.** Phase 0 is API + minimal admin shell. Public portal is Phase 1+.
|
||
|
||
---
|
||
|
||
## 1. Phase 0 scope
|
||
|
||
Phase 0 delivers a runnable local repo that every future phase bolts onto. It does **not** implement domain logic (eligibility rules, matching, timekeeping, payroll, integrations) — that's Phase 1+.
|
||
|
||
### In scope
|
||
|
||
| Area | What ships |
|
||
|---|---|
|
||
| **Repo scaffold** | Monorepo layout matching [`ARCHITECTURE.md`](ARCHITECTURE.md) §5.1: `src/ypms/`, `tests/`, `migrations/`, `docs/`. `pyproject.toml`, `Dockerfile`, `docker-compose.yml`, `alembic.ini`, `.env.example`. `docker compose up` boots app + Postgres. |
|
||
| **Framework vendor-in** | Copy OWC `framework/` and `security/` modules into `src/ypms/framework/` and `src/ypms/security/`. Header comment names the OWC commit; if a YPMS-specific tweak is needed it stays in YPMS's copy and does not flow back. |
|
||
| **Config, logging, health** | `pydantic-settings`-based config, structured JSON logging (sensitive-field sanitizer per ARCH §10.1), `/healthz` and `/readyz`. |
|
||
| **Auth** | Local username + password (pbkdf2_sha256) + JWT. `iam.assurance_level` claim issued at authentication time (§4 of ARCH). No external IdPs in Phase 0. |
|
||
| **RBAC seed** | Data-driven permissions matrix seeded from `src/ypms/seed/permissions_matrix.json`. Roles seeded: `DOES_SYSTEM_ADMIN` (super-role, bypasses checks), `OYP_STAFF`, `ELIGIBILITY_EXAMINER`, `PAYROLL_ADMIN`, `REPORTING_ANALYST`, `HOST_USER`, `PARENT_USER`, `YOUTH_USER`. |
|
||
| **Generic Entity base** | CRUD + soft delete + optimistic locking + audit + version history + cursor pagination + bulk create/update/delete. Vendored from OWC. |
|
||
| **Audit logging** | Framework-emitted for every state change (create / update / delete / restore). Structured JSON, append-only table. Ties to NIST AU-2 / AU-3 / AU-12 and source RFP `C.5.7.39`, `C.5.7.48`, `C.5.7.49`. |
|
||
| **PII-read audit decorator** | YPMS addition: `@audit_read("pii_access")` on sensitive-record read endpoints. Ties to source RFP `C.5.6.9` #5. Exercised on the Phase 0 Registrant vertical slice. |
|
||
| **Registrant vertical slice** | The `Registrant` entity is real, end-to-end: model, schemas, router, migration, permissions, tests, coverage doc. Proves the framework works with a domain entity that has masked SSN, encrypted-at-rest banking placeholder, and PII-read audit. |
|
||
| **501 stubs for other entities** | Every entity enumerated in ARCH §6.1 (Guardian, Host, Worksite, Position, Program, Application, Enrollment, TimeEntry, Timesheet, PayrollBatch, TransportationVoucher, Attachment, Note, IntegrationEvent, Notification, EducationRecord, FosterIndicator, ConsentRecord, Cohort, EligibilityRule, JobPosting, HostContact, Certification, Orientation, TaxDocument) shipped as a 501-returning router that **still fires RBAC + audit**. Permission gaps surface at Phase 0, not Phase N. |
|
||
| **Retention scaffold** | `retention_policy` attribute declared on entity models (per ARCH §6.4). Nightly job wired but no-op in Phase 0 — enforcement lands with the entity-specific implementations in later phases. |
|
||
| **Alembic baseline** | One initial migration containing all model tables. Phase 0 baseline is `0001_baseline.py`; immutable once merged. |
|
||
| **CI-ready layout** | Gitea Actions workflow (`.gitea/workflows/ci.yml`; Gitea is the DOES CI host, decided 2026-10-01): lint (ruff), type check (mypy or pyright — match OWC), tests (pytest), SAST (bandit), SCA (pip-audit), secret scan (gitleaks), SBOM (CycloneDX). Blocks merge on failure. |
|
||
| **Test discipline** | ≥ 80% coverage on `framework/` and `security/`. Every other module needs at least one happy-path test. |
|
||
| **Docs** | `README.md` (runnable quickstart, entity-adding template), `CONTRIBUTING.md` (hard rules, mirrored from OWC), `PHASE0_PLAN.md` (this file), `docs/nist-mapping.md` (stub). |
|
||
|
||
### Out of scope (Phase 1+)
|
||
|
||
- **Any frontend.** Public portal, host portal, staff portal — all Phase 1+. The Phase 0 admin surface is FastAPI's built-in `/docs` (OpenAPI UI).
|
||
- **External IdP integration.** Entra ID (staff), Login.gov (youth/host), social-media OAuth (browsing) — all Phase 1.
|
||
- **Parental co-consent lifecycle.** Data model has a `Guardian` stub and a `ConsentRecord` stub; the workflow that binds them to an `Application` is Phase 1 alongside intake.
|
||
- **Integrations.** SSA, OSSE, CFSA, DC MAR, payroll vendor, LMS — Phase 2 or later depending on dependency.
|
||
- **Eligibility engine.** Rules seed, engine, decision workflow — Phase 2.
|
||
- **Timekeeping and payroll.** Phase 7.
|
||
- **Reporting.** MicroStrategy replacement is Phase 8.
|
||
- **Retention automation execution.** Scaffold only; enforcement per entity as entities gain real fields.
|
||
- **Row-level RBAC.** Phase 0 is "can role X do action Y on entity type Z?" Row-level ("only your caseload") is Phase 2+.
|
||
- **Real PII.** Dev and test are synthetic only. Sanitization pipelines for prod-refresh come with Phase 5 CSV-ingest work.
|
||
|
||
---
|
||
|
||
## 2. Traceability
|
||
|
||
Every Phase 0 component maps to a source-RFP requirement or a NIST control. Table is the audit trail — mirrors OWC's `PROJECT_PLAN.md` §2 pattern.
|
||
|
||
| Component | Source | Notes |
|
||
|---|---|---|
|
||
| User model + role seed | Source RFP `C.5.7.29`–`C.5.7.34`; NIST AC-2 | 8 initial roles; seeded at migration time |
|
||
| RBAC permission check | Source RFP `C.5.7.27`, `C.5.7.32`; NIST AC-3, AC-6 | Data-driven; matrix loaded from seed JSON |
|
||
| Generic CRUD framework | Source RFP `C.5.21.31`–`C.5.21.34` (define, view, comment on, view history of cases and tasks); vendored from OWC | Build once, reuse |
|
||
| Audit log | Source RFP `C.5.7.39`, `C.5.7.48`, `C.5.7.49`, `C.5.6.9` #4, #8, #11, #12, #13, #17, #18; NIST AU-2, AU-3, AU-12 | Structured JSON, append-only table |
|
||
| PII-read audit decorator | Source RFP `C.5.6.9` #5; NIST AU-14 | YPMS-specific addition above OWC's framework |
|
||
| Soft delete + version history | Source RFP implicit (case/task history retention `C.5.21.34`) | Common base mixin |
|
||
| Auth + session mgmt | Source RFP `C.5.7.1`, `C.5.7.3`–`C.5.7.14`, `C.5.7.38`; NIST IA-2, IA-4, IA-5 | Local + JWT; `iam.assurance_level` claim for future assurance gates |
|
||
| Password hashing | Source RFP `C.5.7.9`; NIST IA-5(1) | pbkdf2_sha256; FIPS 140-3 by container of the library where available |
|
||
| Config / secrets handling | NIST SC-12, SC-28, CM-6 | Env-based; `.env.example` committed, `.env` not; Key Vault comes Phase 1 |
|
||
| Registrant vertical slice | Source RFP `C.5.3.1`, `C.5.3.4` (youth application, account management); ARCH §6.1 | Proves the framework end-to-end |
|
||
| 501-stubs on remaining entities | Source RFP `C.5.7.27` (enforcement even on unimplemented routes) | RBAC + audit fire regardless |
|
||
| Alembic migration discipline | NIST CM-3, CM-4; source RFP `C.5.8.12` (impact analysis before DB change) | Migrations immutable once merged (OWC hard rule) |
|
||
|
||
---
|
||
|
||
## 3. Milestones
|
||
|
||
| # | Milestone | Exit criteria |
|
||
|---|---|---|
|
||
| **0.1** | Repo scaffold + Docker Compose boots | ✅ **Complete 2026-09-30.** Tree matches ARCH §5.1 (Phase 0 subset), `docker-compose.yml` boots app+Postgres+Adminer, `pytest` green (3 tests), ruff clean, mypy clean. Docker boot verified via config; live end-to-end verification on next docker-enabled workstation. |
|
||
| **0.2** | Framework vendor-in | ✅ **Complete 2026-09-30.** OWC framework (base, bulk, crud, pagination, router, version_history) + security (models, rbac) + audit (models, service) + users (models) + auth stub vendored with owc→ypms renames. YPMS-specific `framework/pii_audit.py` added on top. Pagination bug in upstream fixed here (peek-ahead cursor was silently dropping rows). 26 tests passing, 93.10% coverage. Migration `0002_framework_tables.py` creates the shared tables. |
|
||
| **0.3** | Auth + JWT + assurance_level | ✅ **Complete 2026-09-30.** `POST /auth/login` (OAuth2 form) returns access + refresh JWTs; access token carries `sub`, `uid`, `roles`, `iam.assurance_level`, `mfa_verified`, `iat`, `exp`, `type`. `get_current_user` verifies and rejects refresh tokens. `/auth/refresh`, `/auth/me`, `/auth/change-password` (≥12 char floor) also shipped. Login lockout after 5 IP-scoped failed attempts / 15 min per NIST AC-7. 43 tests, 93.47% coverage. |
|
||
| **0.4** | RBAC seed loaded | ✅ **Complete 2026-09-30.** 8 roles seeded from `seed/roles.py`. Permissions matrix at `seed/permissions_matrix.json` — role-first grants across 30 entities × 8 actions. `python -m ypms.seed.run` wired into docker-compose. Loader validates every cell against declared actions/entities and refuses unknown roles at seed time. 53 tests, 92.20% coverage; end-to-end `can()` decisions verified per role including admin bypass. |
|
||
| **0.5** | Registrant vertical slice | ✅ **Complete 2026-09-30.** `Registrant` model + schemas + router (`src/ypms/youth/`) + migration `0003_registrants`. SSN AES-256-GCM encrypted at the app layer (`security/column_crypto.py`), write-only on the model, serializer emits only `XXX-XX-1234`. `pii_read` audit on GET, list, and history; every state change emits an audit row with ciphertext redacted via new `__audit_redact__` framework hook. Portal-only roles restricted to own row (Phase 0 minimum; PARENT_USER blocked until Guardian). 98 tests, 94.83% coverage. Coverage doc at `docs/coverage/registrant.md`. |
|
||
| **0.6** | 501-stub routers for remaining entities | ✅ **Complete 2026-09-30.** 26 stub entities registered in `src/ypms/stubs.py`, each with a router from `framework/stub_router.py` exposing one route per matrix action (8). Every route runs the real RBAC check and writes an audit row before returning 501. `tests/test_stubs.py` sweeps entity × role × route against the matrix JSON (501 + one audit row when granted; 403 + none when not). Registrant restore moved to the matrix `Restore` action. 138 tests, 95.05% coverage. |
|
||
| **0.7** | Retention scaffold | ✅ **Complete 2026-10-01.** Every mapped table resolves a `RetentionPolicy` (entity models declare `retention_policy`; vendored framework tables mapped by name). PIA §9 interim periods encoded in `src/ypms/retention_classes.py`, all unapproved pending `YPMS-POAM-054`; classes without a PIA basis report as `unset`. Report-only job in `src/ypms/framework/retention.py` (`python -m ypms.framework.retention`) logs "0 records to purge" and writes a `retention_run` audit row. Stub entity models + migration `0004_stub_entity_tables`. Migration-vs-model drift test added. 156 tests, 95.91% coverage. |
|
||
| **0.8** | CI pipeline | 🟡 **Pipeline complete 2026-10-01; green-on-`main` pending a Gitea runner.** `.gitea/workflows/ci.yml`: ruff, mypy, pytest (80% floor, includes migration-drift test), bandit, pip-audit (runtime deps), gitleaks (pinned + sha256-verified, full history), CycloneDX SBOM artifact. Every step passes locally. Running the gates fixed a broken pip-audit invocation, removed `ecdsa` by swapping python-jose → PyJWT, and enforced a 32-byte `JWT_SECRET`. Closes when the first run on `main` is green. |
|
||
| **0.9** | Docs + acceptance | ✅ **Complete 2026-10-01.** README rewritten (Docker + native quickstart, try-it flow, gates, add-an-entity steps); CONTRIBUTING gains rules 9–13; `docs/nist-mapping.md` with file:line evidence; control mapping v0.3 (12 ✅, 12 🟡, 4 progress notes). Closing §6 gaps added: failed-login audit (AU-2), opaque 500 handler + hidden SQL params (SI-11), startup retention check (§7 risk), migration hash pinning (CM-3). Definition of Done in §8: 12 of 14 checked; remaining two need Docker and a Gitea runner. 167 tests. |
|
||
|
||
Each milestone independently mergeable.
|
||
|
||
---
|
||
|
||
## 4. Data-model sketch (Phase 0)
|
||
|
||
Minimal. Real fields on `Registrant` because it's the vertical slice; everything else is a stub with `id`, `created_at`, `updated_at`, `deleted_at`, `version` — enough to demonstrate framework mixins.
|
||
|
||
```
|
||
Registrant (real — vertical slice)
|
||
├── id
|
||
├── first_name, middle_name, last_name
|
||
├── dob
|
||
├── ssn_encrypted (app-layer encrypted; masked in serializer to XXX-XX-1234)
|
||
├── ssn_last4 (denormalized for search / display)
|
||
├── email, phone
|
||
├── address_line1, line2, city, state, zip
|
||
├── minor_flag (derived from dob at read time)
|
||
├── created_at, updated_at, deleted_at, version (from framework mixins)
|
||
|
||
User (real — needed for auth)
|
||
├── id, username, password_hash, is_active
|
||
├── roles (M2M → Role via RoleAssignment)
|
||
├── mfa_verified (Phase 0: always false; Phase 1 flips to real)
|
||
├── assurance_level ('standard' by default; upgraded by Phase 1 flows)
|
||
|
||
Role (real — seed-loaded)
|
||
├── id, name, description, is_superuser
|
||
|
||
Permission (real — seed-loaded)
|
||
├── id, entity, action
|
||
|
||
RoleAssignment (real — junction)
|
||
├── role_id, permission_id
|
||
|
||
AuditRow (real — framework)
|
||
├── id, actor_id, action, entity, entity_id
|
||
├── request_id, before (JSONB), after (JSONB), timestamp
|
||
|
||
All other entities from ARCH §6.1 (stubs — id + mixins only)
|
||
Guardian · ConsentRecord · Host · HostContact · Worksite · Position ·
|
||
JobPosting · Program · Cohort · EligibilityRule · Application ·
|
||
EligibilityDecision · Certification · Orientation · Enrollment · TimeEntry ·
|
||
Timesheet · PayrollBatch · TaxDocument · TransportationVoucher · Attachment ·
|
||
Note · IntegrationEvent · Notification · EducationRecord · FosterIndicator
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Repository layout at Phase 0 end
|
||
|
||
Matches ARCH §5.1 with only the Phase 0 subset populated:
|
||
|
||
```
|
||
ypms/
|
||
├── README.md (runnable quickstart)
|
||
├── CONTRIBUTING.md (hard rules mirrored from OWC)
|
||
├── PHASE0_PLAN.md (this file)
|
||
├── ARCHITECTURE.md (design blueprint)
|
||
├── ... (SSP, control mapping, POA&M, PIA, TODO, CLAUDE.md, OVERVIEW.md)
|
||
├── Dockerfile
|
||
├── docker-compose.yml
|
||
├── pyproject.toml
|
||
├── alembic.ini
|
||
├── .env.example
|
||
├── .gitea/workflows/ci.yml
|
||
├── migrations/versions/0001_baseline.py
|
||
├── src/ypms/
|
||
│ ├── __init__.py
|
||
│ ├── main.py (FastAPI factory + router registration)
|
||
│ ├── settings.py (pydantic-settings)
|
||
│ ├── logging.py (structured JSON + sensitive-field sanitizer)
|
||
│ ├── db.py (engine, session, base)
|
||
│ ├── deps.py (FastAPI deps)
|
||
│ ├── framework/ (vendored from OWC)
|
||
│ │ ├── __init__.py
|
||
│ │ ├── entity.py (base + mixins)
|
||
│ │ ├── crud.py (CRUDService with audit emission)
|
||
│ │ ├── router.py (make_router factory)
|
||
│ │ ├── audit.py (framework audit emitter)
|
||
│ │ ├── pii_audit.py (YPMS addition — @audit_read decorator)
|
||
│ │ ├── pagination.py
|
||
│ │ ├── retention.py (scaffold; no-op in Phase 0)
|
||
│ │ └── seed_loader.py
|
||
│ ├── security/ (vendored + YPMS additions)
|
||
│ │ ├── __init__.py
|
||
│ │ ├── auth.py (JWT create/verify with iam.assurance_level)
|
||
│ │ ├── passwords.py (pbkdf2_sha256)
|
||
│ │ ├── rbac.py (can/require_permission decorators)
|
||
│ │ └── encryption.py (app-layer column encryption helpers)
|
||
│ ├── seed/
|
||
│ │ └── permissions_matrix.json
|
||
│ ├── youth/ (Registrant — real)
|
||
│ │ ├── __init__.py
|
||
│ │ ├── models.py
|
||
│ │ ├── schemas.py
|
||
│ │ ├── router.py
|
||
│ │ └── service.py (thin service using CRUDService)
|
||
│ ├── parent/ (Guardian — stub)
|
||
│ ├── host/ (Host, HostContact, Worksite, Position, JobPosting — all stubs)
|
||
│ ├── program/ (Program, Cohort, EligibilityRule — stubs)
|
||
│ ├── application/ (Application, EligibilityDecision, Certification, ConsentRecord — stubs)
|
||
│ ├── enrollment/ (Enrollment — stub)
|
||
│ ├── orientation/ (Orientation — stub)
|
||
│ ├── timekeeping/ (TimeEntry, Timesheet — stubs)
|
||
│ ├── payroll/ (PayrollBatch, TaxDocument, TransportationVoucher — stubs)
|
||
│ ├── notifications/ (Notification — stub)
|
||
│ ├── integrations/ (IntegrationEvent — stub; no adapter modules yet)
|
||
│ ├── attachments/ (Attachment, Note — stubs)
|
||
│ ├── education_records/ (EducationRecord — stub; FERPA-scoped)
|
||
│ ├── foster_indicators/ (FosterIndicator — stub; Title 4-scoped)
|
||
│ └── admin/ (User, Role, Permission — real; system-admin endpoints)
|
||
├── tests/
|
||
│ ├── framework/ (vendored tests + YPMS additions for pii_audit)
|
||
│ ├── security/ (auth, rbac, encryption)
|
||
│ ├── youth/ (Registrant e2e)
|
||
│ ├── stubs/ (one test per stub entity: RBAC + audit fire on 501)
|
||
│ └── conftest.py
|
||
└── docs/
|
||
├── nist-mapping.md (skeleton — populated per phase)
|
||
├── coverage/
|
||
│ └── registrant.md
|
||
└── compliance/ (mirrors OWC docs/compliance/ once real controls have code)
|
||
```
|
||
|
||
---
|
||
|
||
## 6. NIST controls that get real (non-stub) code in Phase 0
|
||
|
||
Following OWC's pattern of only wiring controls that code actually implements. Others stay as 📝 in the control mapping until later phases.
|
||
|
||
| Control | Implementation |
|
||
|---|---|
|
||
| **AC-2** | User model, role-based accounts, admin can enable/disable |
|
||
| **AC-3** | `require_permission` decorator on every mutating route; 501 stubs still enforce |
|
||
| **AC-6** | RBAC via permissions matrix; `DOES_SYSTEM_ADMIN` super-role explicit |
|
||
| **AC-7** | 5-failure lockout — Phase 0 targets the check; actual lockout with `locked_until` field lands here |
|
||
| **AC-11 / AC-12** | JWT expiry (short-lived access); refresh token flow lands Phase 1 |
|
||
| **AU-2 / AU-3 / AU-12** | Framework audit emission on every state change; `AuditRow` model + emission tests |
|
||
| **AU-8** | UTC-normalized timestamps everywhere; Postgres timezone set to UTC in Docker Compose |
|
||
| **AU-14** | PII-read audit decorator on Registrant read endpoints |
|
||
| **CM-3** | Alembic migrations immutable once merged (repo policy + CI check) |
|
||
| **IA-2** | Authentication required for every non-public route |
|
||
| **IA-4** | Unique usernames; no re-issue on delete (deleted users tombstoned, username reserved) |
|
||
| **IA-5** | Password change endpoint; pbkdf2_sha256 |
|
||
| **IA-5(1)** | FIPS-approved password hashing |
|
||
| **SC-8** | Dev/test uses TLS via reverse-proxy in compose (self-signed OK for local); production TLS is deploy-phase |
|
||
| **SC-12 / SC-13 / SC-28** | App-layer column encryption for `ssn_encrypted` and banking placeholder using cryptography-library primitives; Key Vault swap-in is Phase 1 |
|
||
| **SI-10** | Pydantic schema validation on all inbound requests |
|
||
| **SI-11** | Non-exploitable error responses (custom exception handler) |
|
||
|
||
Everything else in the control mapping stays 📝 until later phases wire it.
|
||
|
||
---
|
||
|
||
## 7. Risks
|
||
|
||
| Risk | Mitigation |
|
||
|---|---|
|
||
| Vendoring OWC's framework introduces divergence over time | Header-comment naming the OWC commit; document the vendored-code policy in CONTRIBUTING. Pulling upstream OWC changes is a deliberate act, not an ambient sync. |
|
||
| Data-driven RBAC seed drifts from code expectations | Seed loaded at startup with schema validation; missing entity or action fails startup rather than silently ignoring. |
|
||
| 501 stubs mask real permission gaps | Every stub is tested to fire RBAC + audit; gap surfaces in tests, not later. |
|
||
| App-layer encryption in Phase 0 uses local key material | `.env` value in dev/test only; documented; Phase 1 swap to Key Vault is a single-file change with the encryption helper interface preserved. Local keys never used in staging or prod. |
|
||
| Vertical slice on Registrant hides framework issues for other entity shapes | 501 stubs still exercise the framework at the RBAC + audit level for every entity, catching factory-parameter mistakes. |
|
||
| Alembic baseline captures wrong shape and every future migration inherits the mistake | Baseline reviewed against ARCH §6 by CIO before first merge; every downstream migration is additive, so wrong shapes are correctable via new migration (never edit baseline). |
|
||
| CI pipeline slow enough to block iteration | Test suite must stay under 60s (SQLite fixtures). Framework tests get priority; slow integration-style tests deferred to a nightly workflow. |
|
||
| Retention scaffold gets forgotten and never enforces | Every entity model asserts a `retention_policy` in a startup check; entity without one fails startup. Enforcement per class lands later, but the scaffold is unforgettable. |
|
||
|
||
---
|
||
|
||
## 8. Definition of done for Phase 0
|
||
|
||
Phase 0 is complete when:
|
||
|
||
- [ ] `docker compose up` boots app + Postgres cleanly; `/healthz` returns 200. — **Open:** no Docker on the verifying workstation. Native boot (uvicorn + SQLite) verified; `/healthz` and `/readyz` 200.
|
||
- [x] `alembic upgrade head` applies the baseline migration. — All four revisions, SQLite; also exercised by the drift test on every CI run. Postgres run rides with the Docker item.
|
||
- [x] `POST /auth/login` issues a JWT with `iam.assurance_level` claim. (verified 2026-10-01: clean clone, live uvicorn, HTTP)
|
||
- [x] `POST /registrants` creates a Registrant with masked SSN in the response, emits an audit row, requires `OYP_STAFF` or higher. (verified 2026-10-01: clean clone, live uvicorn, HTTP)
|
||
- [x] `GET /registrants/{id}` reads the record, fires the PII-read audit decorator, masks SSN, requires appropriate role. (verified 2026-10-01: clean clone, live uvicorn, HTTP)
|
||
- [x] `PATCH /registrants/{id}` updates fields, emits before/after in audit row, requires appropriate role. (verified 2026-10-01: clean clone, live uvicorn, HTTP)
|
||
- [x] `DELETE /registrants/{id}` soft-deletes, emits audit, requires appropriate role. (verified 2026-10-01: clean clone, live uvicorn, HTTP) Only the super-role holds `Delete` on Registrant today (matrix review item).
|
||
- [x] Every stub entity's endpoints return 501 but still emit an audit row and enforce RBAC. — `tests/test_stubs.py` sweeps 26 entities × 8 roles × 8 routes; also seen live.
|
||
- [ ] Tests green in CI: lint, types, pytest, SAST, SCA, secret scan, SBOM. — **Open:** every gate passes locally; Gitea runs queue until an `act_runner` is registered.
|
||
- [x] Coverage ≥ 80% on `framework/` and `security/`; every stub has one test. — ~96% overall; `framework/router.py` still excluded (first real caller arrives with a domain entity).
|
||
- [x] `README.md` quickstart works on a clean checkout. — Native path verified from a fresh clone; Docker path rides with the Docker item.
|
||
- [x] `CONTRIBUTING.md` published with hard rules mirrored from OWC (plus YPMS rules 9–13).
|
||
- [x] `docs/nist-mapping.md` lists the controls wired above, with file:line evidence.
|
||
- [x] Control mapping `NIST_800_53_CONTROL_MAPPING.md` updated: the Phase-0-wired controls flip from 📝 to ✅ (or 🟡 for partials); file:line evidence populated. — v0.3: 12 ✅, 12 🟡; SC-8 stays ⚠️ (TLS is deployment-phase).
|
||
|
||
---
|
||
|
||
## 9. What I need from you
|
||
|
||
1. **Match OWC's Azure Gov deployment target?** OWC currently runs Docker locally; its Azure Gov deployment is Phase 19/20. YPMS Phase 0 is local-only too. Confirm we're **not** provisioning Azure Gov in Phase 0 — Azure comes with Phase 1 (Key Vault swap-in) or later.
|
||
2. ~~**Match OWC's CI provider?**~~ **Resolved 2026-10-01:** Gitea Actions — Gitea is where DOES CI runs. OWC has no CI yet; it should follow.
|
||
3. **8 initial roles OK, or want a different seed set?** Current default: `DOES_SYSTEM_ADMIN`, `OYP_STAFF`, `ELIGIBILITY_EXAMINER`, `PAYROLL_ADMIN`, `REPORTING_ANALYST`, `HOST_USER`, `PARENT_USER`, `YOUTH_USER`. Missing roles surface fast at Phase 1; adding one later is a seed update.
|
||
4. **First entity as Registrant, or something smaller?** Registrant is the natural vertical slice because everything else hangs off it. Smaller options: `Program` (mostly reference data) or `HostContact` (simple). I lean Registrant because the encryption + masking + PII-read audit path is what Phase 0 needs to prove.
|
||
5. **Test-runner + type-checker to match OWC?** Confirm: pytest for tests, and pick mypy or pyright to match OWC.
|
||
|
||
If you just say "proceed" I'll:
|
||
|
||
- Start with milestone 0.1 (repo scaffold + Docker Compose),
|
||
- Default Gitea Actions (resolved), mypy, the 8 roles above, Registrant as the slice, and no Azure Gov in Phase 0,
|
||
- Merge each milestone independently as it lands.
|
||
|
||
---
|
||
|
||
**End of draft v0.1.**
|