38686-vm/docs/plans/parked-work.md
Konrad du Plessis e71109d27e docs: scrub SiteReport from CLAUDE.md + park rebuild (capture doc)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 02:11:37 +02:00

310 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Parked / deferred work
> Updated 15 May 2026. A small index of features that are designed,
> half-built, blocked on input, or pending an operator step. When a
> fresh session opens, glance here first to see what's already on
> the workbench.
---
## ⏸ Paused — ready to execute (not started, not pushed)
### 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). The
post-attendance flow now simply returns to the dashboard. A 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, which stays on
disk).
**Removal status (local only, HARD STOP — not pushed):** Tasks 1-3
done — model/table/UI/routes deleted, migration
`0018_delete_sitereport` drops `core_sitereport`, suite **193 OK**.
Un-pushed pending Konrad's local verification (destructive migration
on the daily-use attendance path).
---
## ✅ Shipped & live — Manager / Salaried bundle (17 May 2026)
### Manager / Salaried Pay (+ pay-type filter + Salary auto-scope + Pay Salary quick action)
**Status:** Brainstormed + designed + planned + **fully
implemented**, every task two-stage code-reviewed, **PUSHED and
DEPLOYED**. The 36-commit bundle `d7015b9..4c25011` is live on
production (`https://foxlog.flatlogic.app/`) and **Konrad verified it
"all working well" on 17 May 2026**. `origin/ai-dev` HEAD `80d96d7`
== prod (only delta over `4c25011` is doc breadcrumbs). Migrations
`0016`/`0017` applied; `static/css/custom.css` collected. **209/209
tests green.** Design/plan docs: `2026-05-15-manager-salaried-pay-*`,
`2026-05-16-managers-paytype-filter-*`,
`2026-05-16-salary-autoscope-picker-*`,
`2026-05-16-pay-salary-quick-action-*` (all under `docs/plans/`).
Nothing pending on this bundle — fully done.
**What it does:** Lets a manager / salaried worker be paid a fixed
monthly amount without ever logging attendance. A new
`Worker.pay_type` (`'daily'` default | `'fixed'`) marks managers;
they're excluded from attendance + absence pickers ONLY (NOT payroll
modals / TeamForm — they must stay payable), and paid via a new
`Salary` `PayrollAdjustment` type (project-required; "Pay
Immediately" → isolated PayrollRecord like New Loan; unpaid nets via
`_process_single_payment`). The report shows a separate per-project
"Management / Salaried Cost" line — never merged into WorkLog-derived
daily labour cost, so all existing money math is provably unchanged.
"Manager / Salaried" is a Path-A display-only label (model stays
`Worker`). Migrations `0016_worker_pay_type`, `0017_alter_payrolladjustment_type`.
**To resume:** the push is done; the remaining step is the **Flatlogic
VM deploy** (ordered steps in the Status block above — migrate +
collectstatic + restart-last). After Gemini/Konrad deploys, verify on
`https://foxlog.flatlogic.app/` using the manual checklists in the four
design docs, then update this file's "Production status" + the CLAUDE.md
"What's mid-flight" breadcrumb to "fully caught up at `4c25011`". No
code changes pending — implementation is complete and reviewed.
Now also includes a display-only pay-type filter (design
`docs/plans/2026-05-16-managers-paytype-filter-design.md`, plan
`docs/plans/2026-05-16-managers-paytype-filter-plan.md`): a
`/workers/?pay_type=fixed` filter + dropdown and a "Managers only"
client-side toggle on the Add-Adjustment modal picker. Shipped in
the 36-commit push `d7015b9..4c25011`.
Also now: setting the Add-Adjustment **type = Salary** auto-scopes
the picker — pay-type filter → "Managers only", daily rows hidden,
and any selected daily worker auto-unticked (UI guard so a `Salary`
can never silently target a daily worker). Design
`docs/plans/2026-05-16-salary-autoscope-picker-design.md`, plan
`docs/plans/2026-05-16-salary-autoscope-picker-plan.md`. Shipped in
the 36-commit push `d7015b9..4c25011`.
Also now: a home-dashboard admin Quick Actions tile **"Pay Salary"**
deep-links `/payroll/?action=pay-salary` and auto-opens the existing
Pay Salary modal (param stripped after; no re-pop on refresh). Design
`docs/plans/2026-05-16-pay-salary-quick-action-design.md`, plan
`docs/plans/2026-05-16-pay-salary-quick-action-plan.md`. Shipped in
the 36-commit push `d7015b9..4c25011`.
#### Follow-ups from code review (parked, out of scope for this feature)
1. **Atomicity hardening (cross-cutting, pre-existing).**
`add_adjustment`'s three immediate-payment branches — `New Loan`,
`Advance Payment`, and now `Salary` — create a `PayrollAdjustment`
+ `PayrollRecord` and call `_send_payslip_email` WITHOUT a
wrapping `transaction.atomic()`, and `_send_payslip_email`
re-raises on email failure. A mid-sequence failure can therefore
orphan a payment row / 500 after commit. This is **PRE-EXISTING**
(Salary just consistently matches the New Loan / Advance pattern,
it did not introduce the gap) — flagged by Task 5 code review as a
separate hardening ticket, **NOT a Manager/Salaried defect**.
Recommended future cross-cutting fix: wrap each immediate-branch
body in `transaction.atomic()` and swallow/log email failures the
way `process_payment` already does. Out of scope for this feature.
2. **Pre-existing flaky test.** `AbsenceListViewTests` (in
`core/tests.py`) has an `assertContains`/`assertNotContains`
against the `/absences/` list HTML that intermittently failed
~1-in-3 in one run during this feature's execution, then passed on
rerun; 3+ subsequent full-suite runs were clean. A reviewer's
isolation analysis concluded it's pre-existing environmental
nondeterminism (proper Django `TestCase` transactional isolation;
the Manager/Salaried tests cannot perturb it). Low priority:
investigate a possible date/locale or response-rendering
nondeterminism in that test. **NOT introduced by this feature.**
> Note: the **Site-progress logging — rebuild from scratch** parked
> entry above is unaffected by this Manager/Salaried entry (it was
> superseded separately by the 17 May 2026 SiteReport removal).
---
## Production status — ✅ fully caught up (15 May 2026)
Production (`https://foxlog.flatlogic.app/`) is deployed at
`origin/ai-dev` HEAD **`1d224bc`**. Everything is live: the original
SiteReport + Absences feature, all UX polish, the team filters, the
dashboard/report number audit (18 findings), and the
template-comment + subline-clarity fixes. (The 17 May Manager/
Salaried bundle has since shipped too — see the "Shipped & live"
section above.)
**In-progress local-only work (HARD STOP — not pushed):** the
SiteReport / "Log Today's Work" removal — Tasks 1-3 done, suite
**193 OK**, migration `0018_delete_sitereport` drops the table.
Un-pushed pending Konrad's local verify; see the "Site-progress
logging — rebuild from scratch (parked)" entry above + the capture
doc `docs/plans/2026-05-17-site-report-removed-capture.md`.
**Deploy-ordering lesson learned (now in CLAUDE.md):** production
runs `DEBUG=False` → Django's cached template loader holds compiled
templates in memory until the process restarts. The 14 commits of
template fixes were briefly "invisible" because the service was
restarted *before* the code reached the target commit. The fix was
simply to restart AGAIN once `git log` confirmed the right commit.
Correct order is always: `git reset --hard github/ai-dev` → migrate
(if needed) → collectstatic (if `static/` changed) → restart
**last**. See the "DEPLOY ORDERING" bullet in CLAUDE.md's
Flatlogic Deployment section.
---
## 🧊 Backburner — journal/voice features (separate offline track)
> **Decision (15 May 2026):** Konrad parked the entire journal /
> voice-transcript line of work. It's more complex than the rest of
> the app and will be built + tested **offline, on a separate
> track**, NOT mixed into `ai-dev`. **Do NOT start either of these
> in `ai-dev` or in any normal feature session.** They are not
> "blocked waiting on an answer" — they are deliberately deferred
> until Konrad chooses to spin up a dedicated offline effort.
**Important — nothing is in the working app.** Verified 15 May 2026:
zero `JournalEntry` model / views / URLs / templates anywhere in
`core/`, zero Letterly / webhook / `@csrf_exempt` code on `ai-dev`,
latest migration is `0015_absence_project`. There is no dead code,
no feature flag, no migration to reverse. "Removing" these was a
no-op — they were never built into the running app. The working
app (`ai-dev` + production) is completely clean of journal/voice
code.
### Phase A.2 — Manual JournalEntry web UI (deferred)
Designed in the Site Work Logging brainstorm (`~/.claude/plans/
prancy-painting-brook.md`, local). A `JournalEntry` model + manual
entry form/list/edit, admin-only. Open question if/when resumed:
**Q7 "Who is Vi?"** — drives whether `JournalEntry` needs a
`recipient`/`audience` field. Default if never answered: no
recipient field, all-admin-readable. ~1 hour scope. When resumed,
build on the offline track first, not directly in `ai-dev`.
### Phase B — Letterly inbound webhook (deferred)
Designed in the same brainstorm. A `@csrf_exempt` view at
`/webhooks/letterly/` that creates `JournalEntry` rows from voice
transcripts, with HMAC body verification + shared-secret URL token.
Open question if/when resumed: **Q5 — a sample Letterly webhook
payload** (does it include the recording user / project metadata /
audio URL / custom fields?). ~3-4 hours scope + one-off Letterly
account setup. This was always meant to live on the `integrations`
branch, never `ai-dev` — and now it's offline-track until Konrad
revisits.
---
## Small polish follow-ups from the Absences feature
> All 7 cleared on 15 May 2026 (commit `d1d3e15`). Kept the section
> header in place because more polish items will accumulate from
> future code reviews — when they do, append here. The shipped items
> are summarized under "Recently shipped" below.
---
## Defaulted (not blocking — flag if you disagree)
The Q9 (photos on site reports) and Q4 (per-project metric
templates) defaults from the Site Work Logging brainstorm are now
**obsolete** — the SiteReport feature was removed 17 May 2026. Both
were open design considerations for that feature; they are captured
(along with the rest of the design rationale) in
`docs/plans/2026-05-17-site-report-removed-capture.md` for whoever
rebuilds site-progress logging from scratch.
---
## Recently shipped (for context, so a fresh session knows what just landed)
- **Dashboard + report numbers audit** (commits `18c75b2``c02edce`,
15 May 2026): Konrad spotted that the home dashboard's "Paid This
Month" actually showed the last 60 days. A thorough audit found
17 more issues across the home dashboard, payroll dashboard, and
report. All 18 fixed in 7 commits: calendar-month math, outstanding
totals aligned between dashboards, `Project.name` dedupe (was
silently merging same-named projects), `timezone.localdate()`
replacing `timezone.now().date()` (SAST drift fix), off-by-one
on "last 7 / 60 days" windows, dead `year_projects`/`year_teams`
context removed, `_company_cost_velocity` rewritten as single SQL
aggregate, signed/coloured adjustment amounts on report tables
(Bonus vs Deduction now visually distinct), "Outstanding Now"
hero card subline now shows scope when filters active, dual
daily_rate code paths documented in CLAUDE.md. 160 → 173 tests
(+13 regression).
- **Absence-feature polish pass** (commit `d1d3e15`, 15 May 2026):
Seven small cleanups in one focused commit. (1) Deleted unused
`AbsenceQuickForm` (Round C made it obsolete). (2) Extracted
`_build_team_workers_map(user)` helper using `Prefetch(..., to_attr=...)`,
fixing N+1 on both `attendance_log` and `absence_log` (one query
instead of one-per-team). (3) `absence_list` now calls the existing
`_user_can_log_absences` helper. (4) Removed undefined
`var(--badge-neutral-bg)` CSS wrapper — fallback hex was always
winning anyway. (5) `conflicting_worklogs()` rewritten as a single
query with Python-side pair-set filtering — 25-query → 2-query for
a 5×5 form. (6) Extracted `_apply_absence_filters(qs, request)`
shared by `absence_list` + `absence_export_csv` so adding a filter
param now touches one place, not two. (7) `site_report_detail.html`
paid-check icon uses `text-success` instead of background-color
variable as foreground (WCAG contrast fix). 157/157 tests still
green.
- **Day name in Work Log Payroll modal header** (commit `70fa085`,
15 May 2026): The modal opened from /history/ now shows
"Friday, 15 May 2026" instead of "2026-05-15". Pure server-side
`strftime` on the already-loaded `log.date` — zero overhead.
- **Team filters on `/workers/` and `/history/`** (commits `4b57cff`,
`398a5b2`, 15 May 2026): Both pages now have a Team dropdown in
their filter row. `/workers/?team=<id>` filters by Team.workers
M2M membership (who's CURRENTLY on the team); `/history/?team=<id>`
filters by WorkLog.team FK (logs TAGGED with the team when
created — different semantics, intentional). Both accept
`team=none` for the "no team assigned" / "ad-hoc work log" case.
CSV export at `/history/export/` honours the same param.
+8 regression tests.
- **Absences UX polish** (commits `4368e53`, `02c6d4d`, 15 May 2026):
Two production-found bugs fixed. (1) Absence-form team filter was
hiding ALL workers when a team was selected — switched the JS to
read `<input name="workers">[value]` directly instead of going
through `data-worker-id` (proven attendance-form pattern). (2)
Reasons multi-checkbox dropdown on `/absences/` was rendering
behind the table — lifted the filter card with
`position: relative; z-index: 10` so the whole card sits above
its sibling table card in the stacking order. See the Coding
Style section in CLAUDE.md for both gotchas.
- **Worker Absences feature** (commits `bf6f0a5``27fe05e`,
14 May 2026): Complete absence-tracking system. 8 reason choices,
optional project FK (auto-attributes paid-absence Bonus
adjustments to the project), date-range logging with weekend
toggles, supervisor scoping (admin sees all; supervisors see
their teams). Standalone `/absences/log/` form + "Submit + Log
Absences" shortcut on attendance form. List/edit/delete + CSV
export. Worker-detail "Absences" tab with YTD totals. Dashboard
alert card "X absent in last 7 days". Bidirectional cascade with
PayrollAdjustment via `_sync_absence_payroll_adjustment` helper
(single-chokepoint design, transaction.atomic-wrapped). 12
commits pushed to `origin/ai-dev`. Test count: 85 → 149 (+64
tests). Migrations `0013_add_site_report`, `0014_add_absence`,
`0015_absence_project` are LIVE on production as of late 14 May
2026.
- **Phase A.1 — SiteReport** (commit `864ae72`, 14 May 2026):
Model, migration `0013_add_site_report.py`, form, two-step flow
from attendance log, 16 new tests. `CLAUDE.md` updated with model
summary + `site_report_schema.py` pattern section.
- **Pastel soft-fill payroll action buttons** (commit `6c6ade9`,
24 Apr 2026): unified Worker Lookup / Batch Pay / Add Adjustment
/ Price Overtime treatment.
- **Path A display-only rename** (UX Polish Pass, Apr 2026):
"New Loan" / "Advance Payment" / "Advance Repayment" display as
"Loan" / "Advance" / "Advance Repaid" — but **DB values are
unchanged**. See "UI-vs-DB naming drift" section in `CLAUDE.md`
before writing any filter/comparison on `PayrollAdjustment.type`.
---
## Original Site Work Logging brainstorm
The full Q1-Q13 question list and design rationale lives in
`~/.claude/plans/prancy-painting-brook.md` (local — deliberately
not committed to the repo, since the parts that matter have been
absorbed into `CLAUDE.md` and this file). If Konrad answers Q5 or
Q7 later, refer back to that file for the original framing.