Ir al contenido

Plan de Migración (Coveris-e → main)

Task list for the main worktree terminal. Migrate file-by-file from Coveris-e into main in well-curated, atomic phases. Do NOT git merge. Use git checkout Coveris-e -- <paths> per phase, test, commit cleanly, move on.

  • Source branch: Coveris-e (HEAD f371f20, 13 commits ahead of origin/Coveris-e, never pushed)
  • Target branch: main
  • Background reading (in Coveris-e worktree):
    • docs/INTEGRATION-HANDOFF.md (situation summary + 3 blockers)
    • docs/volatile-docs/00-08-*.md (full analysis archive — NOT to be migrated)
    • docs/plans/anesthesia-rotation-mock-data.md (was the plan for C0 below — NOT to be migrated)
    • docs/volatile-docs/04-branch-comparison.md line-by-line file diff
    • docs/volatile-docs/05-merge-readiness.md blockers detail
  • Never git merge Coveris-e or git cherry-pick. Only git checkout Coveris-e -- <path>.
  • Never bring docs/volatile-docs/, docs/plans/anesthesia-rotation-mock-data.md, frontend/design_handoff_scheduling_redesign/.
  • Never commit .env (it is gitignored — verify).
  • Never hardcode SUPERUSER_PASSWORD in any doc; use ${SUPERUSER_PASSWORD} or “(see .env)”.
  • ✅ One phase = one PR-grade commit on main. Conventional message + Co-Authored-By trailer.
  • ✅ Each phase: tests must pass before committing. Migrations must apply on an empty DB.
  • ✅ Update CHANGELOG.md once per phase.
  • ✅ If a phase needs new ADRs to make sense, do A0 first.

Tier A — Spec & contract (no runtime code)

Sección titulada «Tier A — Spec & contract (no runtime code)»
  • git checkout Coveris-e -- docs/adr/adr-025-*.md docs/adr/adr-026-*.md docs/adr/adr-027-*.md docs/adr/adr-028-*.md docs/adr/adr-029-*.md docs/adr/adr-030-*.md docs/adr/adr-031-*.md docs/adr/adr-032-*.md docs/adr/adr-034-*.md
  • Verify file list with ls docs/adr/
  • git checkout Coveris-e -- docs/adr/README.md (index)
  • Verify ADR-029 status = Accepted (per ADR-029 promotion in Coveris-e); if not, edit by hand
  • Commit: docs(adr): add ADRs 025-034 (weekly structure, scheduling, slot anchoring)

[ ] A1 — PRD.md + API.md + api-conventions (resolve blockers B1, B2)

Sección titulada «[ ] A1 — PRD.md + API.md + api-conventions (resolve blockers B1, B2)»
  • B1: Edit PRD.md §3 line ~44 to mention Fase G (scheduling, exceptions, PDF, calendar); §8.4 mark “¿Cuándo entra programación de turnos?” as resolved by ADR-029.
  • B2: git checkout Coveris-e -- API.md (brings full updated API.md including PATCH /api/v1/assignments/{id}/slot_index/).
  • Verify API.md footer “Last Updated” matches today’s date; verify Scope line includes Scheduling + Holidays + AssignmentExceptions + ShiftInstances.
  • git checkout Coveris-e -- docs/api-conventions.md (if changed).
  • git checkout Coveris-e -- docs/07-drf-endpoints.md docs/08-primeng.md docs/09-tailwind4.md (only if these changed since main).
  • Commit: docs: PRD §3+§8.4, API.md scheduling endpoints, conventions (resolve B1+B2)

[ ] B0 — demand/ foundation: OrgUnit depth + manager_position

Sección titulada «[ ] B0 — demand/ foundation: OrgUnit depth + manager_position»
  • git checkout Coveris-e -- backend/apps/demand/enums.py backend/apps/demand/models.py
  • git checkout Coveris-e -- backend/apps/demand/migrations/0003_add_org_level.py through 0010_alter_orgunit_manager_position_and_more.py
  • git checkout Coveris-e -- backend/apps/demand/management/commands/seed_org_units.py
  • git checkout Coveris-e -- backend/apps/demand/tests/test_orgunit_depth.py backend/apps/demand/tests/test_orgunit_manager.py (whatever exists)
  • Inspect serializers/views for breaking changes; bring backend/apps/demand/serializers.py and backend/apps/demand/views.py only if needed
  • cd backend && pytest apps/demand/tests/ -v → all green
  • docker compose down -v && docker compose up -d → migrations apply from empty DB
  • Commit: feat(demand): fixed-depth OrgUnit taxonomy + manager_position (ADR-027)
  • git checkout Coveris-e -- backend/apps/tags/
  • git checkout Coveris-e -- backend/apps/business_rules/
  • git checkout Coveris-e -- backend/apps/tags/management/commands/seed_tags.py
  • Run pytest apps/tags/tests/ apps/business_rules/tests/
  • Commit: feat(tags,business_rules): regime tags + hours_source rules (ADR-028)

[ ] B2 — api/ (Employee) + fsm/ + users/ (minor changes)

Sección titulada «[ ] B2 — api/ (Employee) + fsm/ + users/ (minor changes)»
  • Diff first: git diff Coveris-e..main -- backend/apps/api/ backend/apps/fsm/ backend/apps/users/ — bring only what changed
  • Bring updated seed_anesthesia_nomina.py, seed_therapy_nomina.py, seed_mock_users.py
  • Run pytest apps/api/tests/ apps/fsm/tests/ apps/users/tests/
  • Commit: feat(api,fsm,users): Employee FSM updates + seed commands

[ ] B3 — assignments/ core (without slot_index — that’s B7)

Sección titulada «[ ] B3 — assignments/ core (without slot_index — that’s B7)»
  • git checkout Coveris-e -- backend/apps/assignments/enums.py backend/apps/assignments/models.py backend/apps/assignments/services.py backend/apps/assignments/serializers.py backend/apps/assignments/views.py
  • Migrations 0003-0006: git checkout Coveris-e -- backend/apps/assignments/migrations/0003_*.py 0004_*.py 0005_*.py 0006_*.py
  • Tests: git checkout Coveris-e -- backend/apps/assignments/tests/
  • Skip migration 0007_assignment_slot_index.py for now (B7)
  • Inspect models.py and manually remove the slot_index field for this phase (it lands in B7). Same with any service code that uses it.
  • Run pytest apps/assignments/tests/
  • Commit: feat(assignments): hours_source + effective_weekly_hours + recurrence_rule (ADR-028, ADR-029 partial)
  • git checkout Coveris-e -- backend/apps/weekly_structure/ (everything)
  • Migrations 0001-0012 all of them
  • Run pytest apps/weekly_structure/tests/
  • Verify reconciler: create board → placements → reconcile → check Position.source_subrow_key shape
  • Commit: feat(weekly_structure): PositionTemplate, WeeklyBoard, Placement + reconciler (ADR-025, ADR-026, ADR-031, ADR-032)

[ ] B5 — demand/ Position scheduling fields (ADR-030)

Sección titulada «[ ] B5 — demand/ Position scheduling fields (ADR-030)»
  • git checkout Coveris-e -- backend/apps/demand/migrations/0013_position_time_range.py 0014_position_rotates_weekly.py 0015_position_pinned_slot_indexes.py 0016_remove_position_pos_time_range_coupled_and_more.py
  • Re-apply backend/apps/demand/models.py (Position now has start_time, end_time, pinned_slot_indexes) — already brought in B0, but verify the post-merge state matches Coveris-e
  • Update backend/apps/demand/serializers.py and views.py for new fields
  • Tests for time-range validation, overnight wrap, ADR-032 pinned indexes
  • Run pytest apps/demand/tests/
  • Commit: feat(demand): Position start_time/end_time + pinned_slot_indexes (ADR-030, ADR-032)

[ ] B6 — calendar_api/ (Holidays AR cache)

Sección titulada «[ ] B6 — calendar_api/ (Holidays AR cache)»
  • git checkout Coveris-e -- backend/apps/calendar_api/
  • Migration 0001
  • Run pytest apps/calendar_api/tests/
  • Smoke: curl http://localhost:8000/api/v1/holidays/?country=AR&year=2026
  • Commit: feat(calendar_api): holiday cache for AR (ADR-029 Q12)

[ ] B7 — scheduling/ Fase G + assignments.slot_index

Sección titulada «[ ] B7 — scheduling/ Fase G + assignments.slot_index»
  • git checkout Coveris-e -- backend/apps/scheduling/ (full app)
  • git checkout Coveris-e -- backend/apps/assignments/migrations/0007_assignment_slot_index.py
  • Restore Assignment.slot_index field in backend/apps/assignments/models.py (was held back in B3)
  • Restore PATCH endpoint for slot_index in assignments/views.py
  • Run pytest apps/scheduling/tests/ apps/assignments/tests/
  • Smoke: curl /api/v1/scheduling/<org_unit_id>/2026/5/ returns nested structure
  • Commit: feat(scheduling): monthly projection + ShiftInstance + AssignmentException + PATCH slot_index (ADR-029, ADR-034)

[ ] C0 — Mock data infra + anesthesia rotation

Sección titulada «[ ] C0 — Mock data infra + anesthesia rotation»
  • git checkout Coveris-e -- mock-data/scripts/ (generators + validator)
  • git checkout Coveris-e -- mock-data/notti-anesthesia/ (7 YAMLs)
  • git checkout Coveris-e -- backend/apps/scheduling/management/commands/seed_anesthesia_rotation.py backend/apps/scheduling/management/__init__.py backend/apps/scheduling/management/commands/__init__.py
  • Decide: does main keep its existing mock-data/clinic-bienestar/? If yes, leave it. If no, drop in favor of notti-anesthesia.
  • git checkout Coveris-e -- docker-compose.yml — pulls the ./mock-data:/mock-data:ro mount line
  • git checkout Coveris-e -- backend/entrypoint.sh (if changed)
  • Smoke: docker compose down -v && docker compose up -d && docker compose exec api python manage.py seed_anesthesia_rotation
  • Commit: feat(mock): notti-anesthesia rotation scenario + YAML SSOT scripts
  • Already merged into B0 path via backend/apps/demand/views.py. If not picked up there, cherry-pick manually:
    total_required = sum(d["required_hours"] for d in unit_data.values())
    total_assigned = sum(d["assigned_hours"] for d in unit_data.values())
    total_required = sum((d["required_hours"] for d in unit_data.values()), Decimal("0"))
    total_assigned = sum((d["assigned_hours"] for d in unit_data.values()), Decimal("0"))
  • Verify with empty DB: curl /api/v1/demand/coverage-summary/ returns 200 with "0.00"
  • Commit (only if standalone): fix(demand): coverage-summary 500 on empty positions

Tier D — Frontend (out of scope for this sprint)

Sección titulada «Tier D — Frontend (out of scope for this sprint)»

Backend-first per user’s directive. Frontend migration is a separate plan to be drafted once B0–B7 + C0 land cleanly on main. Anticipated sub-phases:

  • D0 org-chart improvements (manager dialog, worker drawer)
  • D1 roster dialogs (assignment, contract, tag-assignment)
  • D2 structure component + tabs + filters
  • D3 weekly-structure (19 files + WeeklyStructureStore 818 lines)
  • D4 scheduling-month-grid (1142 lines)
  • D5 staffing-plan
  • D6 coverage page polish
  • D7 help system (5 components)
  • D8 theme/design tokens diff (only if changed)
  • D9 NG8113 cleanup + minor lint warnings

Do not start D0 until all of A0..C0 are green and CHANGELOG reflects them.


Ventana de terminal
# 1. tests
cd backend && pytest apps/<app>/tests/ -v
# 2. clean migrations from empty DB
docker compose down -v
docker compose up -d
# wait for health: curl http://localhost:8000/api/health/
# 3. lint (optional but recommended)
cd backend && ruff check apps/<app>/
# 4. CHANGELOG
echo "- <date> feat(<scope>): <one-line summary>" >> CHANGELOG.md
# 5. commit
git add -A
git commit -m "<conventional message>" \
-m "Co-Authored-By: ..."
  • Push de main SOLO con consentimiento explícito del usuario.
  • Push de Coveris-e ya fue autorizado en la sesión pasada (último push de archive).
  • Toda otra rama → local-first.

Estado al cierre de la sesión que armó este plan

Sección titulada «Estado al cierre de la sesión que armó este plan»
  • Coveris-e HEAD: f371f20 (luinloder integrado, mock data anestesia, handoff doc)
  • main HEAD: fdf0a90 (intacto)
  • Worktrees activos:
    • /Dev/Coveris/cotton-coveris-mvp → Coveris-e
    • /Dev/Coveris/cotton-coveris-mvp-main → main (este archivo vive acá)

INTEGRATION FINDINGS — ADR review + models/API diff (2026-05-31)

Sección titulada «INTEGRATION FINDINGS — ADR review + models/API diff (2026-05-31)»

Read-only assessment by the coveris-adr-review Agent Team (3 Opus business-logic experts) + direct diff. Source: Coveris-e @ 6bfd1bd · Target: coveris-b-integration @ 8c6f479 · merge-base ed3b4c8. ⚠️ Per AGENTS.md, ADR integration is HUMAN-IN-THE-LOOP. Nothing below is adopted until the user picks. The team assesses; the user decides.

  • Coveris-e adds 9 net-new ADRs: 025, 026, 027, 028, 029, 030, 031, 032, 034 (there is no 033).
  • Drift on shared ADRs is mostly cosmetic / a downgrade — keep OURS:
    • Coveris-e lacks our adr-000-adr-conventions.md and adr.base.
    • Coveris-e still carries the monolithic adr-001.md (we split it into 001-a/b/c).
    • Its adr-019 differs by ~75 lines but the change is cosmetic only (stripped YAML frontmatter + wikilinks, heading style) — a downgrade for our vault tooling, zero semantic change. ADR-028 does not depend on it. → Keep our adr-019.
New ADREffect on our spec
ADR-027 (Accepted)Supersedes ADR-026 (already marked Superseded) and supersedes our ADR-018 (UNIT-only positions). Reshapes the nucleus: drops OrgUnitType enum (CLINIC/DEPARTMENT/SERVICE/UNIT) → depth 0-4 (CLINICA/AREA/DEPTO/SERVICIO/UNIDAD, adds AREA = 5 levels); Positions allowed at any depth; manager slot via OrgUnit.manager_position OneToOne. Rewrites ADR-001-a OrgUnit rows + the whole org-units/coverage API. Spanish enum names (CLINICA/AREA/DEPTO) are a possible ADR-001-b (English-in-code) snag.
ADR-025 (Accepted)Its core “Semanal isolated, no Position materialization, four models incl. WeeklyTier” is already reversed by 026/027 — auto-sync reconciler materializes Positions per-day; WeeklyTier is dropped (confirmed: weekly_structure/models.py has no WeeklyTier). 025 stands only as a historical record.
ADR-026 (Superseded by 027)Bring in as a Superseded record for completeness only — no active force.
ADR-028 (Accepted)Silently contradicts our ADR-024 (effective_hours == required_weekly_hours + binary is_reinforcement) and extends ADR-001-c (adds violet PRESTACION band, redefines “blue”) without amending either. Coupled edits to 024 + 001-c are mandatory before adoption.
ADR-029 (Accepted)Monthly scheduling LAYERS ON ADR-011 weekly-only (011 pre-authorizes “monthly = read-time aggregation”). No contradiction; ADR-011 stays Accepted/untouched. Caveat: keep ShiftInstance.planned_hours / AssignmentException.hours_delta projection-only — never feeding the weekly ledger.

3. Per-ADR verdict (team recommendation — user picks)

Sección titulada «3. Per-ADR verdict (team recommendation — user picks)»
ADRTitleSrc statusVerdictCoupled work
025Weekly design surfaceAcceptedACCEPT (historical)note isolation/WeeklyTier superseded by 027
026Configurable org levelsSupersededACCEPT as Superseded onlynone (no active force)
027Fixed-depth taxonomy + manager slotAcceptedNEEDS-DISCUSSIONsupersede ADR-018; rewrite ADR-001-a + org-units API; resolve Spanish enum vs ADR-001-b
028Staffing hours_sourceAcceptedNEEDS-DISCUSSIONreconcile/ supersede ADR-024; amend ADR-001-c (violet band)
029Monthly schedulingAcceptedACCEPT-WITH-MODSstate publish/staleness invariant in-ADR; ADR-001-a glossary rows; fold Q1–Q15
030Position time-bandsAcceptedACCEPT(optional) rename file to match scope; band must divide required_weekly_hours
031Closed-on-holidaysAcceptedACCEPTlowest risk; default-safe
032Weekly rotationAcceptedACCEPT-WITH-MODSpin ISO-week TZ per ADR-015; decide year-boundary anchor
034Assignment slot anchoringAcceptedACCEPT (gated)land AFTER 032; consider INFO on silent slot-collision

4. Backend models.py comparison (coveris-b-integration → Coveris-e)

Sección titulada «4. Backend models.py comparison (coveris-b-integration → Coveris-e)»

Net-new apps: scheduling, weekly_structure. Unchanged: api (Employee/BaseModel byte-identical), fsm, business_rules, offer, users.

FileΔWhat changed
demand/models.py+336OrgUnit: unit_type, +depth, +max_weekly_extra_hours, +manager_position(OneToOne). Position: +plan_line FK, +source_subrow_key, +day, +start_time, +end_time, +pinned_slot_indexes, time-range clean()/derived_hours/constraints. NEW models StaffingPlanLine, StaffingPlanLineTag. (ADR-026/027/030/032 + staffing-plan)
assignments/models.py+73+effective_weekly_hours, +hours_source, +recurrence_rule(JSON), +slot_index (ADR-028/034)
tags/models.py+52TagCatalog +allows_productividad, +sourced_from_template; NEW model PositionTemplateTag (ADR-028/025)
scheduling/models.pyNEW 171AssignmentException, ShiftInstance (ADR-029)
weekly_structure/models.pyNEW 244PositionTemplate, WeeklyBoard, Placement, DayOfWeekno WeeklyTier (ADR-025/031/032)
  • Footer is STALE in Coveris-e: Last Updated: 2026-03-26 and the Scope: line are byte-identical to ours despite ~+464 lines of new endpoints. (MIGRATION-PLAN steps A1/B2 “verify footer/scope” → FAIL in source.)
  • New endpoints (body): GET /employees/{id}/capacity/, GET /demand/{id}/eligible-employees/, GET /calendar/holidays/, GET /scheduling/{ou}/{y}/{m}/, POST …/publish/, GET …/pdf/, GET/POST/GET/PATCH/DELETE /assignment-exceptions/; Position write adds start_time/end_time/pinned_slot_indexes.
  • 🔴 API.md ↔ ADR-027 INCONSISTENCY (must fix before adoption): Coveris-e API.md §“Org Levels (ADR-026)” still documents the full /api/v1/org-levels/ CRUD + reorder, the OrgUnit.level nested object, and unit_type “survives one release”. ADR-027 deleted all of that (depth model, no OrgLevel, no unit_type) and added POST/DELETE /org-units/:id/manager — which API.md does not document. The code is already on depth. → Coveris-e’s org-units section cannot be adopted as-is; it must be rewritten to the depth + manager-slot contract.

6. Human-in-the-loop decision points (asked via the pick interface)

Sección titulada «6. Human-in-the-loop decision points (asked via the pick interface)»
  1. Org nucleus reshape (025/026/027): adopt ADR-027 depth+manager model (supersede ADR-018, rewrite ADR-001-a + org-units API) — load-bearing: scheduling/staffing/Semanal all sit on it — vs defer vs reject.
  2. Staffing 028 vs 024: 028 supersedes 024 (hours_source replaces is_reinforcement) + amend ADR-001-c · vs coexist · vs defer.
  3. Scheduling stack (029→031→030→032→034): adopt with MODS · as-is now · defer.
  4. Doc drift + API.md: keep our adr-000/adr.base/adr-019/split-001 and rewrite org-units API.md to depth+manager (discard the stale Org-Levels section, bump footer) · vs take Coveris-e docs wholesale · vs per-file.

EXECUTION PLAN — ADR curation + API.md fix (⏳ AWAITING USER APPROVAL — 2026-05-31)

Sección titulada «EXECUTION PLAN — ADR curation + API.md fix (⏳ AWAITING USER APPROVAL — 2026-05-31)»

Autonomous /goal from the user (AFK). Hard gate: the user approves THIS plan before any ADR/API.md write begins. Nothing below is executed until then.

  • Current ADRs are never destroyed or edited destructively. No Status flips, no rewrites on adr-000…adr-024. (Old-ADR diff = cosmetic-only, except Coveris-e’s ADR-009 Draft→Accepted promotion, which we do not auto-adopt.)
  • Every imported ADR lands as Status: Proposed. Our branch’s code is still the OLD model (4-level unit_type, no scheduling/weekly_structure/hours_source); “Accepted” would contradict our code. Promotion to Accepted happens per backend phase when the code lands, with the user’s express OK (AGENTS.md HITL rule).
  • Drafting via the project’s cotton-coveris-backlog ADR-draft pipeline (Draft/Proposed only).
  • Keep Coveris-e’s ADR numbers (025–032, 034) — no collision with ours (we end at 024); matches the incoming code’s references.
  • No backend/, no frontend/, no push.

Old-ADR comparison (DONE — read-only Haiku pass)

Sección titulada «Old-ADR comparison (DONE — read-only Haiku pass)»
  • All shared ADRs 000–024 changed COSMETIC-only (frontmatter/wikilink/heading/callout downgrade) → keep ours wholesale.
  • Exception: Coveris-e flips adr-009 / 009-a / 009-b Draft→Accepted → separate minor ruling; default = keep ours Draft unless the user says adopt.
  • Coveris-e lacks our adr-000 + adr.base, carries monolithic adr-001.md → ignore (our split + conventions win).

Phase 1 — New ADRs (per-ADR disposition; all writes = new files, Status Proposed)

Sección titulada «Phase 1 — New ADRs (per-ADR disposition; all writes = new files, Status Proposed)»
ADRDispositionNotes / conflict handling (written INTO the ADR, not into ours)
025 weekly design surfaceKEEP (Proposed)Historical banner: its isolation + WeeklyTier are superseded by 027/reconciler
026 configurable org levelsDISCARDDead/superseded, never shipped; 1-paragraph history folded into 027’s Context
027 fixed-depth + manager slotKEEP (Proposed)In-ADR notes: “supersedes ADR-018 when depth code+API land” (not now). Flag open rulings: (a) 0.01h manager-hours sentinel, (b) AREA = new 5th level vs ADR-021’s 4-level reasoning, (c) Spanish enum CLINICA/AREA vs ADR-001-b, (d) glossary rows pending. Carries a “Glossary additions (pending ADR-001-a)” block instead of editing ADR-001-a
028 staffing hours_sourceKEEP (Proposed)Prominent RECONCILE-BEFORE-ACCEPT block: must reconcile ADR-024 (is_reinforcement vs hours_source) + amend ADR-001-c (violet band). Neither edited now
029 monthly schedulingKEEP (Proposed)+ MODS: publish/staleness invariant stated in-ADR; explicit “planned_hours/hours_delta never feed the weekly ledger (ADR-011)”; glossary rows; fold/link the Q1–Q15 design decisions
030 position time-bandsKEEP (Proposed)Rename file → adr-030-position-time-ranges.md (title/scope honesty; current filename says “schedule-architecture”)
031 closed-on-holidaysKEEP (Proposed)Clean, lowest-risk
032 weekly rotationKEEP (Proposed)+ MODS: pin ISO-week tick to browser-local per ADR-015; decide year-boundary anchor
034 slot anchoringKEEP (Proposed)Gated after 032; note 033 intentionally absent

Net: write 8 Proposed ADRs, discard 1 (026), update docs/adr/README.md index. Zero edits to existing ADRs.

Minimalist alternative (user prefers discard): DISCARD ALL 9 now; re-introduce each ADR alongside its backend code phase (B0–B7). The §INTEGRATION FINDINGS above already preserves the full assessment, so nothing is lost. → reply “discard-all” to take this path instead.

Phase 2 — API.md fix (only after Phase 1 lands)

Sección titulada «Phase 2 — API.md fix (only after Phase 1 lands)»

Bring API.md to the integration target, done correctly (fixing Coveris-e’s own mistakes), reflecting exactly the Proposed ADRs — via the cotton-coveris-live-docs pipeline:

  • Org-units: unit_typedepth + depth_name; add POST/DELETE /org-units/:id/manager; nesting rules → depth 0-4; do NOT import the dead /org-levels surface; coverage-summary.org_unit_typedepth_name.
  • Positions: add start_time / end_time / pinned_slot_indexes (+ day).
  • Assignments: hours_source; slot_index PATCH.
  • Staffing: GET /employees/{id}/capacity/, GET /demand/{id}/eligible-employees/.
  • Scheduling: GET /scheduling/{ou}/{y}/{m}/, POST …/publish/, GET …/pdf/; assignment-exceptions CRUD; GET /calendar/holidays/.
  • Bump footer Last Updated + Scope line. Mark new endpoints as target/planned per ADR-005-b (code catches up during migration).
  • Conservative alternative: add endpoints phase-by-phase as backend code lands → reply “api-phased”.

Promote any ADR to Accepted · edit/supersede any existing ADR (018/024/001-a/001-c/009…) · touch backend/ or frontend/ · push.

  • After Phase 1: git diff coveris-b-integration -- docs/adr/adr-000*…adr-024* is empty (existing ADRs untouched); only new adr-025/027/028/029/030/031/032/034 files + README index appear; no adr-026.
  • After Phase 2: contract-coverage guard parses every ### METHOD /path; footer bumped; zero /org-levels references.

Three quick confirmations for the user (defaults in bold)

Sección titulada «Three quick confirmations for the user (defaults in bold)»
  1. ADR set: keep-8-as-Proposed + discard-026 · or discard-all.
  2. API.md: target-state rewrite (correct, no org-levels) · or api-phased.
  3. ADR-009 Draft→Accepted from Coveris-e: keep ours Draft · or adopt-009.

Reply “approve” for all defaults, or override any of the three.



DRY-RUN SIMULATION RESULTS — 2026-05-31 (Agent Team coveris-migration-dryrun)

Sección titulada «DRY-RUN SIMULATION RESULTS — 2026-05-31 (Agent Team coveris-migration-dryrun)»

Read-only simulation of every phase of this plan against the real code in BOTH worktrees. No git checkout, no migrate, no edits to code — verified only via git ls-tree / git show <ref>:<path> / git diff. Three Opus teammates (backend-dryrun, docs-dryrun, frontend-dryrun) each dry-ran a tier; the orchestrator independently corroborated every load-bearing migration-dependency claim against the actual migration files.

This section is the executable-reality complement to the two ADR/docs-focused sections above (§INTEGRATION FINDINGS and §EXECUTION PLAN). Those decide which ADRs/API contract to adopt; this decides whether each migration phase mechanically applies, and ends with the consolidated user questionnaire.

Source: Coveris-e @ 6bfd1bd (⚠️ has uncommitted working-tree edits — see §6) · Target: coveris-b-integration @ 8c6f479 · merge-base: ed3b4c8. Coveris-e is 178 commits ahead of the merge-base; target is 42 ahead; 617 files diverge (+52.8k / −104.1k).

0. Headline corrections to the plan’s premises

Sección titulada «0. Headline corrections to the plan’s premises»
#The plan assumesReality (verified)
0.1Target branch is main (fdf0a90)Target is coveris-b-integration @ 8c6f479. The header + §“Estado al cierre” are stale.
0.2Source Coveris-e HEAD f371f20Source HEAD is 6bfd1bd with a dirty working tree (uncommitted edits to API.md, ADRs 030/032/034, api-conventions, 6 backend files, 4 frontend files).
0.3git checkout Coveris-e -- X brings the intended contentIt brings the committed 6bfd1bd content and silently reverts any file dirty in the source worktree (§6). The single biggest trap in the plan.
0.4Migrations can be cherry-picked per phase (“0003 through 0010”, “skip 0007”)Django migrations are an indivisible linear per-app chain. Several phase splits are mechanically impossible (§2).
0.5.env hard rules✅ Verified safe: .env gitignored on both branches, tracked nowhere; no SUPERUSER_PASSWORD exposure.
0.6(the §EXECUTION PLAN guardrails)✅ Real — they live in the §EXECUTION PLAN section of THIS doc (no separate file). This dry-run aligns with them: every imported ADR lands Proposed; 026 discarded; existing ADRs untouched.

Legend: 🟢 GREEN (works as written) · 🟡 NEEDS-FIX (works after a correction) · 🔴 BLOCKED (cannot work as written).

PhasePlan intentVerdictLoad-bearing problemRequired correction
A0ADRs 025–034 + README🟡ADR-file glob is safe (033 truly absent; monoliths not dragged). But wholesale checkout docs/adr/README.md drops our adr-000/adr.base/frontmatter/wikilinks and reintroduces the monolithic adr-001 row. Committed statuses ship 030/032/034 as Proposed (not Accepted).Keep the ADR-file glob. Do NOT checkout README — hand-append rows into OUR README (per §EXECUTION PLAN Phase 1).
A1PRD §3/§8.4, API.md, conventions, docs 07/08/09🟡PRD live file is root PRD.md (docs/PRD.md is 0 bytes); wholesale checkout strips frontmatter/wikilinks, inlines a 23-row tag table, reverts the 001-c ref, and leaves §8.4 open. API.md footer byte-identical/stale, committed copy lacks slot_index, org-units section contradicts ADR-027 (§3). Paths docs/07/08/09-*.md don’t exist in either branch; api-conventions.md moved to docs/stack/.Keep B1 as a surgical §3+§8.4 hand-edit on root PRD.md. Rewrite API.md per §EXECUTION PLAN Phase 2. Drop the 07/08/09 steps; reconcile docs/stack/api-conventions.md by path.
B0demand: checkout “0003 → 0010” + models.py🔴demand/0003 deps ('demand','0002_staffing_plan_line'); target has only 0001. Skipping 0002 = broken graph. models.py brought is the final state (StaffingPlanLine, max_weekly_extra_hours, Position time fields) whose migrations are 0002/0011/0013-0016 → makemigrations drift.Collapse B0+B5 into one phase: checkout demand migrations 0002 → 0016 as one contiguous block + models.py + views.py. The chain is unsplittable.
B1tags/ + business_rules/🟢tags 0003-0005 + business_rules 0002 are intra-app (no cross-app deps; seed_rules RunPython own-app idempotent).⚠️ seed_tags.py dirty in source (DEBUG-guard) — re-apply after checkout (§6).
B2api/fsm/users “minor” + seeds🟡Not minor: +1178/−936 across 12 files. seed_notti_structure.py (apps/api) is unlisted but called by entrypoint.sh:32 → boot failure. seed_transition_logs.py deleted. 3 dirty source seeds/test.Add seed_notti_structure.py; re-apply 3 dirty diffs; decide fate of removed seeds (§7-Q4).
B3assignments core, hold back slot_index🟡slot_index lives in models.py + serializers.py (4 sites: tuples ln 32/58/82, extra_kwargs 85, validate_slot_index 88-94)views.py has ZERO references (no PATCH endpoint; rides DRF partial_update). Plan strips the wrong file.Strip from models.py and serializers.py, ignore views.py — or drop the B3/B7 split entirely (§7-Q2).
B4weekly_structure full🔴ws/0001 deps demand/0002; ws/0003 deps demand/0004; ws/0004 RunPython replays OrgLevel + OrgUnit.level + WeeklyTier (transient — created mid-chain, dropped by demand/0009 & ws/0005). History-replay: only works if the full demand 0002-0009 chain ran in order first. No squash shortcut.Gate B4 on the fixed B0 (full demand 0002-0016). Then ws 0001-0012 apply cleanly.
B5demand 0013-0016🔴0013 deps 0012 deps 0011 deps 0010 — unscheduled before B5.Redundant once B0 is fixed — fold B5 into B0 (indivisible chain).
B6calendar_api + “Migration 0001” + pytest🔴calendar_api has no models.py, no migrations/, no tests/ — 5 files (__init__, apps, services, urls, views), a pure external-holiday-API service. Migration + pytest steps are fiction.Plain checkout of the 5 service files + wire urls into config/urls.py; drop migration/pytest steps; smoke /holidays/.
B7scheduling + restore slot_index🟡scheduling/0001 deps api/0001 + assignments/0004 + demand/0012 → gated on fixed B0 + B3≥0004. slot_index restore must target models.py + serializers.py, not views.py. Smoke needs B6 wired first.Correct slot_index targets; sequence B6 before the B7 smoke.
C0mock-data + docker + entrypoint🟡docker-compose ./mock-data:/mock-data:ro ✅. entrypoint.sh ✅ changes — but new boot calls seed_anesthesia_rotation and seed_notti_structure (unlisted) and drops 6 legacy seeds. mock-data dirs diverge (Coveris-e: notti-anesthesia/+notti-structure/; target: clinic-bienestar/). seed_anesthesia_rotation.py dirty in source.Add seed_notti_structure.py + mock-data/notti-structure/; re-apply dirty guard; resolve clinic-bienestar (§7-Q3).
C1coverage-summary Decimal fix🟢2-line sum(..., Decimal("0")) in demand/views.py; lands with views.py in the fixed B0.Keep as a verification checkpoint, not a standalone commit.
D0–D9frontend (deferred)🟡Directionally right but materially under-scoped (§5): ~11k LOC net-new, 2 wrong line counts, 1 no-op phase, 2 omitted prerequisite buckets, destructive org-chart reconcile.Re-scope per §5 before starting; net-new FE areas are 1:1 coupled to specific net-new backend apps.

2. The load-bearing correction — demand is ONE indivisible chain

Sección titulada «2. The load-bearing correction — demand is ONE indivisible chain»

The plan’s central mistake is treating Django migrations like cherry-pickable patches. Verified dependency declarations:

demand/0003_add_org_level → deps [('demand','0002_staffing_plan_line')]
demand/0013_position_time_range → deps [('demand','0012_seed_clinic_extra_hours_cap')]
weekly_structure/0001_initial → deps [('demand','0002_staffing_plan_line'), AUTH_USER_MODEL]
weekly_structure/0004_materialize_tiers_as_org_units → RunPython reads OrgLevel + OrgUnit.level + apps.get_model('weekly_structure','WeeklyTier')
scheduling/0001_initial → deps [('api','0001'),('assignments','0004_backfill_hours_source_cargo'),('demand','0012_seed_clinic_extra_hours_cap'), AUTH_USER_MODEL]

Consequences that rewrite the phase plan:

  • demand 0002→0016 must be one phase (collapse B0 + B5). You cannot bring 0003-0010 without 0002, nor 0013-0016 without 0011-0012.
  • weekly_structure (B4) can only migrate by replaying demand’s deleted intermediate state — hard-gated on the full demand chain running in order; no squash.
  • scheduling (B7) is gated on demand≥0012 + assignments≥0004 + api, and its smoke needs calendar_api (B6).
  • assignments slot_index is a models.py + serializers.py concern (4 sites), never views.py.
  • A0 committed statuses (= what a checkout ships): 025 Accepted · 026 Superseded-by-027 · 027 Accepted · 028 Accepted · 029 Accepted · 030 Proposed · 031 Accepted · 032 Proposed · 034 Proposed. Source-dirty edits flip 030/032/034 → Accepted, but the plan checks out committed → ships them Proposed (which the §EXECUTION PLAN “all Proposed” guardrail wants anyway).
  • A0 README hazard: Coveris-e’s README links the monolithic adr-001/002/003/005/009/012 via plain .md links and has no adr-000/adr.base row → wholesale checkout regresses our vault conventions. Hand-append instead.
  • A1 API.md ↔ ADR-027 (verified quotes): API.md :1205-1213 still documents /api/v1/org-levels/ GET/POST/PATCH/DELETE + reorder; :1223,1246 keep unit_type “survives one release”; :1245 embeds nested level. ADR-027 :105Drop OrgLevel model, OrgUnit.level FK, and the unit_type cache”; :132-134 adds POST/DELETE /api/v1/org-units/:id/managergrep /org-units/.*/manager in Coveris-e API.md → 0 hits. Shipped code is already on depth. ⟹ org-units API section is unadoptable as-is; rewrite per §EXECUTION PLAN Phase 2.
  • A1 API.md footer byte-identical to ours (Last Updated: 2026-03-26) despite new endpoints → the plan’s “verify footer matches today” step fails in source; bump by hand.
  • seed_notti_structure.py (apps/api) is the silent boot-breaker: present in Coveris-e, called by entrypoint.sh:32, in no checkout list → add to B2 + C0.
  • entrypoint.sh boot (Coveris-e): create_superuser → seed_mock_users → seed_org_units → seed_tags → seed_anesthesia_nomina → seed_therapy_nomina → seed_anesthesia_rotation → seed_notti_structure. Legacy seed_employees/positions/assignments/employee_tags/position_tags/transition_logs no longer booted (seed_transition_logs.py deleted upstream).
  • audit_log/ and offer/ are byte-identical across branches — plan correctly ignores them. demand/0001_initial identical across branches (0003’s AlterField is valid against target).
  • The hours_source quartet (demand 0011, tags 0003, assignments 0003, business_rules 0002) is all intra-app — the plan’s split across phases is graph-safe per se; the only danger is the linear-chain gaps inside demand/assignments.

5. Tier D (frontend) — scope reality-check (deferred tier, informational)

Sección titulada «5. Tier D (frontend) — scope reality-check (deferred tier, informational)»
  • Diffstat: ~87 files, +18,965 / −1,841 — most volume is net-new dirs, not edits.
  • Net-new (add-a-module): features/scheduling/ (6), features/structure/weekly-structure/ (19), features/structure/staffing-plan/ (3), shared/components/staffing/ (7), 8 net-new core/models. The last two buckets are omitted from D0–D9 and are prerequisites for D3/D4/D5.
  • Wrong line counts: scheduling-month-grid = 1,690L (plan: 1142); WeeklyStructureStore = 834L (plan: 818, and it’s 1 of 19 files / ~4k-LOC dir).
  • D7 “help system” is a NO-OPfeatures/help/ is byte-identical between branches.
  • D0 org-chart is destructive: Coveris-e deletes assign-drawer, candidate-assign-panel, node-detail-drawer, and worker-detail-drawer (not re-added); only manager-badge/manager-dialog are added. Plan phrasing implies additive (§7-Q10).
  • Hard backend coupling: net-new FE services hit /weekly-structure/, /scheduling/, /staffing-plan/, /employees/{id}/capacity/ (backend apps absent on target) → D3/D4/D5 + roster-capacity can’t precede their Tier-B phases.
  • frontend/design_handoff_scheduling_redesign/ (8-file JSX/CSS prototype) is already excluded by this plan’s hard rules ✅ — keep excluded (reference-only).

6. The dirty-source trap — files a plain git checkout silently reverts

Sección titulada «6. The dirty-source trap — files a plain git checkout silently reverts»

git checkout Coveris-e -- X ships committed 6bfd1bd content. These files are dirty in the source worktree and would be silently reverted (their uncommitted edits lost):

FileUncommitted edit that would be lost
backend/apps/tags/management/commands/seed_tags.pyraise CommandError DEBUG-guard
backend/apps/api/management/commands/seed_anesthesia_nomina.pyDEBUG-guard
backend/apps/api/management/commands/seed_therapy_nomina.pyDEBUG-guard
backend/apps/scheduling/management/commands/seed_anesthesia_rotation.pyDEBUG-guard
backend/apps/api/tests/test_seed_anesthesia_nomina.py+7-line test
backend/tests/test_regression_backlog.pyexpects start_time/end_time/pinned_slot_indexes (030/032) + slot_index (034)
API.mdadds the Assignment.slot_index contract row + ADR-034 notes
docs/adr/adr-030/032/034-*.mdStatus Proposed → Accepted
docs/api-conventions.mdadds frontmatter + wikilink conversion
docs/adr/README.md(dirty)
frontend/.../manager-dialog.component.ts · employee-form.component.ts · staffing-plan.service.tshttpResource.value().hasValue() guards
frontend/.../layout/component/app.menu.tsnav hover-bold width-stability CSS fix

Mitigation (strongly recommended): commit these edits on Coveris-e before running the migration so every checkout carries the intended content. Otherwise each phase must hand-re-apply its diffs. (§7-Q5.)


7. USER QUESTIONNAIRE — the exit (decisions only you can make)

Sección titulada «7. USER QUESTIONNAIRE — the exit (decisions only you can make)»

The dry-run is mechanically complete; the remaining blockers are decisions, not code lookups. Answer these tomorrow and the plan can be rewritten into executable form. Defaults reflect the team’s recommendation. ADR/API decisions are NOT repeated here — they live in the §EXECUTION PLAN “Three quick confirmations” above; this dry-run only confirms they’re mechanically migratable.

Forced by code (confirm you accept the restructure):

  1. demand phasing — Collapse plan B0 + B5 into one indivisible phase feat(demand): checkout migrations 0002→0016 + models.py + views.py? The chain is mechanically unsplittable — there is no alternative that applies cleanly. Default: yes.
  2. assignments slot_index split — Keep the B3-hold-back / B7-restore dance (corrected to strip/restore in models.py + serializers.py, not views.py), or land assignments whole in one phase (simpler; slot_index is the additive chain tail)? Default: land whole — drop the split.

Dataset & seed strategy: 3. clinic-bienestar keep-or-drop — Coveris-e’s entrypoint stops seeding mock-data/clinic-bienestar/ and switches to notti-anesthesia/ + notti-structure/. Keep clinic-bienestar alongside, or fully switch to the Notti dataset? Default: switch to Notti (matches the new entrypoint); archive clinic-bienestar. 4. Removed seed commandsseed_employees/seed_positions/seed_assignments/seed_employee_tags/seed_position_tags/seed_transition_logs are no longer booted (seed_transition_logs.py deleted upstream). Delete from the repo, or keep dormant? Default: drop the upstream-deleted one; keep the rest only if still referenced by tests. 5. Commit dirty source first? — Commit the 6 backend + 4 frontend + API.md/ADR/api-conventions uncommitted edits on Coveris-e before migrating (so plain checkout carries them), or hand-apply per phase? Default: commit-first — removes the §6 trap entirely.

Frontend (Tier D — for when it’s scheduled): 6. org-chart deletions — Confirm the 4 component deletions on target (assign-drawer, candidate-assign-panel, node-detail-drawer, worker-detail-drawer) are intended (D0 is a destructive reconcile, not additive)? 7. Re-scoped D plan — Accept adding a “D-pre: shared-staffing kit + scheduling/capacity models” phase, dropping the no-op D7, and fixing the D3/D4 line counts? Default: yes.

Carry-over (still open from §INTEGRATION FINDINGS-6, restated not re-decided): the ADR human-in-the-loop picks — org-nucleus reshape 025/026/027 (supersede ADR-018, rewrite ADR-001-a), staffing 028 vs 024 (+ amend ADR-001-c violet band), scheduling stack 029→031→030→032→034. Those remain your decisions; this dry-run verified only that the code matching them is mechanically migratable once the phasing above is corrected.

Dry-run by: Agent Team coveris-migration-dryrun (backend-dryrun, docs-dryrun, frontend-dryrun — Opus) + orchestrator corroboration, 2026-05-31. Method: read-only git inspection of both worktrees; zero mutations to code or branches.


Last edited: 2026-05-31 — Opus orchestrator + Agent Team coveris-migration-dryrun: appended §DRY-RUN SIMULATION RESULTS (per-phase verdicts, migration-graph reality, dirty-source trap, consolidated questionnaire). Prior 2026-05-31: Opus appended §EXECUTION PLAN (ADR curation + API.md fix, awaiting approval). Prior 2026-05-31: coveris-adr-review Agent Team — ADR assessment, models/API diff, HITL findings. Prior 2026-05-24: sesión Opus que integró luinloder y armó mock data de anestesia.