A task with status='' caused the Kanban byColumn memo to throw
TypeError: undefined is not an object (out[t.status].push).
Defensive UI: bucket unknown statuses into backlog with a console
warning. Defensive API: reject PATCHes with invalid status values
(including empty string) with a 400. Cleaned up two existing
empty-status rows in PB.
Adds the visible front-end and the worker back-end for the agent
task system. The PocketBase schema, API routes, and ToastProvider
were already in the previous commit; this commit completes the
picture.
UI
- app/tasks/{page,KanbanBoard,TaskCard,CardModal,TaskDetail,api,columns}
Full Kanban with six columns (Backlog, Ready, In Progress,
Blocked, Done, Failed), drag-and-drop between columns, quick-add
chips, type/priority filters, task detail panel with retry/cancel/
delete, 5s polling, transition toasts.
- components/NavBar.tsx: 'Tasks' link in primary nav.
- app/bizportal-2/BizPortal2Explorer.tsx: 'Send to Kanban' button
on the result view — dispatches the current search to the agent
queue as a 'ready' task.
Back-end
- scripts/task_worker.py: PocketBase polling worker with two
lanes. Deterministic handles bizportal_search/reserve, pdf_generate,
email_send. Custom lane writes to /tmp/agent_inbox/ for the
LLM agent to pick up. Atomic claim via PB filter, auto-retry up
to 3x, structured log on each task.
- scripts/bizportal_service.py: bugfix — _is_logged_in was using
a non-CSS selector so session-recovery was a no-op. Also made
the login flow re-runnable mid-session (handles BizPortal's
~20-minute session timeout transparently).
- scripts/init_pb_schema.py: duration_ms no longer capped at 0.
Verified end-to-end:
- CK search via worker: 1 company, 3 directors, ~10s
- ID search via worker: 4 companies, 7 total directors, ~34s
- Auto-retry + status transitions + toast fires
- DirectorAppointmentTab: table + 'Acceptance Letter' modal with company/director dropdowns
- GET/POST /api/appointments: create record, generate HTML letter, convert to PDF via LibreOffice, email to director via himalaya SMTP
- PATCH /api/appointments/[id]: save signature PNG, embed in signed letter PDF, mail to all company directors
- GET /api/appointments/sign/[token]: public token lookup
- /sign/[token]/page.tsx: public signature canvas (mouse + touch)
- ysterbaard_director_appointments PB collection (requires manual schema setup)
- Section 3 'Director Appointment' now active in ResolutionExplorer
- NEXT_PUBLIC_APP_URL added to .env.local
Uncommitted work in progress at the point of starting the Kanban
project. Captures:
- Registration wizard refactor + multi-step routing
- structured physical/postal address fields on companies and directors
- appointments flow (sign + verify)
- document type manager + upload/panel for companies and directors
- status actions, approve button, action/dropdown buttons
- step2 review page
tsconfig.tsbuildinfo is now ignored (build artefact).
Replaces the brittle agent-driven /api/bizportal/search queue with a
deterministic Python FastAPI service that drives BizPortal via a real
headless Chromium. The old flow is untouched.
What this adds:
- scripts/bizportal_service.py — FastAPI + Playwright service with
/search, /reserve, /health endpoints. Persistent login session
via storage_state.json. Locator-based selectors (vs eval JS).
- app/bizportal-2/ — new /bizportal-2 page, synchronous fetch
(no polling), shows round-trip duration, mirrors the existing
bulk-add + link UX.
- app/api/bizportal-2/{search,reserve,health}/route.ts — thin
proxies to the Python sidecar.
- scripts/README.md, .env.example, .gitignore, requirements.txt.
- .gitignore: ignore tsconfig.tsbuildinfo (build artefact).
Wiring:
- components/NavBar.tsx: 'BizPortal 2.0' nav entry with TEST badge.
- app/bizportal/page.tsx: 'Try BizPortal 2.0' action link in
header so the new option is discoverable from the existing page.
Run: cd scripts && source .venv/bin/activate && uvicorn
bizportal_service:app --port 7001
Add a conditional cleanup step to remove the public directory if it does not exist or contains no files. This reduces the final docker image size since Next.js standalone builds automatically inline public assets when they are present, making the copied public directory often redundant.