From a502bac8ec97a5562b485fcd95db2a14619cc45f Mon Sep 17 00:00:00 2001 From: Konrad du Plessis Date: Sun, 17 May 2026 01:27:40 +0200 Subject: [PATCH] docs: TDD plan for SiteReport removal (3 tasks, HARD STOP) Task 1 revert attendance flow (TDD via 2 rewritten cross-ref tests), Task 2 delete the SiteReport surface + 5 test classes + autogenerate 0018_delete_sitereport (suite 209->193), Task 3 docs. HARD STOP + grep-clean/makemigrations-check gate before any push. Local-only. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../2026-05-17-site-report-removal-plan.md | 375 ++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 docs/plans/2026-05-17-site-report-removal-plan.md diff --git a/docs/plans/2026-05-17-site-report-removal-plan.md b/docs/plans/2026-05-17-site-report-removal-plan.md new file mode 100644 index 0000000..17ee368 --- /dev/null +++ b/docs/plans/2026-05-17-site-report-removal-plan.md @@ -0,0 +1,375 @@ +# Remove "Log Today's Work" / SiteReport — Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task-by-task (in-session, fresh subagent + 2-stage review per task). Each subagent uses superpowers:test-driven-development where a behavioural test exists. + +**Goal:** Completely remove the SiteReport / "Log Today's Work" feature — model, table, code, UI, routes, tests, docs — with zero live remnants and nothing broken; revert the post-attendance flow to redirect→home; drop `core_sitereport` via a new migration. + +**Architecture:** Three tasks: (1) revert the attendance redirect — genuine TDD via the two cross-ref flow tests; (2) delete the SiteReport surface + its 5 test classes + autogenerate `0018_delete_sitereport`; (3) docs. Then a HARD-STOP verification gate. + +**Tech Stack:** Django 5.2.7, Python 3.13, SQLite local; the deletion migration is autogenerated (`makemigrations`), never hand-written. + +**Design doc:** `docs/plans/2026-05-17-site-report-removal-design.md` (commit `777c7c6`). **Capture doc:** `docs/plans/2026-05-17-site-report-removed-capture.md` (knowledge preserved for the future rebuild). + +**Branch / baseline:** `ai-dev`, local HEAD `777c7c6`, origin `aaca0b3` (production caught up & verified). **209/209 tests passing.** + +**Test command (Git Bash, per CLAUDE.md):** +```bash +USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests -v 2 +``` + +**Exact test-count math (pin this):** baseline **209**. Task 1 *rewrites* 2 tests (count unchanged → still 209). Task 2 *deletes* **16** SiteReport-only tests (`SiteReportModelTests` 4 + `SiteReportFormTests` 4 + `SiteReportEditViewTests` 5 + `SiteReportDetailViewTests` 2 + `AttendanceLogRedirectsToSiteReportTests` 1). **Final suite = 209 − 16 = 193 OK.** No new tests added (YAGNI — design §3; the 404 check is on Konrad's manual checklist, not automated). + +> ⛔ **HARD STOP after Task 3 + the verification gate.** Do NOT `git push`, do NOT deploy. Konrad runs the local verification checklist (design doc) — including `makemigrations --check`, the route-404 checks, and the grep-clean — then pushes himself. Deploy (his call): pull → `migrate` (applies `0018`, drops `core_sitereport`) → restart. **No collectstatic** (no `static/` change). + +> **Destructive:** `0018_delete_sitereport` drops `core_sitereport` and all its rows. **No backup** — Konrad's explicit, recorded choice (design "Decision"). Do not add a backup step. + +--- + +### Task 1: Revert the post-attendance flow (TDD via the 2 cross-ref tests) + +**Files:** +- Test: `core/tests.py` — `AbsenceAttendanceShortcutTests` (class at line 2835): `test_default_attendance_submit_unchanged` (2858) + `test_log_only_explicit_value_still_goes_to_site_report` (2873) +- Modify: `core/views.py` — `attendance_log` POST tail (lines ~694-785) + +**Step 1: Rewrite the two failing tests (expect the NEW contract)** + +In `core/tests.py`, replace `test_default_attendance_submit_unchanged` (currently lines ~2858-2871) with: + +```python + def test_default_attendance_submit_redirects_home(self): + """A plain attendance submit (no next_action) now redirects to + the dashboard — the old forced Site Report redirect was removed + (SiteReport feature deleted 17 May 2026).""" + resp = self.client.post(reverse('attendance_log'), data={ + 'date': '2026-05-14', + 'project': self.project.id, + 'team': self.team.id, + 'workers': [self.worker.id], + 'overtime_amount': '0.00', + 'notes': '', + # next_action omitted on purpose — default path → home. + }) + self.assertEqual(resp.status_code, 302) + self.assertEqual(resp.url, reverse('home')) +``` + +Replace `test_log_only_explicit_value_still_goes_to_site_report` +(currently lines ~2873-2886) with: + +```python + def test_log_only_explicit_value_redirects_home(self): + """An explicit next_action='log_only' also lands on the + dashboard (same default path; no Site Report anymore).""" + resp = self.client.post(reverse('attendance_log'), data={ + 'date': '2026-05-14', + 'project': self.project.id, + 'team': self.team.id, + 'workers': [self.worker.id], + 'overtime_amount': '0.00', + 'notes': '', + 'next_action': 'log_only', + }) + self.assertEqual(resp.status_code, 302) + self.assertEqual(resp.url, reverse('home')) +``` + +Leave `test_log_absences_button_redirects_to_absence_log` (line ~2888) +**untouched**. + +**Step 2: Run them — verify they FAIL** + +```bash +USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests.AbsenceAttendanceShortcutTests -v 2 +``` +Expected: the two rewritten tests FAIL (`resp.url` is `/site-report//edit/`, not `/`); `test_log_absences_button_redirects_to_absence_log` still PASSES. + +**Step 3: Revert the view** + +In `core/views.py::attendance_log`, the POST tail currently (≈688-785) +tracks `created_log_ids` and redirects to `site_report_edit`. Make +exactly these edits: + +(a) **Delete the `created_log_ids` comment + init** (currently ~696-698): +```python + # Track the IDs of work logs we just created so we can redirect + # the supervisor to the site-report form for the most recent one. + created_log_ids = [] +``` +(Keep the `created_count = 0` and `skipped_count = 0` lines above it.) + +(b) **Delete the append** (currently ~739): the line +`created_log_ids.append(work_log.id)` (keep `created_count += 1`). + +(c) **Trim the Round-C comment** (currently ~750-758) — remove the +SiteReport wording. Replace the comment block with: +```python + # === Post-submit destination === + # The attendance form has two submit buttons (both POST + # `next_action`): + # - default / 'log_only' → dashboard (home) + success toast + # - 'log_absences' → /absences/log/ pre-filled + next_action = request.POST.get('next_action', 'log_only') +``` + +(d) **Fix the log_absences guard** (currently ~760): +`if next_action == 'log_absences' and created_log_ids:` → +`if next_action == 'log_absences' and created_count:` +(the rest of that branch — the `urlencode` prefill + `return +redirect(...)` — is UNCHANGED.) + +(e) **Replace the two-step block** (currently ~775-785, from the +`# Two-step flow:` comment through `return redirect('home')`) with: +```python + # SiteReport feature removed (17 May 2026) — a successful + # attendance submit now simply returns to the dashboard; + # the success toast above already confirms what was logged. + return redirect('home') +``` + +**Step 4: Run the tests — verify they PASS** + +```bash +USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests.AbsenceAttendanceShortcutTests -v 2 +``` +Expected: all 3 PASS. Then the full suite (still **209** — nothing +deleted yet, 2 rewritten): +```bash +USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests -v 2 +``` +Expected: **209 OK** (the SiteReport classes still exist + pass at this +point; the redirect tests pass; nothing else regressed). If any +SiteReport view/model test fails here, STOP — only the attendance +redirect should have changed; investigate before continuing. + +**Step 5: Commit** + +```bash +git add core/views.py core/tests.py +git commit -m "refactor: revert post-attendance flow to redirect-home (SiteReport removal step 1) + +Co-Authored-By: Claude Opus 4.7 (1M context) " +``` + +--- + +### Task 2: Delete the SiteReport surface + its tests + autogenerate the drop migration + +**Files (delete entire files):** +- `core/site_report_schema.py` +- `core/templates/core/site_report_edit.html` +- `core/templates/core/site_report_detail.html` + +**Files (edit):** +- `core/models.py` — delete `class SiteReport` (lines **182-272** + inclusive: `class SiteReport(models.Model):` through + `return f"Site Report — {self.work_log}"`). Ensure exactly two blank + lines remain between the preceding class's end and `class Loan` (line + was 274). +- `core/forms.py` — + - line 17 ` SiteReport, Absence,` → ` Absence,` (KEEP Absence). + - delete line 19 `from .site_report_schema import COUNT_METRICS, CHECK_METRICS`. + - delete the `SiteReportForm` class **and its preceding `# === … ===` + doc-header comment block**, ending immediately before line 615's + `# ====…` / `# === ABSENCE FORMS ===` banner (read `forms.py` + ~470-617 to get the exact start of the SiteReport doc-header; the + ABSENCE FORMS banner at 615-617 and everything after it stays). +- `core/views.py` — + - delete the model import `SiteReport,` (line ~31, inside the + `from .models import (…)` block — keep the other names), + - delete the form import `SiteReportForm,` (line ~38, inside + `from .forms import (…)`), + - delete line ~41 `from .site_report_schema import COUNT_METRICS, CHECK_METRICS, label_for`, + - delete the three functions `_can_access_site_report`, + `site_report_edit`, `site_report_detail` (contiguous, ~819-963 — + read 815-966 for exact bounds: from the blank/comment line above + `def _can_access_site_report` through the last line of + `site_report_detail`, stopping before `def work_history`), + - in `work_history` (~965+), remove the `'site_report'` from the + `WorkLog.objects.select_related('site_report')` calls (it is the + only select_related arg on those lines — drop the entire + `.select_related('site_report')` so it reads `WorkLog.objects` / + `WorkLog.objects.filter(…)`; keep any `prefetch_related`/other + chaining intact) and delete the now-stale explanatory comment + (~969-970) that mentions the indicator. +- `core/urls.py` — delete the two `path('site-report/…')` lines (32-33). +- `core/admin.py` — delete line 7 ` SiteReport,` (keep line 8 + ` Absence,`); delete the `@admin.register(SiteReport)` decorator + + the entire `SiteReportAdmin` class (starts line 154 — read + `admin.py` 153-190 to find its end: next `@admin.register` or EOF). +- `core/templates/core/work_history.html` — delete the site-report + indicator block: the `{# Site-report indicator… #}` comment (line + 458) through the `{% endif %}` that closes the + `{% if log.site_report %}` (read 455-485 for the exact `{% endif %}` + line — it follows the `{% else %}` "Add site report" anchor). Remove + the whole comment+if/else/endif; leave the surrounding cell markup + valid. +- `core/tests.py` — delete the contiguous SiteReport test block: from + line **1622** (`# ===…` opening the `# === SITE REPORT TESTS ===` + banner) through line **1925** (last line of + `AttendanceLogRedirectsToSiteReportTests`, + `self.assertEqual(WorkLog.objects.count(), 1)`), INCLUDING the + `from core.models import SiteReport` / `from core.forms import + SiteReportForm` imports (1629-1630). Ensure exactly two blank lines + precede line 1927's `# === Worker Absence — Phase 1 ===` banner. + +**Step 1: Delete files + apply all edits above.** + +**Step 2: Generate the deletion migration (do NOT hand-write)** + +```bash +USE_SQLITE=true python manage.py makemigrations core +``` +Expected: creates `core/migrations/0018_delete_sitereport.py` containing +`migrations.DeleteModel(name='SiteReport')` (Django also emits the +correct `RemoveField` ordering for the `work_log`/`created_by` +relations) with `dependencies = [('core', '0017_alter_payrolladjustment_type')]`. +Open the file and sanity-check it is ONLY a SiteReport deletion (no +unrelated model changes). Then: +```bash +USE_SQLITE=true python manage.py makemigrations --check --dry-run +``` +Expected: "No changes detected" (model state + migrations consistent). + +**Step 3: System check + full suite** + +```bash +USE_SQLITE=true python manage.py check +USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests -v 2 +``` +Expected: `check` clean; suite **193 OK** (209 − 16 deleted). If the +count isn't exactly 193, or any non-SiteReport test fails, STOP and +report (do not patch unrelated tests). + +**Step 4: Grep-clean check (no live remnants)** + +```bash +grep -rniE "sitereport|site_report|site-report" core/ --include="*.py" --include="*.html" | grep -v "/migrations/0013_add_site_report.py" | grep -v "/migrations/0014_add_absence.py" | grep -v "/migrations/0018_delete_sitereport.py" +``` +Expected: **no output**. (The three migration files legitimately +contain the name — `0013` created it, `0014` depends on it for +ordering, `0018` deletes it; those are immutable history, not +remnants.) Any other match = a missed reference; fix before commit. + +**Step 5: Commit** + +```bash +git add -A core/ +git commit -m "feat!: remove SiteReport / Log Today's Work feature (model, code, UI, tests) + +Drops core_sitereport via 0018_delete_sitereport. Knowledge preserved +in docs/plans/2026-05-17-site-report-removed-capture.md. + +Co-Authored-By: Claude Opus 4.7 (1M context) " +``` +(`git add -A core/` so the deleted files are staged as deletions. Do +NOT `git add` outside `core/`.) + +--- + +### Task 3: Docs (CLAUDE.md + parked-work.md) + +**Files:** +- Modify: `CLAUDE.md` +- Modify: `docs/plans/parked-work.md` + +**Step 1: Full-suite anchor** + +```bash +USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests -v 2 +``` +Expected: **193 OK**. If not, STOP. + +**Step 2: CLAUDE.md** + +- Delete the entire **"## SiteReport metric schema (Apr 2026)"** + section (heading through the line before the next `##`). +- Delete the **Key Models** bullet that starts **`- **SiteReport**`**. +- Delete the two URL-routes table rows for + `/site-report//edit/` and `/site-report//`. +- Remove SiteReport/two-step-flow mentions elsewhere (e.g. the + "two-step flow" / "auto-redirected here after /attendance/log/ POST" + wording in the SiteReport-schema area and any Key-Business-Rules or + Absence-section cross-mention of the site-report redirect — grep + `grep -n "SiteReport\|site.report\|site_report\|Log Today" CLAUDE.md` + and clear every live reference). +- Add a one-line pointer where the SiteReport schema section was, e.g.: + > **SiteReport / "Log Today's Work" — REMOVED 17 May 2026.** Feature + > deleted (model/table/UI/routes) to be rebuilt from scratch later. + > See `docs/plans/2026-05-17-site-report-removed-capture.md`. + +**Step 3: parked-work.md** + +- Replace the **"### Post-Attendance Flow v2"** paused entry (under + "## ⏸ Paused — ready to execute") with a short entry, e.g.: + > ### Site-progress logging — rebuild from scratch (parked) + > The SiteReport / "Log Today's Work" feature was **removed** + > 17 May 2026 (Konrad's call — work mix shifting, scope uncertain). + > Post-attendance now just returns to the dashboard. Future rebuild + > starts fresh: see + > `docs/plans/2026-05-17-site-report-removed-capture.md` (preserves + > the schema-as-Python pattern, recovery pointers, and the + > now-superseded `2026-05-15-post-attendance-flow-v2-*` prior + > thinking — those files stay on disk). +- Leave the `2026-05-15-post-attendance-flow-v2-design.md` / + `-plan.md` files in place (do NOT delete — historical thinking). +- Scan the rest of parked-work.md for any other live "Site Report" / + two-step-flow reference and reconcile to "removed; see capture doc". + +**Step 4: Verify docs** + +```bash +grep -niE "sitereport|site.report|site_report|log today's work" CLAUDE.md docs/plans/parked-work.md +``` +Expected: only the new "REMOVED — see capture doc" pointers (and the +parked-work rebuild entry) match — no stale "how SiteReport works" / +"Post-Attendance Flow v2 ready to execute" content remains. + +**Step 5: Commit** + +```bash +git add CLAUDE.md docs/plans/parked-work.md +git commit -m "docs: scrub SiteReport from CLAUDE.md + park rebuild (capture doc) + +Co-Authored-By: Claude Opus 4.7 (1M context) " +``` + +--- + +## ⛔ HARD STOP — verification gate, then hand back to Konrad + +After Task 3's commit, run the full verification and STOP: + +1. `git status` clean; `git log --oneline -4` shows the 3 removal + commits on top of `777c7c6`. +2. `USE_SQLITE=true python manage.py makemigrations --check --dry-run` + → "No changes detected". +3. `USE_SQLITE=true python manage.py check` → no issues. +4. `USE_SQLITE=true python manage.py migrate` on a fresh local SQLite + → `0018_delete_sitereport` applies with no error; then + `USE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests -v 2` + → **193 OK**. +5. Grep-clean (Task 2 Step 4 command) → no output beyond the 3 + migration files. +6. **Do NOT push. Do NOT deploy.** Report the commit list + 193 count + and point Konrad at the **"Local verification (Konrad — HARD STOP + gate)"** section of + `docs/plans/2026-05-17-site-report-removal-design.md` (7 manual + steps incl. `/attendance/log/` → dashboard, `/site-report/1/edit/` + → 404, admin has no SiteReport). +7. On Konrad's explicit approval: he pushes; deploy = pull → + `migrate` (drops `core_sitereport`) → restart **last**. **No + collectstatic** (no `static/` change). + +## Notes + +- **DRY/YAGNI:** no replacement built (future separate rethink — + captured). No new tests beyond the 2 rewritten flow tests. +- **Migration discipline:** `0018` is autogenerated, never hand-edited; + `0013`/`0014` are immutable history and stay. +- **Don't touch:** the `next_action == 'log_absences'` branch, the + Manager/Salaried bundle, pay-type filter, Salary auto-scope, Pay + Salary quick action — all out of scope. +- The production-caught-up breadcrumb was already pushed separately + (`aaca0b3`); the design + capture docs are local (`777c7c6`). This + plan's commits + those docs all go in the SAME push when Konrad + approves — one bundle.