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>
17 KiB
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) — includingmakemigrations --check, the route-404 checks, and the grep-clean — then pushes himself. Deploy (his call): pull →migrate(applies0018, dropscore_sitereport) → restart. No collectstatic (nostatic/change).
Destructive:
0018_delete_sitereportdropscore_sitereportand 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_logPOST 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.pycore/templates/core/site_report_edit.htmlcore/templates/core/site_report_detail.html
Files (edit):
core/models.py— deleteclass SiteReport(lines 182-272 inclusive:class SiteReport(models.Model):throughreturn f"Site Report — {self.work_log}"). Ensure exactly two blank lines remain between the preceding class's end andclass 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
SiteReportFormclass and its preceding# === … ===doc-header comment block, ending immediately before line 615's# ====…/# === ABSENCE FORMS ===banner (readforms.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).
- line 17
core/views.py—- delete the model import
SiteReport,(line ~31, inside thefrom .models import (…)block — keep the other names), - delete the form import
SiteReportForm,(line ~38, insidefrom .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 abovedef _can_access_site_reportthrough the last line ofsite_report_detail, stopping beforedef work_history), - in
work_history(~965+), remove the'site_report'from theWorkLog.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 readsWorkLog.objects/WorkLog.objects.filter(…); keep anyprefetch_related/other chaining intact) and delete the now-stale explanatory comment (~969-970) that mentions the indicator.
- delete the model import
core/urls.py— delete the twopath('site-report/…')lines (32-33).core/admin.py— delete line 7SiteReport,(keep line 8Absence,); delete the@admin.register(SiteReport)decorator + the entireSiteReportAdminclass (starts line 154 — readadmin.py153-190 to find its end: next@admin.registeror 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 ofAttendanceLogRedirectsToSiteReportTests,self.assertEqual(WorkLog.objects.count(), 1)), INCLUDING thefrom core.models import SiteReport/from core.forms import SiteReportFormimports (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.mdand 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-superseded2026-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.mdfiles 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:
git statusclean;git log --oneline -4shows the 3 removal commits on top of777c7c6.USE_SQLITE=true python manage.py makemigrations --check --dry-run→ "No changes detected".USE_SQLITE=true python manage.py check→ no issues.USE_SQLITE=true python manage.py migrateon a fresh local SQLite →0018_delete_sitereportapplies with no error; thenUSE_SQLITE=true DJANGO_DEBUG=true python manage.py test core.tests -v 2→ 193 OK.- Grep-clean (Task 2 Step 4 command) → no output beyond the 3 migration files.
- 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). - On Konrad's explicit approval: he pushes; deploy = pull →
migrate(dropscore_sitereport) → restart last. No collectstatic (nostatic/change).
Notes
- DRY/YAGNI: no replacement built (future separate rethink — captured). No new tests beyond the 2 rewritten flow tests.
- Migration discipline:
0018is autogenerated, never hand-edited;0013/0014are 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.