Plan de Migración (Coveris-e → main)
MIGRATION-PLAN.md — Coveris-e → main
Sección titulada «MIGRATION-PLAN.md — Coveris-e → main»Task list for the main worktree terminal. Migrate file-by-file from
Coveris-eintomainin well-curated, atomic phases. Do NOTgit merge. Usegit checkout Coveris-e -- <paths>per phase, test, commit cleanly, move on.
Source of truth & references
Sección titulada «Source of truth & references»- Source branch:
Coveris-e(HEADf371f20, 13 commits ahead oforigin/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.mdline-by-line file diffdocs/volatile-docs/05-merge-readiness.mdblockers detail
Hard rules
Sección titulada «Hard rules»- ✋ Never
git merge Coveris-eorgit cherry-pick. Onlygit 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_PASSWORDin 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.mdonce 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)»[ ] A0 — ADRs 025–034 + README index
Sección titulada «[ ] A0 — ADRs 025–034 + README index»-
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 includingPATCH /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)
Tier B — Backend, in dependency order
Sección titulada «Tier B — Backend, in dependency order»[ ] 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.pythrough0010_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.pyandbackend/apps/demand/views.pyonly 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)
[ ] B1 — tags/ + business_rules/
Sección titulada «[ ] B1 — tags/ + business_rules/»-
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.pyfor now (B7) - Inspect
models.pyand manually remove theslot_indexfield 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)
[ ] B4 — weekly_structure/ full module
Sección titulada «[ ] B4 — weekly_structure/ full module»-
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.pyandviews.pyfor 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_indexfield inbackend/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)
Tier C — Data, runtime, bug fixes
Sección titulada «Tier C — Data, runtime, bug fixes»[ ] 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:romount 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
[ ] C1 — coverage-summary empty-state fix
Sección titulada «[ ] C1 — coverage-summary empty-state fix»- 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.
Verification per phase (mandatory)
Sección titulada «Verification per phase (mandatory)»# 1. testscd backend && pytest apps/<app>/tests/ -v
# 2. clean migrations from empty DBdocker compose down -vdocker compose up -d# wait for health: curl http://localhost:8000/api/health/
# 3. lint (optional but recommended)cd backend && ruff check apps/<app>/
# 4. CHANGELOGecho "- <date> feat(<scope>): <one-line summary>" >> CHANGELOG.md
# 5. commitgit add -Agit commit -m "<conventional message>" \ -m "Co-Authored-By: ..."Push policy (recordatorio)
Sección titulada «Push policy (recordatorio)»- Push de
mainSOLO con consentimiento explícito del usuario. - Push de
Coveris-eya 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-eHEAD:f371f20(luinloder integrado, mock data anestesia, handoff doc)mainHEAD: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-reviewAgent Team (3 Opus business-logic experts) + direct diff. Source:Coveris-e @ 6bfd1bd· Target:coveris-b-integration @ 8c6f479· merge-baseed3b4c8. ⚠️ Per AGENTS.md, ADR integration is HUMAN-IN-THE-LOOP. Nothing below is adopted until the user picks. The team assesses; the user decides.
1. ADR divergence (bidirectional)
Sección titulada «1. ADR divergence (bidirectional)»- 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.mdandadr.base. - Coveris-e still carries the monolithic
adr-001.md(we split it into 001-a/b/c). - Its
adr-019differs 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.
- Coveris-e lacks our
2. Supersession map (the load-bearing part)
Sección titulada «2. Supersession map (the load-bearing part)»| New ADR | Effect 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)»| ADR | Title | Src status | Verdict | Coupled work |
|---|---|---|---|---|
| 025 | Weekly design surface | Accepted | ACCEPT (historical) | note isolation/WeeklyTier superseded by 027 |
| 026 | Configurable org levels | Superseded | ACCEPT as Superseded only | none (no active force) |
| 027 | Fixed-depth taxonomy + manager slot | Accepted | NEEDS-DISCUSSION | supersede ADR-018; rewrite ADR-001-a + org-units API; resolve Spanish enum vs ADR-001-b |
| 028 | Staffing hours_source | Accepted | NEEDS-DISCUSSION | reconcile/ supersede ADR-024; amend ADR-001-c (violet band) |
| 029 | Monthly scheduling | Accepted | ACCEPT-WITH-MODS | state publish/staleness invariant in-ADR; ADR-001-a glossary rows; fold Q1–Q15 |
| 030 | Position time-bands | Accepted | ACCEPT | (optional) rename file to match scope; band must divide required_weekly_hours |
| 031 | Closed-on-holidays | Accepted | ACCEPT | lowest risk; default-safe |
| 032 | Weekly rotation | Accepted | ACCEPT-WITH-MODS | pin ISO-week TZ per ADR-015; decide year-boundary anchor |
| 034 | Assignment slot anchoring | Accepted | ACCEPT (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 | +336 | OrgUnit: −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 | +52 | TagCatalog +allows_productividad, +sourced_from_template; NEW model PositionTemplateTag (ADR-028/025) |
scheduling/models.py | NEW 171 | AssignmentException, ShiftInstance (ADR-029) |
weekly_structure/models.py | NEW 244 | PositionTemplate, WeeklyBoard, Placement, DayOfWeek — no WeeklyTier (ADR-025/031/032) |
5. API.md comparison
Sección titulada «5. API.md comparison»- Footer is STALE in Coveris-e:
Last Updated: 2026-03-26and theScope: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 addsstart_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, theOrgUnit.levelnested object, andunit_type“survives one release”. ADR-027 deleted all of that (depth model, no OrgLevel, no unit_type) and addedPOST/DELETE /org-units/:id/manager— which API.md does not document. The code is already ondepth. → 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)»- 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.
- Staffing 028 vs 024: 028 supersedes 024 (
hours_sourcereplacesis_reinforcement) + amend ADR-001-c · vs coexist · vs defer. - Scheduling stack (029→031→030→032→034): adopt with MODS · as-is now · defer.
- 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
/goalfrom the user (AFK). Hard gate: the user approves THIS plan before any ADR/API.md write begins. Nothing below is executed until then.
Guardrails (non-negotiable)
Sección titulada «Guardrails (non-negotiable)»- Current ADRs are never destroyed or edited destructively. No
Statusflips, no rewrites onadr-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-levelunit_type, noscheduling/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-backlogADR-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/, nofrontend/, 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–024changed COSMETIC-only (frontmatter/wikilink/heading/callout downgrade) → keep ours wholesale. - Exception: Coveris-e flips
adr-009/009-a/009-bDraft→Accepted → separate minor ruling; default = keep ours Draft unless the user says adopt. - Coveris-e lacks our
adr-000+adr.base, carries monolithicadr-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)»| ADR | Disposition | Notes / conflict handling (written INTO the ADR, not into ours) |
|---|---|---|
| 025 weekly design surface | KEEP (Proposed) | Historical banner: its isolation + WeeklyTier are superseded by 027/reconciler |
| 026 configurable org levels | DISCARD | Dead/superseded, never shipped; 1-paragraph history folded into 027’s Context |
| 027 fixed-depth + manager slot | KEEP (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_source | KEEP (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 scheduling | KEEP (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-bands | KEEP (Proposed) | Rename file → adr-030-position-time-ranges.md (title/scope honesty; current filename says “schedule-architecture”) |
| 031 closed-on-holidays | KEEP (Proposed) | Clean, lowest-risk |
| 032 weekly rotation | KEEP (Proposed) | + MODS: pin ISO-week tick to browser-local per ADR-015; decide year-boundary anchor |
| 034 slot anchoring | KEEP (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_type→depth+depth_name; addPOST/DELETE /org-units/:id/manager; nesting rules → depth 0-4; do NOT import the dead/org-levelssurface;coverage-summary.org_unit_type→depth_name. - Positions: add
start_time/end_time/pinned_slot_indexes(+day). - Assignments:
hours_source;slot_indexPATCH. - Staffing:
GET /employees/{id}/capacity/,GET /demand/{id}/eligible-employees/. - Scheduling:
GET /scheduling/{ou}/{y}/{m}/,POST …/publish/,GET …/pdf/;assignment-exceptionsCRUD;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”.
Will NOT do without further OK
Sección titulada «Will NOT do without further OK»Promote any ADR to Accepted · edit/supersede any existing ADR (018/024/001-a/001-c/009…) · touch backend/ or frontend/ · push.
Verification
Sección titulada «Verification»- After Phase 1:
git diff coveris-b-integration -- docs/adr/adr-000*…adr-024*is empty (existing ADRs untouched); only newadr-025/027/028/029/030/031/032/034files + README index appear; noadr-026. - After Phase 2: contract-coverage guard parses every
### METHOD /path; footer bumped; zero/org-levelsreferences.
Three quick confirmations for the user (defaults in bold)
Sección titulada «Three quick confirmations for the user (defaults in bold)»- ADR set: keep-8-as-Proposed + discard-026 · or
discard-all. - API.md: target-state rewrite (correct, no org-levels) · or
api-phased. - 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, nomigrate, no edits to code — verified only viagit 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 assumes | Reality (verified) |
|---|---|---|
| 0.1 | Target branch is main (fdf0a90) | Target is coveris-b-integration @ 8c6f479. The header + §“Estado al cierre” are stale. |
| 0.2 | Source Coveris-e HEAD f371f20 | Source 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.3 | git checkout Coveris-e -- X brings the intended content | It brings the committed 6bfd1bd content and silently reverts any file dirty in the source worktree (§6). The single biggest trap in the plan. |
| 0.4 | Migrations 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. |
1. Phase verdict matrix
Sección titulada «1. Phase verdict matrix»Legend: 🟢 GREEN (works as written) · 🟡 NEEDS-FIX (works after a correction) · 🔴 BLOCKED (cannot work as written).
| Phase | Plan intent | Verdict | Load-bearing problem | Required correction |
|---|---|---|---|---|
| A0 | ADRs 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). |
| A1 | PRD §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. |
| B0 | demand: 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. |
| B1 | tags/ + 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). |
| B2 | api/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). |
| B3 | assignments 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). |
| B4 | weekly_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. |
| B5 | demand 0013-0016 | 🔴 | 0013 deps 0012 deps 0011 deps 0010 — unscheduled before B5. | Redundant once B0 is fixed — fold B5 into B0 (indivisible chain). |
| B6 | calendar_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/. |
| B7 | scheduling + 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. |
| C0 | mock-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). |
| C1 | coverage-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–D9 | frontend (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.
3. Tier A (docs) — verified detail
Sección titulada «3. Tier A (docs) — verified detail»- 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/012via plain.mdlinks and has noadr-000/adr.baserow → wholesale checkout regresses our vault conventions. Hand-append instead. - A1 API.md ↔ ADR-027 (verified quotes): API.md
:1205-1213still documents/api/v1/org-levels/GET/POST/PATCH/DELETE +reorder;:1223,1246keepunit_type“survives one release”;:1245embeds nestedlevel. ADR-027:105“DropOrgLevelmodel,OrgUnit.levelFK, and theunit_typecache”;:132-134addsPOST/DELETE /api/v1/org-units/:id/manager— grep/org-units/.*/managerin Coveris-e API.md → 0 hits. Shipped code is already ondepth. ⟹ 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.
4. Tier B/C (backend) — verified detail
Sección titulada «4. Tier B/C (backend) — verified detail»seed_notti_structure.py(apps/api) is the silent boot-breaker: present in Coveris-e, called byentrypoint.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. Legacyseed_employees/positions/assignments/employee_tags/position_tags/transition_logsno longer booted (seed_transition_logs.pydeleted upstream). audit_log/andoffer/are byte-identical across branches — plan correctly ignores them.demand/0001_initialidentical across branches (0003’s AlterField is valid against target).- The
hours_sourcequartet (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-newcore/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-OP —
features/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); onlymanager-badge/manager-dialogare 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):
| File | Uncommitted edit that would be lost |
|---|---|
backend/apps/tags/management/commands/seed_tags.py | raise CommandError DEBUG-guard |
backend/apps/api/management/commands/seed_anesthesia_nomina.py | DEBUG-guard |
backend/apps/api/management/commands/seed_therapy_nomina.py | DEBUG-guard |
backend/apps/scheduling/management/commands/seed_anesthesia_rotation.py | DEBUG-guard |
backend/apps/api/tests/test_seed_anesthesia_nomina.py | +7-line test |
backend/tests/test_regression_backlog.py | expects start_time/end_time/pinned_slot_indexes (030/032) + slot_index (034) |
API.md | adds the Assignment.slot_index contract row + ADR-034 notes |
docs/adr/adr-030/032/034-*.md | Status Proposed → Accepted |
docs/api-conventions.md | adds frontmatter + wikilink conversion |
docs/adr/README.md | (dirty) |
frontend/.../manager-dialog.component.ts · employee-form.component.ts · staffing-plan.service.ts | httpResource.value() → .hasValue() guards |
frontend/.../layout/component/app.menu.ts | nav 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):
- 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. - assignments
slot_indexsplit — 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 commands — seed_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.