38686-vm/docs/plans/2026-05-17-site-report-removal-plan.md
Konrad du Plessis a502bac8ec 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) <noreply@anthropic.com>
2026-05-17 01:27:40 +02:00

17 KiB
Raw Blame History

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):

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.pyAbsenceAttendanceShortcutTests (class at line 2835): test_default_attendance_submit_unchanged (2858) + test_log_only_explicit_value_still_goes_to_site_report (2873)
  • Modify: core/views.pyattendance_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:

    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:

    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

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/<id>/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):

            # 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:

            # === 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:

            # 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

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):

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

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) <noreply@anthropic.com>"

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)

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:

USE_SQLITE=true python manage.py makemigrations --check --dry-run

Expected: "No changes detected" (model state + migrations consistent).

Step 3: System check + full suite

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)

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

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) <noreply@anthropic.com>"

(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

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/<work_log_id>/edit/ and /site-report/<work_log_id>/.
  • 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

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

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) <noreply@anthropic.com>"

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 2193 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.