Commit graph

120 commits

Author SHA1 Message Date
DEls
9a6aed50a4 feat(management): add admin planning dashboards
Add turnover, project, retention, and year comparison pages from the supplied workbook, protected by named admin access. Include the local chart gallery, grouped monthly hover, smooth comparison toggle, and monthly income minus cost series.

Require named PocketBase sign-in in every environment and remove the shared PIN bypass. Update access documentation and business-route test fixtures.
2026-10-09 10:16:50 +02:00
DEls
4a8edf1f7e docs(dn): site-decision implementation plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 06:57:29 +02:00
DEls
86abe6b1db docs(dn): stage 2 implementation plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 02:21:41 +02:00
DEls
62bd5af576 docs(dn): stage 1 implementation plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 02:01:47 +02:00
DEls
ab61182ced docs(dn): Avidan matching study and agent tools design
Study of 18 Avidan delivery documents matched by hand against their POs,
with root causes for why nearly every slip is flagged. Design for giving
agents full delivery-note tooling (everything except delete), staged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 01:55:21 +02:00
DEls
cb463c0faa feat(workforce): add timesheet exports and duplicate protection
Add per-site CSV and PDF matrix reports, editable sick-note date ranges, and weekend calendar styling. Preserve verified duplicate uploads for audit while excluding their entries from hours and costs, and prevent repeat files and overlapping daily imports.
2026-10-07 23:53:47 +02:00
DEls
a21c728d61 feat(workforce): add calendar and sick notes 2026-10-07 14:27:44 +02:00
DEls
f72397259c feat(auth): add Office access and restrict Clock users 2026-10-07 14:22:57 +02:00
DEls
8d53e3688c feat(workforce): add active and inactive workers 2026-10-07 13:41:05 +02:00
DEls
0df711875c feat(workforce): add rate history and missing-rate editor 2026-10-07 13:19:50 +02:00
DEls
4922d649ae feat: add remote purchasing MCP to existing app 2026-10-05 02:46:36 +02:00
DEls
9c9a9d1866 docs: plan same-app remote purchasing MCP implementation 2026-10-05 01:22:08 +02:00
DEls
7ffe4eb94d docs: use existing app URL for remote MCP design 2026-10-05 01:16:34 +02:00
DEls
4970e61e70 docs: specify remote purchasing MCP service 2026-10-05 00:51:44 +02:00
DEls
ed69b517e8 Integrate agent work and add dashboard attention indicators
Remove repeated Plant, Financial and Workforce shortcut cards. Add site-scoped Plant and Financial sidebar attention status with automatic refresh.

Include accumulated delivery processing, purchase order recovery, supplier reconciliation, plant data and user management changes and audit records.
2026-10-04 21:53:32 +02:00
DEls
32d4a82cdf feat: add employee search and save pending fleet updates 2026-09-30 00:42:38 +02:00
DEls
11a227bb4e Allow clock users to upload purchase orders for assigned sites 2026-09-29 13:33:46 +02:00
DEls
223da69884 Fix document upload permissions and clock correction origin 2026-09-29 13:26:47 +02:00
DEls
b700252932 fix(auth): verify PocketBase backup key before migration 2026-09-29 07:01:15 +02:00
DEls
f5259d852f feat(auth): add PocketBase logins and site access controls 2026-09-28 23:51:47 +02:00
DEls
3ba004ffd3 fix(dn): harden delivery-note matching, posting and PO reconciliation
Fresh duplicate check before posting, dismiss route sets stage, free accessory
lines no longer crash promotion, readiness treats only posted notes as terminal,
document-reference/usable-PO helpers, PO delivery reconciliation API + docs and
audit register.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-28 21:16:24 +02:00
DEls
fecc885d3e docs(ask): agent actions
docs/modules.md §7.1: threads, memory, files, free queries/virtual
datasets, the propose/confirm/apply/undo gateway, both PDF export paths
(manual answer export and Ask-made reports via make_pdf_report),
images, and change requests — with the actor/x-ask-user model and the
three new PocketBase collections.

docs/data-model.md §4.17: field-by-field ask_threads/ask_messages/
ask_log reference, cross-linked from modules.md via explicit anchors.

docs/api-reference.md §12/§12b: /api/chat's new body shape and its
thread/action/chart/image/pdf SSE events, plus every new agent-actions
route (picker, threads, attachments, confirm-card actions, memories,
change requests) and their auth-matrix entries; the pb-files proxy
entry now notes its ask_messages admin-token path.

README.md: the intro, the modules table (adds Change requests), and
the API surface summary now describe an agent that acts, not just a
read-only chat.

Also marks Tasks 1–12 and 9b's implementation checkbox done in the plan
doc (docs/superpowers/plans/2026-09-26-ask-agent-actions.md) — the
remaining unchecked box is 9b's live check, skipped because
ask_threads/ask_messages/ask_log aren't applied on the target
PocketBase yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:32:53 +02:00
DEls
88011eb5b8 chore(ask): configurable Claude model
provider-anthropic.ts's model id is now overridable via ANTHROPIC_MODEL
(default unchanged: claude-opus-5, still the current model per the
claude-api skill — this was a configurability gap, not a stale model
id). docs/operations.md documents the new env var and a 5-prompt
comparison set for evaluating providers/models against each other.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:27:32 +02:00
DEls
ba75c65795 feat(ask): download Ask-made PDF reports
Added mid-plan (docs/superpowers/plans/2026-09-26-ask-agent-actions.md,
new Task 9b — image generation from Task 9 is untouched): the user can
ask for any custom report and get a finished PDF, e.g. "give me a PDF
on fuel at The Hills for September", not just a manual export of
whatever is on screen (Task 8).

make_pdf_report({ title, subtitle?, filename? }) is the model's own
"this answer is a finished report" signal, called last once it has
already built the report (headings, narrative, KPI tables quoting tool
display strings, charts via make_chart). extractPdfMeta splits the
meta out the same way extractCharts/extractActionCard already do: the
model gets an ack, the browser gets the full meta via a new `event:
pdf`. The assistant's ask_messages row stores it in the existing
`parsed` json field, so reopening the thread still shows the report
cover and Download button — no schema change.

chat-view.tsx renders a report cover (wordmark, title, subtitle, date,
"Prepared by Ask for {name}") and a prominent "Download PDF · {title}"
button reusing Task 8's answerToPdf (same page-slicing, so a chart is
never cut across pages), and auto-downloads once per turn via
nextPdfTurnToDownload — a pure decision function, unit-tested directly
since this codebase's component tests use renderToStaticMarkup rather
than interactive DOM testing.

Live check skipped: needs a thread, which needs ask_threads (not yet
applied). Covered by unit tests (tool meta, the route's pdf event and
persistence, and the once-per-turn auto-download decision) instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:25:00 +02:00
DEls
7299f81c63 docs(ask): agent actions plan
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 17:42:46 +02:00
DEls
16652bb36c fix(auth): remove the ?demo=1 PIN bypass
The query string skipped the PIN gate on any URL. The PIN is now the
only way in. Docs updated; the login plan notes there is no fixed
cutover date while the app is in on-site testing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 17:37:20 +02:00
DEls
46e9e3dbc4 docs(ask): reports and shortcuts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 09:57:28 +02:00
DEls
e83a0d302c fix: overview counts, wrong-unit fuel limits, meter guard, en-ZA numbers, vehicle merge, docs
- Overview/Plant tiles and pages share one vehicle bucket rule (blank unit_type = fleet)
- Plant landing equipment figures were always 0; hour diffs now reject 0 baselines and >24h/day jumps
- km/L limits on plant (and L/h on vehicles) report unit-mismatch instead of false alerts
- formatPercent/formatDecimal replace display toFixed (en-ZA)
- POST /api/vehicles/[id]/merge + Merge duplicate dialog (dry run first)
- Attention links go to equipment vs vehicles by unit type; PO upload view link fixed
- Conditional hook fixed in DN document view; ESLint flat config (next lint removed in 16)
- Docs: current modules/routes/env vars; login + Ask reports plans

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 22:04:08 +02:00
DEls
5cdfaa3128 feat(clock): review missed scan-outs without changing phone flow 2026-09-25 07:27:12 +02:00
DEls
e68e2d5dab docs(clock): plan daily shifts and exception review 2026-09-25 01:56:35 +02:00
DEls
d694dde501 docs(clock): design daily shifts and missed scan review 2026-09-25 01:50:25 +02:00
DEIs
87f75dd98f feat(plant): fleet map page with GPS-denormalised vehicles, clustering, and tile switcher
Adds /dashboard/plant/map: a Leaflet view of every vehicle scanned from
the MEGA Clock kiosk, plotted at its last known position.

Schema
- vehicles.last_latitude / last_longitude / last_scanned_at /
  last_location_accuracy_m denormalised off fuel_logs so the map page
  renders every vehicle in a single query. Same best-effort pattern as
  attention_dismissed_at; updated by /api/clock/vehicle-scan after every
  successful scan. PB init defaults new number cols to 0 (not null) —
  filter uses 'last_latitude > 0 || last_latitude < 0' to mean 'real fix'.
- Indexed via idx_vehicles_last_scanned.

Map UI
- Custom SVG markers per unit_type: car silhouette for fuel, tractor
  silhouette for plant, 24x24, drop-shadowed.
- Clustering via react-leaflet-cluster over Leaflet.markercluster.
  Cluster icons colour-ramp with size (green->sky->amber->rose) and
  scale 32->54px.
- Three-button base-layer switcher: Street (OSM), Dark (ESRI Dark
  Gray Canvas — CartoDB watermarks tiles without a key), Satellite
  (ESRI World Imagery). Last choice persists in localStorage.
- Site pins: small purple map-marker divIcon, click flies the map to
  zoom level 15.
- Empty-state is a floating chip on the map rather than a full-page
  card so the map is always visible.

APIs
- /api/clock/vehicle-scan: stores the GPS fix on the fuel_logs row
  AND denormalises onto the vehicle when present; missing-coord
  scan leaves last_* untouched.

Sidebar: new Map entry under Plant group.
2026-09-22 21:39:25 +02:00
DEls
a589b14ac9 feat(clock): enhance vehicle scan API with GPS support
Updated the vehicle scan API to include optional GPS coordinates (latitude, longitude) and location accuracy. The API now validates these fields, ensuring both latitude and longitude are provided together and within valid ranges. Enhanced error handling for invalid inputs and updated the API reference documentation accordingly. Additionally, modified the FleetScanner component to capture GPS data and reflect its status in the UI.
2026-09-22 19:59:44 +02:00
DEls
3077223d38 feat(clock): add MEGA Clock API endpoints and update documentation
Introduced three new server-only API endpoints for the MEGA Clock PWA:
- `POST /api/clock/scan` for clocking workers in and out via badge scanning.
- `POST /api/clock/vehicle-lookup` for looking up vehicle details using QR codes.
- `POST /api/clock/vehicle-scan` for logging odometer readings from scanned vehicles.

Updated the README and API reference documentation to include these new endpoints and their functionalities. Replaced the placeholder for fleet scanning in the ClockView component with the new FleetScanner component.
2026-09-22 12:42:39 +02:00
DEls
252dd7eb6a fix(dn): let staged ingestion run against the real PocketBase schema
Three faults stopped the staged pipeline from ingesting anything once it
met a live PocketBase rather than a test double.

PocketBase treats 0 as blank for a required number field, and two staged
fields legitimately hold zero: dn_batch_sources.ordinal is 0 for the
first source in a batch, and dn_batch_pages.region is 0 for a whole-page
read. Both creates were rejected with validation_required, so the first
file of every upload failed and no page ever persisted. The migration now
declares them optional, and pb-relax-dn-zero-fields.py relaxes instances
that already have them required.

The staged runner verifies its batch lease and commits the protected
write in one transaction, which needs PocketBase's batch API. That is off
by default, so the runner failed closed with
dn_pipeline_transactions_required and no deployment note explained why.
pb-enable-batch-api.py turns it on and raises the 3s transaction timeout,
which is tight for a commit carrying page images.

compare-dn-pipelines used top-level await while resolving as CJS, so the
command in the rollout runbook always failed to transform. Renamed .mts.

The tests missed all of this because they mock PocketBase, and the mock
accepts what the real server rejects.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 23:49:58 +02:00
DEls
da756f5224 feat(worker-qr): real PDF + CSV batch export
Replace the off-screen CSS grid + window.print() in the worker QR
batch dialog with two on-click exports:

  - Download PDF — A4 portrait, 2x5 grid of CR80 (85x54mm) cards per
    page, brand orange header, generated with pdf-lib (dynamically
    imported, same pattern as src/lib/photo-pdf.ts).
  - Download CSV — UTF-8 BOM, columns employee_no, name, position,
    worker_type, project_code, qr_payload via Papa.unparse.

The single-card print on the worker detail page still uses
window.print() — one card has no pagination problem, only the batch
view did.

Spec §4.4 updated to match the new output mechanism and explain the
mobile failure mode that prompted the change.

New tests (8) cover the CSV builder (header order, qr_payload ==
employee_no per spec §4.1, RFC 4180 quoting, empty input) and the PDF
builder (empty input throws, valid %PDF output, 11 workers paginate
onto 2 pages, fake PNG injection so layout is testable without qrcode).
2026-09-20 20:43:10 +02:00
DEls
5d094183e6 feat: add delivery note posting and pipeline comparison 2026-09-19 18:45:30 +02:00
DEls
7ab55703b4 docs: plan hybrid delivery note ingestion 2026-09-18 18:43:52 +02:00
DEls
fb8ff612e8 docs: design hybrid delivery note ingestion 2026-09-18 18:34:57 +02:00
DEls
8d1452ae6f docs: add the Hermes brief for the needs-attention queue
Measured all 54 blocked notes first. Only 27 are agent work:

  24  not_duplicate      the same Vula invoices uploaded three times in
                         different groupings. Resolving these would
                         double-count real deliveries.
  22  product_matched    genuine decisions -> the agent
   9  units_convertible  pipes sold by the each, ordered by the metre.
                         The 2.5 in 'IJ Pipes 450x2.5' is the length, so
                         1 ea = 2.5 m. A conversion rule, not a judgement.
   5  po_resolved        misread order numbers -> the agent proposes only

The brief leads with the rule that governs it: a wrong product match is
worse than a block, because it applies the wrong rate to a supplier
payment. It names both real wrong matches from this data, ranks the
evidence that is actually decisive (quantity pairing first, word overlap
never), and routes every action through the API so each decision writes
an alias and compounds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 00:23:47 +02:00
DEls
d02feee57f feat(dn-batches): one batch per tick by default
A tick is fully sequential: one vision call per page, then each note's
checks and promotion in turn. Claiming three batches of a 13-page scan
can approach the route's 300s ceiling, and a tick that times out leaves
its batch to be reclaimed and re-read, paying for those pages twice.

One batch per tick also makes n8n's execution log directly readable —
one run, one document, one outcome. DN_TICK_BATCH_SIZE still overrides
it once the page-count distribution is known.

34 batches at a 1-minute schedule is ~34 minutes rather than ~12.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:54:32 +02:00
DEls
ba6ce2bf2e docs: add the MiniMax handoff brief for DN inbox tasks 6-11
Constrains the implementer away from the two failure modes that have
already bitten this work: writing to production PocketBase, and guessing
when the plan does not match the code. Also states plainly that a wrong
product match costs money and a block does not, since Task 6 is where
that trade-off lives.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:30:16 +02:00
DEls
c32b41506e docs(delivery-notes): record the Task 3 fixture files the plan missed
Making mass_unit required breaks typecheck in three test files the task's
file list did not name. Anyone re-running Task 3 in isolation hits the
same failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:04:39 +02:00
DEls
b1ce964f39 docs(delivery-notes): add the inbox corrections implementation plan
Twelve tasks against the nine defects the first production run exposed.

Task 6 is the core: a three-outcome PO item matcher (matched / ambiguous /
none) with a dimension gate, where the set of integers >= 100 must match
exactly. Two naive relaxations tested during design both produced wrong
matches -- U PIPES-600 resolving to S&S Pipes-1050, Kabelflex to uPVC Pipe
-- and a wrong match applies the wrong rate to money. The task ships a
regression test asserting no known input returns a wrong match, and the
plan tells the implementer not to move the thresholds without extending
that table first.

Task 7 adds the alias table, keyed on material_code so a human decision
carries onto the next purchase order. That is what makes wording like
"Kabelflex Pipe & Coupling" -> "160mm Sleeves Black" tractable at all.

Task 11 deliberately narrows the original page-accounting guarantee: a
stray page becomes a dismissable open question instead of failing the
batch, because batch 5qzcv6tvgb6cqks promoted 3 of 3 notes correctly and
still read as a failure over a tax invoice cover sheet. The guarantee that
every detected note must promote is unchanged.

Task 12 re-runs all 34 batches and audits every ledger link, so a matcher
regression cannot land silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 20:49:49 +02:00
DEls
1fcd5f0e8e docs(delivery-notes): spec the inbox corrections after the first real run
Ran the pipeline over 34 real documents: 7 processed, 14 blocked, 13 never
picked up. 13 of the 14 blocks were our own bugs.

Root causes, measured from dn_batches.checks:
- 5 batches are tax invoices or collection notes, which carry no delivery
  note number. The pipeline was built assuming every document has one.
- 4 blocked on product_matched; the matcher requires the PO line's first
  token to be 3+ chars, so "IJ Pipes 450x2.5 100D" never matches
  "IJ PIPES-450 X 2.5(100D)".
- 2 on weighbridge_arithmetic comparing nett in kg against quantity in tons.
- 2 on a single stray page condemning notes that promoted correctly.

Critically, two candidate relaxations of the matcher were tested against the
real descriptions and BOTH produced wrong matches (U PIPES-600 to S&S
Pipes-1050; Kabelflex to uPVC Pipe). A wrong match applies the wrong rate to
money, so the fix is a three-outcome matcher -- matched / ambiguous / none --
with a dimension gate, plus a learned alias table for wording that can never
match textually. The specified algorithm was verified against all 23 failing
descriptions with zero wrong matches.

Also found: dn_batches.created is never populated, so board ordering is
arbitrary; and the tick has never claimed a batch, so the n8n schedule is
not running.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 20:41:20 +02:00
DEls
31fbc52c4d chore: 410 the old ocr-result route and archive its workflow export 2026-09-17 15:31:29 +02:00
DEls
49b6547c0c docs(n8n): rewrite for the two-node tick workflow 2026-09-17 15:23:16 +02:00
DEls
aa16422466 Merge fix/delivery-note-units: delivery note unit conversion fix (Phase 1)
Fixes two bugs in delivery-note processing:
1. 1000x unit conversion error: slips quoted in kg but PO lines priced per ton.
2. Off-by-one that paired delivery lines to the wrong PO items.

All 8 plan tasks complete: units module, dn-item-row builder, route rewire,
dual-unit UI rendering, backfill migration (56/57 rows patched), and operational
cleanup. Final state: PO MEGCPO9165 line bxqbo8rs04pjqwc reconciles to
133.5 ton / R22,494.75 across 4 notes (not 1 515% or R10.9 m).

See docs/superpowers/plans/2026-09-17-delivery-note-unit-correctness.md.
2026-09-17 13:05:51 +02:00
DEls
dd0ac47375 docs(delivery-notes): add the phase 2-3 inbox pipeline implementation plan
Seventeen tasks covering the batch collections, the pure segmentation and
check modules, page rendering via poppler, per-page extraction, promotion,
the orchestrator, the tick route, the Kanban board and the batch detail view
with note correction.

The design point: pages are read independently and grouped into notes by the
delivery note numbers printed on them, so a note spanning three pages is one
note and a page belonging to no note blocks the batch. segmentPages throws
rather than return a segmentation that loses a page, and runBatch cannot
reach "processed" unless notes_promoted equals notes_detected -- the check
whose absence let two notes vanish from scan_20260915 while the pipeline
reported success at 95% confidence.

Task 16 step 7 is the regression test for exactly that: force a page read to
fail and confirm the batch lands in needs_attention rather than processed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 12:51:03 +02:00
DEls
6cee861c3e chore: final review polish (trailing newlines, JSDoc, changelog tense) 2026-09-17 12:49:37 +02:00