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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Annual-planning Playwright coverage now boots isolated local frontend/backend servers on `127.0.0.1:3100` and `127.0.0.1:8010`, so the review-flow slice no longer depends on whatever developer servers are already running on `3000/8000`.
- Annual shock preview now groups the full repair-block blast radius around the direct absence hits, so coordinators can see which other scheduled assignments sit in the same repair block before generating or publishing repair work.
- Annual-planning readiness now evaluates effective program policy snapshots, recurring program calendar anchors, and policy-layered institutional events together, so coordinators see the full DB-backed policy stack instead of only institutional-event coverage before annual optimization or repair work.
- The annual-planning hub's policy rollover action now refreshes program policy snapshots and recurring calendar anchors alongside institutional events, keeping academic-year policy carry-forward aligned across all current DB-backed policy sources.
Expand All @@ -29,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Added current mocked browser coverage for the annual-planning review surface, so Playwright now verifies effective program policy, recurring calendar anchors, institutional events, plan provenance, repair preview, and plan history together instead of the older policy-anchor-only copy.
- Added admin rollover endpoints for `ProgramPolicySnapshot` and `ProgramCalendarAnchor`, plus annual-planning hub views for the effective program-policy snapshot and effective recurring calendar anchors for a selected academic year.
- Added a recent proving-pass report panel to the annual-planning hub so schedulers can review baseline/shock/repair drill outcomes for the selected academic year without leaving the coordinator surface.
- Added a scheduler-only annual-planner proving-pass report feed sourced from the native `docs/reports/automation/annual_proving_pass_*.json` artifacts, so recent baseline/repair drill outcomes can be surfaced in the app instead of staying trapped in local files.
Expand All @@ -38,7 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added annual-planning hub clone/delete controls for unpublished scenarios, so coordinators can branch year-level what-if plans and clean up stale drafts without dropping to raw API calls.
- Added annual-plan provenance and repair-scope details to the annual-planning hub, so coordinators can see who created, approved, or published a plan and what exact repair slice a draft is carrying before acting on it.
- Added annual-plan history timelines to the planning hub, so coordinators can review lifecycle and repair-scope events on a selected plan before approving, publishing, or repairing from it.
- Added a mocked Playwright annual-planning review-flow test that verifies policy anchors, plan provenance, and plan history render together in the coordinator hub.
- Added a mocked Playwright annual-planning review-flow test that verifies the current policy-layer review surface, plan provenance, and plan history render together in the coordinator hub.
- Annual shock preview now returns and displays the exact resident/block assignments that a selected set of approved or confirmed absences would unlock, so coordinators can review the real blast radius before creating a repair draft.
- Annual plan proving-pass coverage now includes exact resident/block repair scope, repair-plan and repair-draft publish flow, and a full scripted proving pass that validates the localized shock-repair path before coordinators use it manually.
- Added a `verify_hybrid_runtime.py` report tool plus `HybridRuntimeVerificationService` so operators can compare published hybrid outpatient template policy against actual generated HDAs, including weekly patterns, scope-adjusted targets, source breakdown, and slot-grid output for real pilot blocks.
Expand Down
5 changes: 3 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# TODO — Actionable Items

> **Updated:** 2026-03-21 (policy-layer readiness/review/rollover actualization in progress)
> **Updated:** 2026-03-21 (policy-layer and annual-planning browser-confidence actualization in progress)
> **Source:** Extracted from architecture docs, planning docs, code TODOs, and Explore agent audit.
> **Companion:** `docs/planning/ROADMAP.md` (macro vision), `docs/planning/TECHNICAL_DEBT.md` (debt tracker)
> **Cutover tracker:** `docs/planning/ROTATION_SHAPE_CUTOVER_STATUS_20260320.md` (merged vs open PR vs untouched rotation-shape/constraint cutover work)
Expand All @@ -26,6 +26,7 @@
- [x] **Faculty weekly shapes** — Primary operational blocker from `docs/reviews/FULL_STACK_AUDIT_20260320.md`. Replaced heuristic-first faculty scheduling with baseline weekly shapes, role drivers, person deltas, and week-specific overrides across merged PRs [#1412](https://github.com/Euda1mon1a/Autonomous-Assignment-Program-Manager/pull/1412) through [#1419](https://github.com/Euda1mon1a/Autonomous-Assignment-Program-Manager/pull/1419).
- [ ] **Program / regulatory / institution policy tables** — `ProgramPolicySnapshot`, `ProgramCalendarAnchor`, and policy-layered `InstitutionalEvent` tables/admin surfaces now exist, and the annual-planning hub can now assess, review, and roll all three sources together. Remaining: move the surviving higher-order policy still trapped in Python into these DB-backed layers without making ad hoc scheduling-logic changes.
- [ ] **13-block AY 26-27 draft / validate / publish workflow** — Core lifecycle is merged on `main` via [#1425](https://github.com/Euda1mon1a/Autonomous-Assignment-Program-Manager/pull/1425) through [#1428](https://github.com/Euda1mon1a/Autonomous-Assignment-Program-Manager/pull/1428). Current hardening stack is [#1452](https://github.com/Euda1mon1a/Autonomous-Assignment-Program-Manager/pull/1452) through [#1457](https://github.com/Euda1mon1a/Autonomous-Assignment-Program-Manager/pull/1457), plus [#1468](https://github.com/Euda1mon1a/Autonomous-Assignment-Program-Manager/pull/1468), [#1484](https://github.com/Euda1mon1a/Autonomous-Assignment-Program-Manager/pull/1484), and [#1485](https://github.com/Euda1mon1a/Autonomous-Assignment-Program-Manager/pull/1485): publish aliases, naive UTC timestamps, idempotent block-draft regeneration, annual optimization leave pressure, resilience-helper proving-pass fixes, and exact shock-impact assignment review. Native proving passes now reach baseline publish, repair publish, and repair-draft publish. Remaining: merge the open stack and keep tightening annual diff/review ergonomics.
- [x] **Annual-planning browser confidence** — The mocked coordinator review-flow slice now runs on dedicated Playwright frontend/backend ports (`127.0.0.1:3100` / `127.0.0.1:8010`) instead of ambient `3000/8000` dev servers, and the browser spec is aligned with the current policy-layer review surface (effective program policy, calendar anchors, institutional events, provenance, repair preview, and plan history). Remaining GUI coverage work should build on this isolated path instead of reusing whatever local servers happen to be running.
- [ ] **Shock-event model + targeted regeneration** — Initial `Absence`-driven shock preview/draft slice is merged on `main`. Current extensions are [#1457](https://github.com/Euda1mon1a/Autonomous-Assignment-Program-Manager/pull/1457), [#1468](https://github.com/Euda1mon1a/Autonomous-Assignment-Program-Manager/pull/1468), and [#1485](https://github.com/Euda1mon1a/Autonomous-Assignment-Program-Manager/pull/1485), which feed approved/confirmed `Absence` rows into annual optimization as leave requests, persist shock repair scope even when the annual rotation diff is unchanged, keep repair generation from aborting on archived combo templates, legacy encrypted absence fields, or fully absent repair residents, and expose the exact resident/block assignments affected by a shock before draft creation. This branch extends that review surface with block-scoped blast-radius context showing the full repair block alongside the direct absence hits. Remaining: finish the operator-facing repair publish flow and any downstream review/reporting gaps.

### Annual Rotation Optimizer (ARO) — Frontend UI
Expand All @@ -37,7 +38,7 @@
- [x] **ARO service layer** — create_plan → import_leave → optimize → approve → publish (PR #1276)
- [x] **ARO API routes** — REST endpoints under `/api/v1/annual-planner/plans/...` (PR #1276)
- [x] **ARO rotation mapping** — `publish_plan()` resolves rotation_template_id + conflict handling (PR #1276)
- [x] **ARO Frontend UI** — `/hub/annual-planning` exists on `main` and now covers create / optimize / validate / approve / publish, shock preview / draft, diff review, and annual workbook export. Remaining improvements live under the north-star workflow items above.
- [x] **ARO Frontend UI** — `/hub/annual-planning` exists on `main` and now covers create / optimize / validate / approve / publish, shock preview / draft, diff review, annual workbook export, policy-layer review, and browser-verified review/history surfaces. Remaining improvements live under the north-star workflow items above.
- **Doc:** `docs/architecture/ANNUAL_ROTATION_OPTIMIZER.md`

### Excel Pipeline — Stateful Roundtrip
Expand Down
56 changes: 56 additions & 0 deletions docs/planning/ANNUAL_PLANNING_BROWSER_CONFIDENCE_20260321.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Annual Planning Browser Confidence — 2026-03-21

## Objective

Make the annual-planning review-flow browser proof reliable on a real local machine without depending on ambient frontend/backend dev servers.

## Problem

The mocked annual-planning Playwright slice was reusing whatever happened to be running on `localhost:3000` and `localhost:8000`.

That was brittle for two reasons:

1. the browser flow could silently talk to a developer's unrelated Next.js session
2. admin bootstrap could fail if the reused backend was started from the wrong working directory and never loaded `backend/.env`

The concrete symptom on this machine was `POST /api/v1/auth/initialize-admin -> 500` while the database itself was already at Alembic head.

## Implementation

1. Added a shared Playwright local-dev env helper that primes dedicated defaults:
- `PLAYWRIGHT_BASE_URL=http://127.0.0.1:3100`
- `PLAYWRIGHT_API_URL=http://127.0.0.1:8010`
- `BACKEND_URL` follows the Playwright API URL unless explicitly provided
2. Updated both `frontend/playwright.config.ts` and `frontend/e2e/playwright.config.ts` to:
- launch Next.js on `3100`
- launch FastAPI on `8010`
- pass `BACKEND_URL` into the frontend server command
3. Updated `frontend/e2e/utils/local-dev-auth.ts` to use the shared Playwright API default and to spawn any fallback backend on the API URL's port instead of hardcoding `8000`.
4. Updated the annual-planning browser spec so it matches the current review surface:
- effective program policy
- effective calendar anchors
- effective institutional events
- plan provenance
- repair preview
- plan history

## Result

- The annual-planning Playwright slice no longer depends on ambient `3000/8000` developer servers.
- The old admin-bootstrap `500` is gone in the targeted browser run.
- The mocked review-flow browser proof now validates the current policy-layer UI rather than stale "policy anchors" copy.

## Validation

- `npx playwright test e2e/tests/annual-planning.spec.ts --project=chromium --grep 'Annual Planning review surfaces'`
- `npm run type-check`
- `npx eslint playwright.config.ts e2e/playwright.config.ts e2e/utils/local-dev-env.ts e2e/utils/local-dev-auth.ts e2e/tests/annual-planning.spec.ts`
- `npm test -- --watchAll=false src/app/hub/annual-planning/__tests__/AnnualPlanningHubClient.test.tsx`

## Files

- `frontend/playwright.config.ts`
- `frontend/e2e/playwright.config.ts`
- `frontend/e2e/utils/local-dev-env.ts`
- `frontend/e2e/utils/local-dev-auth.ts`
- `frontend/e2e/tests/annual-planning.spec.ts`
2 changes: 1 addition & 1 deletion docs/planning/TECHNICAL_DEBT.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ command: celery -A app.core.celery_app worker -Q default,resilience,notification
**Found:** 2026-03-04 (Codex GPT-5 full-stack assessment)
**Status:** ✅ RESOLVED (2026-03-06, branch `fix/playwright-port-conflict`)

**Resolution:** Unique ports assigned: root 3001, CI 3002, E2E 3003. Fixed hardcoded localhost:3000 in test files.
**Resolution:** Playwright now uses dedicated local frontend/backend ports for the annual-planning review slice (`127.0.0.1:3100` and `127.0.0.1:8010`) instead of ambient `3000/8000` dev servers, and the review-flow spec no longer relies on whichever backend a developer already has running.

---

Expand Down
18 changes: 12 additions & 6 deletions frontend/e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { defineConfig, devices } from '@playwright/test';

import { primePlaywrightLocalDevEnv } from './utils/local-dev-env';

const { apiUrl, baseUrl } = primePlaywrightLocalDevEnv();
const frontendPort = new URL(baseUrl).port || '3100';
const backendPort = new URL(apiUrl).port || '8010';

/**
* Playwright E2E Test Configuration
*
Expand Down Expand Up @@ -33,11 +39,11 @@ export default defineConfig({
/* Shared settings for all the projects below */
use: {
/* Base URL to use in actions like `await page.goto('/')` */
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:3000',
baseURL: baseUrl,

/* API URL for backend */
extraHTTPHeaders: {
'X-API-Base-URL': process.env.PLAYWRIGHT_API_URL || 'http://localhost:8000',
'X-API-Base-URL': apiUrl,
},

/* Collect trace when retrying the failed test */
Expand Down Expand Up @@ -156,16 +162,16 @@ export default defineConfig({
/* Run local dev server before starting tests */
webServer: process.env.CI ? undefined : [
{
command: 'npm run dev',
url: 'http://localhost:3000',
command: `BACKEND_URL=${apiUrl} npm run dev -- --hostname 127.0.0.1 --port ${frontendPort}`,
url: baseUrl,
reuseExistingServer: !process.env.CI,
timeout: 120_000,
stdout: 'ignore',
stderr: 'pipe',
},
{
command: 'cd ../backend && uvicorn app.main:app --host 0.0.0.0 --port 8000',
url: 'http://localhost:8000/health',
command: `cd ../backend && .venv/bin/python -m uvicorn app.main:app --host 127.0.0.1 --port ${backendPort}`,
url: `${apiUrl}/health`,
reuseExistingServer: !process.env.CI,
timeout: 120_000,
stdout: 'ignore',
Expand Down
83 changes: 79 additions & 4 deletions frontend/e2e/tests/annual-planning.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,14 @@ async function mockAnnualPlanningHubShell(adminPage: Page) {
},
policyAnchors: {
status: 'ready',
hasEffectiveProgramPolicy: true,
effectiveProgramPolicySource: 'academic_year',
activeEventCount: 1,
activeCalendarAnchorCount: 1,
activeAnchorCount: 2,
programAnchorCount: 1,
regulatoryAnchorCount: 0,
institutionAnchorCount: 1,
programEventCount: 0,
regulatoryEventCount: 0,
institutionEventCount: 1,
Expand Down Expand Up @@ -108,7 +115,7 @@ test.describe('Annual Planning Hub', () => {
});

test.describe('Annual Planning review surfaces', () => {
test('renders policy anchors, provenance, and history from planner APIs', async ({
test('renders policy layers, provenance, and history from planner APIs', async ({
adminPage,
}) => {
const repairPlanId = '11111111-1111-4111-8111-111111111111';
Expand Down Expand Up @@ -208,6 +215,63 @@ test.describe('Annual Planning review surfaces', () => {
},
});

await mockAPI(adminPage, '**/api/v1/program-policies/effective*', {
body: {
id: '99999999-9999-4999-8999-999999999999',
name: 'AY 2026-2027 Core Policy',
academicYear: 2026,
isDefault: false,
isActive: true,
pgy1InpatientClinicDayOfWeek: 2,
pgy1InpatientClinicTimeOfDay: 'AM',
pgy2InpatientClinicDayOfWeek: 1,
pgy2InpatientClinicTimeOfDay: 'PM',
pgy3InpatientClinicDayOfWeek: 0,
pgy3InpatientClinicTimeOfDay: 'PM',
smAcademicDayOfWeek: 2,
smAcademicTimeOfDay: 'AM',
facultyDidacticDayOfWeek: 2,
facultyDidacticTimeOfDay: 'PM',
skipFinalWeekForFacultyDidactic: true,
notes: 'Graduation month stays protected for didactics and handoffs.',
requestedAcademicYear: 2026,
source: 'academic_year',
},
});

await mockAPI(adminPage, '**/api/v1/program-calendar/effective*', {
body: {
items: [
{
id: '88888888-8888-4888-8888-888888888888',
name: 'Graduation Week',
policyLayer: 'institution',
anchorKind: 'protected_time',
anchorRule: 'fixed_date',
academicYear: 2026,
federalHoliday: null,
month: 6,
day: 20,
dayOffset: 0,
durationDays: 11,
appliesTo: 'all',
appliesToInpatient: true,
activityId: null,
timeOfDay: null,
notes: 'Protect end-of-year outprocessing',
isActive: true,
anchorDate: '2027-06-20',
effectiveStartDate: '2027-06-20',
effectiveEndDate: '2027-06-30',
affectsSchedule: true,
},
],
total: 1,
page: 1,
pageSize: 200,
},
});

await mockAPI(adminPage, '**/api/v1/institutional-events/effective*', {
body: {
items: [
Expand Down Expand Up @@ -263,19 +327,30 @@ test.describe('Annual Planning review surfaces', () => {
await expect(
adminPage.getByRole('heading', { name: 'Annual Planning & Roll-Over' }),
).toBeVisible();
await expect(adminPage.getByText('Policy Anchors for AY 2026-2027')).toBeVisible();
await expect(adminPage.getByText('Graduation Week')).toBeVisible();
await expect(adminPage.getByText('Policy Layers for AY 2026-2027')).toBeVisible();
await expect(
adminPage.getByText('Effective Program Policy', { exact: true }),
).toBeVisible();
await expect(adminPage.getByText('AY 2026-2027 Core Policy')).toBeVisible();
await expect(adminPage.getByText('AY 2026-2027 override')).toBeVisible();
await expect(adminPage.getByText('Graduation Week').first()).toBeVisible();
await expect(adminPage.getByText('Created by: plancreator')).toBeVisible();
await expect(
adminPage.getByText('Source: Published AY 26-27 Baseline (published)'),
).toBeVisible();
await expect(adminPage.getByText('Repair blocks: 5')).toBeVisible();
await expect(
adminPage.getByRole('link', { name: 'Open Events Admin' }),
adminPage.getByRole('link', { name: 'Open Events Admin' }).first(),
).toHaveAttribute(
'href',
/\/admin\/institutional-events\?active=active&policyLayer=all&academicYear=2026/,
);
await expect(
adminPage.getByRole('link', { name: 'Open Policy Admin' }),
).toHaveAttribute(
'href',
/\/admin\/program-policy\?active=active&academicYear=2026/,
);
await expect(
adminPage.getByRole('link', { name: 'Manage AY Roster' }),
).toHaveAttribute(
Expand Down
Loading