Vibecode Workelo
track this build5 phases, 15 steps, beginner friendly0%Strip away the branding and Workelo is a workflow engine: journey templates, tasks with owners and due dates relative to a start date, reminders, and a page the new hire actually sees. That part is genuinely a weekend build, and for a five person company a self-hosted version is fine. What breaks is everything at the edges: HRIS sync, Google Workspace and Slack account provisioning, equipment requests routed to IT, document signing, and the audit trail HR wants when someone leaves. Also worth naming the obvious: this is a tool for coordinating other people, so a personal build only pays off if you are the one running onboarding. If you are, you get most of the value; if you need it to talk to your HRIS, you will spend the weekend on OAuth instead of features.
You are building a lean indie version of Workelo. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Workelo · indie build Journey templates of tasks offset from a start or leaving date, launched per employee with owners assigned, reminders for overdue items, and an offboarding view that reports every outstanding access revocation by name and system until each is confirmed by the person responsible for it. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Node 22 with PostgreSQL | journeys are relational, the audit trail is append only, and both want a real database | | Deployment | Self-hosted and private | this holds employee names, start dates, leaving dates and who has access to what | | Time | Dates rather than instants, with an explicit timezone for reminders | a task due on someone's first day is a date, and treating it as an instant shifts it across a timezone | | Email | A transactional provider with a verified domain | reminders that land in spam are tasks that do not get done | | Audit | Append-only completion records with the confirming person | who confirmed a revocation is the record that matters later | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the application and the reminder job. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **PostgreSQL 16 or newer** · free - Why: Journeys are relational and the audit trail is append only. - Get it: Install locally or run it in Docker. - Verify: psql connects and reports 16 or higher - [ ] **A real register of every system people have access to** · free, and it usually takes longer than expected - Why: Offboarding is only as good as this list, and the systems nobody remembers are exactly the ones that stay open. Assembling it is the most valuable part of this build. - Get it: Ask every team what they administer. Include the small ones: a shared inbox, a design tool, a monitoring dashboard, a door code. - Verify: The register exists with an owner per system - [ ] **A transactional email provider with a verified domain** · free tiers cover a small organisation - Why: Reminders and the weekly report both depend on delivery, and internal mail that lands in spam is a task nobody does. - Get it: Create an account, add the DNS records, take the credentials. - Verify: A reminder arrives in an inbox and is not in spam - [ ] **Retention periods for employee records** · free - Why: This holds personal data including leaving dates, and how long you keep it is a decision with legal weight in most places. - Get it: Decide per record kind with a reason. The security audit usually needs a longer period than the personal details, and separating them is the useful part. - Verify: The policy is written with a reason per kind ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Automatic revocation through identity provider integration. - Payroll, benefits and human resources records. - Employment advice of any kind. - Mobile applications. - HRIS and ATS connectors, so employee records get typed in by hand - Automatic account and access provisioning in Google Workspace, Slack, and IT tools, plus the deprovisioning checklist on exit - E-signature and document collection flows that a legal team will accept - The polished employee-facing portal and email design that makes day one feel intentional rather than homemade - Reporting, audit logs, and the GDPR paperwork story your HR lead will ask about If one of those is essential to you, that is the reason to keep paying for Workelo, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Workelo The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me onboarding and offboarding journeys as templates, with the access checklist actually verified to replace Workelo. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Offboarding is the half that matters and the half everyone builds badly. An onboarding task nobody did means a new person waits for a laptop. An offboarding task nobody did means someone who left still has access to your systems, and nobody notices until it matters. So an access revocation task is not complete because someone ticked a box. It is complete when the person who owns that system confirms it, with a timestamp and a name, and the journey reports outstanding revocations loudly until they are. A checklist without verification is theatre, and this is the one place where that is expensive. This also holds personal data about employees, including their leaving. Retention and access controls are part of the build rather than a later addition. ### Stack (fixed, do not substitute) - Runtime: Node 22 with PostgreSQL · journeys are relational, the audit trail is append only, and both want a real database - Deployment: Self-hosted and private · this holds employee names, start dates, leaving dates and who has access to what - Time: Dates rather than instants, with an explicit timezone for reminders · a task due on someone's first day is a date, and treating it as an instant shifts it across a timezone - Email: A transactional provider with a verified domain · reminders that land in spam are tasks that do not get done - Audit: Append-only completion records with the confirming person · who confirmed a revocation is the record that matters later ### Data model (create this before Phase 1) - employees(id, name, work_email, personal_email, role, manager_id, start_on, leaving_on, state) - templates(id, name, kind, active) · kind is onboarding, crossboarding or offboarding - template_tasks(id, template_id, title, description, offset_days, owner_role, category, requires_confirmation) · offset from the start or leaving date - journeys(id, employee_id, template_id, anchor_date, launched_at, completed_at) - tasks(id, journey_id, template_task_id, title, owner_id, due_on, state, completed_at, completed_by, confirmation_note) - access_systems(id, name, owner_role, criticality) · what has to be revoked and who owns it - audit(id, entity, entity_id, at, actor_id, action, detail_json) · append only, no update path - A task marked requires_confirmation cannot be completed by the person it was assigned to without a note saying what they verified. ### Phase 1 · Templates with offsets rather than dates Build: Templates of tasks with an offset in days from an anchor date and an owner expressed as a role rather than a person. Launching a journey for an employee resolves offsets into dates and roles into people. Trap: Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. Done when: A template with offsets produces correct due dates for two employees with different start dates. An owner role resolves to a person at launch and the resolution is recorded. Changing who holds a role does not alter already launched journeys. A template with an unresolvable role blocks launch and names it. Do not build yet: No reminders, offboarding or audit. ### Phase 2 · Reminders that reach the right person Build: A daily job emailing each owner their overdue and upcoming tasks in one message rather than one per task. Escalate to the manager after a configured period. Stop when the task is completed, checked at send time. Trap: Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. Done when: An owner with several overdue tasks receives one email listing them. Completing a task before the send removes it from that message, verified by completing one after queuing. Escalation to the manager happens after the configured period rather than immediately. A completed journey generates no reminders. Do not build yet: No offboarding or verification yet. ### Phase 3 · Offboarding, and access that is verified Build: An access system register with an owner per system. An offboarding journey generates a revocation task per system the employee had. Completing one requires a confirmation note from the system owner, not a tick from the person leaving or their manager. Trap: A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. Done when: An offboarding journey generates a revocation task per system the employee had access to. Completing a revocation requires a note and records who confirmed. A revocation cannot be completed by the departing employee. Outstanding revocations appear in a report that stays visible until every one is confirmed. Do not build yet: No reporting or retention yet. ### Phase 4 · The report nobody can ignore Build: A standing report of every unconfirmed revocation across all offboarding journeys, sorted by how long it has been outstanding and by system criticality. Email it weekly to whoever is accountable. Do not let a journey close with revocations outstanding. Trap: Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. Done when: The report lists every unconfirmed revocation with its age and criticality. A journey cannot be marked complete while a revocation is outstanding, verified by attempting it. The report is emailed weekly to the accountable person. A revocation outstanding beyond a threshold is escalated. Do not build yet: No access control or retention yet. ### Phase 5 · Who can see what Build: Roles limiting what each person sees: an owner sees their own tasks, a manager sees their reports' journeys, an administrator sees everything. Enforce it in handlers and test with direct requests. Keep a leaving date visible only to those who need it. Trap: A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. Done when: A task owner cannot see another employee's journey, tested by direct request. A manager sees their reports and not others. A leaving date is not visible in any list a general owner can reach, verified by inspecting the response. Administrators see everything and that access is logged. Do not build yet: Retention and export come last. ### Phase 6 · Retention, export and the record you keep Build: A retention policy per record kind with its reason, applied by a job. A subject access export producing everything held about one person. Backups. Keep the offboarding audit longer than the personal details, because it is the security record. Trap: Deleting an offboarding journey when its employee record expires destroys the evidence that access was revoked, which is the record you most need if something surfaces later. Separate the retention periods: personal details expire, the revocation audit persists in a form that does not identify the person more than necessary. Done when: Retention removes personal details past their period while preserving the revocation audit. A subject access export produces everything held about one person. A restore reproduces journeys and the audit trail. The retention policy is written with a reason per record kind. Do not build yet: Nothing after this. ### Out of scope (and why) - Integration with identity providers to revoke access automatically, which would be genuinely better than a checklist and is a per-system integration project. - Payroll, benefits and the human resources information system this would sit beside. - Employment advice of any kind. This tracks tasks people decided on; it decides nothing. - Mobile applications, which matter for owners who are rarely at a desk. ### README must contain - That an access revocation is complete only when the system owner confirms it with a note - That a journey cannot close while revocations are outstanding - That template owners are roles resolved at launch rather than named people - That reminders batch per person and escalate rather than repeating - The retention policy per record kind, and why the revocation audit outlives personal details ===== AGENTS.md ===== # Agent instructions · Workelo indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Node 22 with PostgreSQL, Self-hosted and private, Dates rather than instants, with an explicit timezone for reminders, A transactional provider with a verified domain, Append-only completion records with the confirming person. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. - Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. - A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. - Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. - A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. ===== BUILD_PLAN.md ===== # Build plan · Workelo Journey templates of tasks offset from a start or leaving date, launched per employee with owners assigned, reminders for overdue items, and an offboarding view that reports every outstanding access revocation by name and system until each is confirmed by the person responsible for it. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Templates with offsets rather than dates A journey template describes timing relative to a date, so it works for anyone. ### Steps 1. Model tasks as offsets from an anchor Negative offsets for things due before a start date, which is most of onboarding. Files: `src/templates.js` 2. Express owners as roles Resolved at launch, recorded on the task. Named people on templates rot. Files: `src/roles.js` 3. Block launch on an unresolvable role Rather than assigning to nobody, which is how tasks disappear. Files: `src/journeys.js` ### Done when - [ ] Offsets produce correct dates for two different start dates - [ ] Roles resolve to people at launch and the resolution is recorded - [ ] A role change does not alter launched journeys - [ ] An unresolvable role blocks launch and is named ### Watch out - Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. ## Phase 2 · Reminders that reach the right person An overdue task produces exactly one clear reminder to whoever owns it. ### Steps 1. Batch per person per day One message with everything, rather than one per task. Files: `src/reminders.js` 2. Check state at send time Not at queue time. Reminding someone about a task they just finished is the annoying failure. Files: `src/reminders.js` 3. Escalate rather than repeat To the manager after a period. Repetition alone is ignored. Files: `src/escalation.js` ### Done when - [ ] An owner with several overdue tasks receives one email - [ ] Completing before the send removes the task from it - [ ] Escalation happens after the configured period - [ ] A completed journey generates no reminders ### Watch out - Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. ## Phase 3 · Offboarding, and access that is verified Every system someone had access to is revoked and confirmed by the person who owns it. ### Steps 1. Register systems with owners Name, owner role, criticality. This register is the thing that makes offboarding real. Files: `src/systems.js` 2. Generate revocation tasks per system From what the employee actually had, not from a generic list. Files: `src/offboarding.js` 3. Require confirmation with a note From the system owner. A tick from anyone else does not close it. Files: `src/tasks.js` ### Done when - [ ] A revocation task exists per system the employee had - [ ] Completing requires a note and records the confirmer - [ ] The departing employee cannot complete a revocation - [ ] Outstanding revocations stay visible until confirmed ### Watch out - A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. ## Phase 4 · The report nobody can ignore Outstanding access revocations are impossible to miss. ### Steps 1. Build the standing report Across all journeys, sorted by age and criticality. Not per journey. Files: `src/reports.js` 2. Block completion on outstanding revocations The journey is not finished. Allowing it hides the problem. Files: `src/journeys.js` 3. Email it weekly with escalation To someone accountable, with anything old escalated. Files: `src/reports.js` ### Done when - [ ] The report lists unconfirmed revocations with age and criticality - [ ] A journey cannot complete with revocations outstanding - [ ] The weekly report is delivered - [ ] Old revocations escalate ### Watch out - Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. ## Phase 5 · Who can see what Employee records are visible only to the people who need them. ### Steps 1. Enforce roles in handlers Not in templates. Test with direct requests rather than through the interface. Files: `src/auth.js` 2. Restrict sensitive fields A leaving date is not general information. Filter at the query. Files: `src/queries.js` 3. Log administrative access Seeing everything is a permission worth recording the use of. Files: `src/audit.js` ### Done when - [ ] An owner cannot reach another employee's journey - [ ] A manager sees only their reports - [ ] A leaving date is absent from general responses - [ ] Administrative access is logged ### Watch out - A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. ## Not in this build - Automatic revocation through identity provider integration. - Payroll, benefits and human resources records. - Employment advice of any kind. - Mobile applications. ## After v1, if you want it - Identity provider integration for the systems that support it, keeping the checklist for those that do not - A quarterly access review, which uses the same register and catches what offboarding missed - Equipment tracking, since a returned laptop is the other thing nobody chases ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Employee records and the audit trail. Personal data, kept private. DATABASE_URL=postgres://user:pass@localhost:5432/journeys # Required. Internal only. Not publicly indexed. PUBLIC_URL=https://onboarding.internal.example.com # Required. When the daily reminder job runs. Task due dates are dates rather than instants. REMINDER_TIMEZONE=Europe/London # Required. Overdue days before a task escalates to the manager. ESCALATE_AFTER_DAYS=3 # Required. Shorter for access revocations, because an open account is a live risk. REVOCATION_ESCALATE_DAYS=1 # Required. Personal details, per your written policy. RETENTION_MONTHS_PERSONAL=24 # Required. Revocation audit. Longer, because it is the security record. RETENTION_MONTHS_AUDIT=84
You are building a lean indie version of Workelo. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Workelo · indie build Journey templates of tasks offset from a start or leaving date, launched per employee with owners assigned, reminders for overdue items, and an offboarding view that reports every outstanding access revocation by name and system until each is confirmed by the person responsible for it. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Node 22 with PostgreSQL | journeys are relational, the audit trail is append only, and both want a real database | | Deployment | Self-hosted and private | this holds employee names, start dates, leaving dates and who has access to what | | Time | Dates rather than instants, with an explicit timezone for reminders | a task due on someone's first day is a date, and treating it as an instant shifts it across a timezone | | Email | A transactional provider with a verified domain | reminders that land in spam are tasks that do not get done | | Audit | Append-only completion records with the confirming person | who confirmed a revocation is the record that matters later | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the application and the reminder job. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **PostgreSQL 16 or newer** · free - Why: Journeys are relational and the audit trail is append only. - Get it: Install locally or run it in Docker. - Verify: psql connects and reports 16 or higher - [ ] **A real register of every system people have access to** · free, and it usually takes longer than expected - Why: Offboarding is only as good as this list, and the systems nobody remembers are exactly the ones that stay open. Assembling it is the most valuable part of this build. - Get it: Ask every team what they administer. Include the small ones: a shared inbox, a design tool, a monitoring dashboard, a door code. - Verify: The register exists with an owner per system - [ ] **A transactional email provider with a verified domain** · free tiers cover a small organisation - Why: Reminders and the weekly report both depend on delivery, and internal mail that lands in spam is a task nobody does. - Get it: Create an account, add the DNS records, take the credentials. - Verify: A reminder arrives in an inbox and is not in spam - [ ] **Retention periods for employee records** · free - Why: This holds personal data including leaving dates, and how long you keep it is a decision with legal weight in most places. - Get it: Decide per record kind with a reason. The security audit usually needs a longer period than the personal details, and separating them is the useful part. - Verify: The policy is written with a reason per kind ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Automatic revocation through identity provider integration. - Payroll, benefits and human resources records. - Employment advice of any kind. - Mobile applications. - HRIS and ATS connectors, so employee records get typed in by hand - Automatic account and access provisioning in Google Workspace, Slack, and IT tools, plus the deprovisioning checklist on exit - E-signature and document collection flows that a legal team will accept - The polished employee-facing portal and email design that makes day one feel intentional rather than homemade - Reporting, audit logs, and the GDPR paperwork story your HR lead will ask about If one of those is essential to you, that is the reason to keep paying for Workelo, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Workelo The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me onboarding and offboarding journeys as templates, with the access checklist actually verified to replace Workelo. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Offboarding is the half that matters and the half everyone builds badly. An onboarding task nobody did means a new person waits for a laptop. An offboarding task nobody did means someone who left still has access to your systems, and nobody notices until it matters. So an access revocation task is not complete because someone ticked a box. It is complete when the person who owns that system confirms it, with a timestamp and a name, and the journey reports outstanding revocations loudly until they are. A checklist without verification is theatre, and this is the one place where that is expensive. This also holds personal data about employees, including their leaving. Retention and access controls are part of the build rather than a later addition. ### Stack (fixed, do not substitute) - Runtime: Node 22 with PostgreSQL · journeys are relational, the audit trail is append only, and both want a real database - Deployment: Self-hosted and private · this holds employee names, start dates, leaving dates and who has access to what - Time: Dates rather than instants, with an explicit timezone for reminders · a task due on someone's first day is a date, and treating it as an instant shifts it across a timezone - Email: A transactional provider with a verified domain · reminders that land in spam are tasks that do not get done - Audit: Append-only completion records with the confirming person · who confirmed a revocation is the record that matters later ### Data model (create this before Phase 1) - employees(id, name, work_email, personal_email, role, manager_id, start_on, leaving_on, state) - templates(id, name, kind, active) · kind is onboarding, crossboarding or offboarding - template_tasks(id, template_id, title, description, offset_days, owner_role, category, requires_confirmation) · offset from the start or leaving date - journeys(id, employee_id, template_id, anchor_date, launched_at, completed_at) - tasks(id, journey_id, template_task_id, title, owner_id, due_on, state, completed_at, completed_by, confirmation_note) - access_systems(id, name, owner_role, criticality) · what has to be revoked and who owns it - audit(id, entity, entity_id, at, actor_id, action, detail_json) · append only, no update path - A task marked requires_confirmation cannot be completed by the person it was assigned to without a note saying what they verified. ### Phase 1 · Templates with offsets rather than dates Build: Templates of tasks with an offset in days from an anchor date and an owner expressed as a role rather than a person. Launching a journey for an employee resolves offsets into dates and roles into people. Trap: Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. Done when: A template with offsets produces correct due dates for two employees with different start dates. An owner role resolves to a person at launch and the resolution is recorded. Changing who holds a role does not alter already launched journeys. A template with an unresolvable role blocks launch and names it. Do not build yet: No reminders, offboarding or audit. ### Phase 2 · Reminders that reach the right person Build: A daily job emailing each owner their overdue and upcoming tasks in one message rather than one per task. Escalate to the manager after a configured period. Stop when the task is completed, checked at send time. Trap: Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. Done when: An owner with several overdue tasks receives one email listing them. Completing a task before the send removes it from that message, verified by completing one after queuing. Escalation to the manager happens after the configured period rather than immediately. A completed journey generates no reminders. Do not build yet: No offboarding or verification yet. ### Phase 3 · Offboarding, and access that is verified Build: An access system register with an owner per system. An offboarding journey generates a revocation task per system the employee had. Completing one requires a confirmation note from the system owner, not a tick from the person leaving or their manager. Trap: A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. Done when: An offboarding journey generates a revocation task per system the employee had access to. Completing a revocation requires a note and records who confirmed. A revocation cannot be completed by the departing employee. Outstanding revocations appear in a report that stays visible until every one is confirmed. Do not build yet: No reporting or retention yet. ### Phase 4 · The report nobody can ignore Build: A standing report of every unconfirmed revocation across all offboarding journeys, sorted by how long it has been outstanding and by system criticality. Email it weekly to whoever is accountable. Do not let a journey close with revocations outstanding. Trap: Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. Done when: The report lists every unconfirmed revocation with its age and criticality. A journey cannot be marked complete while a revocation is outstanding, verified by attempting it. The report is emailed weekly to the accountable person. A revocation outstanding beyond a threshold is escalated. Do not build yet: No access control or retention yet. ### Phase 5 · Who can see what Build: Roles limiting what each person sees: an owner sees their own tasks, a manager sees their reports' journeys, an administrator sees everything. Enforce it in handlers and test with direct requests. Keep a leaving date visible only to those who need it. Trap: A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. Done when: A task owner cannot see another employee's journey, tested by direct request. A manager sees their reports and not others. A leaving date is not visible in any list a general owner can reach, verified by inspecting the response. Administrators see everything and that access is logged. Do not build yet: Retention and export come last. ### Phase 6 · Retention, export and the record you keep Build: A retention policy per record kind with its reason, applied by a job. A subject access export producing everything held about one person. Backups. Keep the offboarding audit longer than the personal details, because it is the security record. Trap: Deleting an offboarding journey when its employee record expires destroys the evidence that access was revoked, which is the record you most need if something surfaces later. Separate the retention periods: personal details expire, the revocation audit persists in a form that does not identify the person more than necessary. Done when: Retention removes personal details past their period while preserving the revocation audit. A subject access export produces everything held about one person. A restore reproduces journeys and the audit trail. The retention policy is written with a reason per record kind. Do not build yet: Nothing after this. ### Out of scope (and why) - Integration with identity providers to revoke access automatically, which would be genuinely better than a checklist and is a per-system integration project. - Payroll, benefits and the human resources information system this would sit beside. - Employment advice of any kind. This tracks tasks people decided on; it decides nothing. - Mobile applications, which matter for owners who are rarely at a desk. ### README must contain - That an access revocation is complete only when the system owner confirms it with a note - That a journey cannot close while revocations are outstanding - That template owners are roles resolved at launch rather than named people - That reminders batch per person and escalate rather than repeating - The retention policy per record kind, and why the revocation audit outlives personal details ===== AGENTS.md ===== # Agent instructions · Workelo indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Node 22 with PostgreSQL, Self-hosted and private, Dates rather than instants, with an explicit timezone for reminders, A transactional provider with a verified domain, Append-only completion records with the confirming person. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. - Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. - A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. - Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. - A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. ===== BUILD_PLAN.md ===== # Build plan · Workelo Journey templates of tasks offset from a start or leaving date, launched per employee with owners assigned, reminders for overdue items, and an offboarding view that reports every outstanding access revocation by name and system until each is confirmed by the person responsible for it. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Templates with offsets rather than dates A journey template describes timing relative to a date, so it works for anyone. ### Steps 1. Model tasks as offsets from an anchor Negative offsets for things due before a start date, which is most of onboarding. Files: `src/templates.js` 2. Express owners as roles Resolved at launch, recorded on the task. Named people on templates rot. Files: `src/roles.js` 3. Block launch on an unresolvable role Rather than assigning to nobody, which is how tasks disappear. Files: `src/journeys.js` ### Done when - [ ] Offsets produce correct dates for two different start dates - [ ] Roles resolve to people at launch and the resolution is recorded - [ ] A role change does not alter launched journeys - [ ] An unresolvable role blocks launch and is named ### Watch out - Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. ## Phase 2 · Reminders that reach the right person An overdue task produces exactly one clear reminder to whoever owns it. ### Steps 1. Batch per person per day One message with everything, rather than one per task. Files: `src/reminders.js` 2. Check state at send time Not at queue time. Reminding someone about a task they just finished is the annoying failure. Files: `src/reminders.js` 3. Escalate rather than repeat To the manager after a period. Repetition alone is ignored. Files: `src/escalation.js` ### Done when - [ ] An owner with several overdue tasks receives one email - [ ] Completing before the send removes the task from it - [ ] Escalation happens after the configured period - [ ] A completed journey generates no reminders ### Watch out - Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. ## Phase 3 · Offboarding, and access that is verified Every system someone had access to is revoked and confirmed by the person who owns it. ### Steps 1. Register systems with owners Name, owner role, criticality. This register is the thing that makes offboarding real. Files: `src/systems.js` 2. Generate revocation tasks per system From what the employee actually had, not from a generic list. Files: `src/offboarding.js` 3. Require confirmation with a note From the system owner. A tick from anyone else does not close it. Files: `src/tasks.js` ### Done when - [ ] A revocation task exists per system the employee had - [ ] Completing requires a note and records the confirmer - [ ] The departing employee cannot complete a revocation - [ ] Outstanding revocations stay visible until confirmed ### Watch out - A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. ## Phase 4 · The report nobody can ignore Outstanding access revocations are impossible to miss. ### Steps 1. Build the standing report Across all journeys, sorted by age and criticality. Not per journey. Files: `src/reports.js` 2. Block completion on outstanding revocations The journey is not finished. Allowing it hides the problem. Files: `src/journeys.js` 3. Email it weekly with escalation To someone accountable, with anything old escalated. Files: `src/reports.js` ### Done when - [ ] The report lists unconfirmed revocations with age and criticality - [ ] A journey cannot complete with revocations outstanding - [ ] The weekly report is delivered - [ ] Old revocations escalate ### Watch out - Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. ## Phase 5 · Who can see what Employee records are visible only to the people who need them. ### Steps 1. Enforce roles in handlers Not in templates. Test with direct requests rather than through the interface. Files: `src/auth.js` 2. Restrict sensitive fields A leaving date is not general information. Filter at the query. Files: `src/queries.js` 3. Log administrative access Seeing everything is a permission worth recording the use of. Files: `src/audit.js` ### Done when - [ ] An owner cannot reach another employee's journey - [ ] A manager sees only their reports - [ ] A leaving date is absent from general responses - [ ] Administrative access is logged ### Watch out - A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. ## Not in this build - Automatic revocation through identity provider integration. - Payroll, benefits and human resources records. - Employment advice of any kind. - Mobile applications. ## After v1, if you want it - Identity provider integration for the systems that support it, keeping the checklist for those that do not - A quarterly access review, which uses the same register and catches what offboarding missed - Equipment tracking, since a returned laptop is the other thing nobody chases ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Employee records and the audit trail. Personal data, kept private. DATABASE_URL=postgres://user:pass@localhost:5432/journeys # Required. Internal only. Not publicly indexed. PUBLIC_URL=https://onboarding.internal.example.com # Required. When the daily reminder job runs. Task due dates are dates rather than instants. REMINDER_TIMEZONE=Europe/London # Required. Overdue days before a task escalates to the manager. ESCALATE_AFTER_DAYS=3 # Required. Shorter for access revocations, because an open account is a live risk. REVOCATION_ESCALATE_DAYS=1 # Required. Personal details, per your written policy. RETENTION_MONTHS_PERSONAL=24 # Required. Revocation audit. Longer, because it is the security record. RETENTION_MONTHS_AUDIT=84
You are building a production product version of Workelo. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Workelo · product brief ## Problem Strip away the branding and Workelo is a workflow engine: journey templates, tasks with owners and due dates relative to a start date, reminders, and a page the new hire actually sees. That part is genuinely a weekend build, and for a five person company a self-hosted version is fine. What breaks is everything at the edges: HRIS sync, Google Workspace and Slack account provisioning, equipment requests routed to IT, document signing, and the audit trail HR wants when someone leaves. Also worth naming the obvious: this is a tool for coordinating other people, so a personal build only pays off if you are the one running onboarding. If you are, you get most of the value; if you need it to talk to your HRIS, you will spend the weekend on OAuth instead of features. ## Product outcome Journeys that work for any start date, reminders people actually read, and an offboarding process where every access revocation is confirmed by name and stays visible until it is. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Node 20 and a place to run it (a small VPS or a container) - SMTP credentials for reminder emails, or nothing if you accept an in-app inbox only - Someone willing to keep the templates current, which is the actual work ## Explicit non-goals for v1 - Automatic revocation through identity provider integration. - Payroll, benefits and human resources records. - Employment advice of any kind. - Mobile applications. - HRIS and ATS connectors, so employee records get typed in by hand - Automatic account and access provisioning in Google Workspace, Slack, and IT tools, plus the deprovisioning checklist on exit - E-signature and document collection flows that a legal team will accept - The polished employee-facing portal and email design that makes day one feel intentional rather than homemade - Reporting, audit logs, and the GDPR paperwork story your HR lead will ask about ## Success criteria - A revocation is confirmed to require a note and record the confirmer - A journey is confirmed unable to close with revocations outstanding - An owner is confirmed unable to reach another employee's journey - A leaving date is confirmed absent from general responses - A restore is confirmed to preserve the audit trail intact ===== BRIEF.md ===== # Build brief · Workelo The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me onboarding and offboarding journeys as templates, with the access checklist actually verified to replace Workelo. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Offboarding is the half that matters and the half everyone builds badly. An onboarding task nobody did means a new person waits for a laptop. An offboarding task nobody did means someone who left still has access to your systems, and nobody notices until it matters. So an access revocation task is not complete because someone ticked a box. It is complete when the person who owns that system confirms it, with a timestamp and a name, and the journey reports outstanding revocations loudly until they are. A checklist without verification is theatre, and this is the one place where that is expensive. This also holds personal data about employees, including their leaving. Retention and access controls are part of the build rather than a later addition. ### Stack (fixed, do not substitute) - Runtime: Node 22 with PostgreSQL · journeys are relational, the audit trail is append only, and both want a real database - Deployment: Self-hosted and private · this holds employee names, start dates, leaving dates and who has access to what - Time: Dates rather than instants, with an explicit timezone for reminders · a task due on someone's first day is a date, and treating it as an instant shifts it across a timezone - Email: A transactional provider with a verified domain · reminders that land in spam are tasks that do not get done - Audit: Append-only completion records with the confirming person · who confirmed a revocation is the record that matters later ### Data model (create this before Phase 1) - employees(id, name, work_email, personal_email, role, manager_id, start_on, leaving_on, state) - templates(id, name, kind, active) · kind is onboarding, crossboarding or offboarding - template_tasks(id, template_id, title, description, offset_days, owner_role, category, requires_confirmation) · offset from the start or leaving date - journeys(id, employee_id, template_id, anchor_date, launched_at, completed_at) - tasks(id, journey_id, template_task_id, title, owner_id, due_on, state, completed_at, completed_by, confirmation_note) - access_systems(id, name, owner_role, criticality) · what has to be revoked and who owns it - audit(id, entity, entity_id, at, actor_id, action, detail_json) · append only, no update path - A task marked requires_confirmation cannot be completed by the person it was assigned to without a note saying what they verified. ### Phase 1 · Templates with offsets rather than dates Build: Templates of tasks with an offset in days from an anchor date and an owner expressed as a role rather than a person. Launching a journey for an employee resolves offsets into dates and roles into people. Trap: Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. Done when: A template with offsets produces correct due dates for two employees with different start dates. An owner role resolves to a person at launch and the resolution is recorded. Changing who holds a role does not alter already launched journeys. A template with an unresolvable role blocks launch and names it. Do not build yet: No reminders, offboarding or audit. ### Phase 2 · Reminders that reach the right person Build: A daily job emailing each owner their overdue and upcoming tasks in one message rather than one per task. Escalate to the manager after a configured period. Stop when the task is completed, checked at send time. Trap: Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. Done when: An owner with several overdue tasks receives one email listing them. Completing a task before the send removes it from that message, verified by completing one after queuing. Escalation to the manager happens after the configured period rather than immediately. A completed journey generates no reminders. Do not build yet: No offboarding or verification yet. ### Phase 3 · Offboarding, and access that is verified Build: An access system register with an owner per system. An offboarding journey generates a revocation task per system the employee had. Completing one requires a confirmation note from the system owner, not a tick from the person leaving or their manager. Trap: A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. Done when: An offboarding journey generates a revocation task per system the employee had access to. Completing a revocation requires a note and records who confirmed. A revocation cannot be completed by the departing employee. Outstanding revocations appear in a report that stays visible until every one is confirmed. Do not build yet: No reporting or retention yet. ### Phase 4 · The report nobody can ignore Build: A standing report of every unconfirmed revocation across all offboarding journeys, sorted by how long it has been outstanding and by system criticality. Email it weekly to whoever is accountable. Do not let a journey close with revocations outstanding. Trap: Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. Done when: The report lists every unconfirmed revocation with its age and criticality. A journey cannot be marked complete while a revocation is outstanding, verified by attempting it. The report is emailed weekly to the accountable person. A revocation outstanding beyond a threshold is escalated. Do not build yet: No access control or retention yet. ### Phase 5 · Who can see what Build: Roles limiting what each person sees: an owner sees their own tasks, a manager sees their reports' journeys, an administrator sees everything. Enforce it in handlers and test with direct requests. Keep a leaving date visible only to those who need it. Trap: A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. Done when: A task owner cannot see another employee's journey, tested by direct request. A manager sees their reports and not others. A leaving date is not visible in any list a general owner can reach, verified by inspecting the response. Administrators see everything and that access is logged. Do not build yet: Retention and export come last. ### Phase 6 · Retention, export and the record you keep Build: A retention policy per record kind with its reason, applied by a job. A subject access export producing everything held about one person. Backups. Keep the offboarding audit longer than the personal details, because it is the security record. Trap: Deleting an offboarding journey when its employee record expires destroys the evidence that access was revoked, which is the record you most need if something surfaces later. Separate the retention periods: personal details expire, the revocation audit persists in a form that does not identify the person more than necessary. Done when: Retention removes personal details past their period while preserving the revocation audit. A subject access export produces everything held about one person. A restore reproduces journeys and the audit trail. The retention policy is written with a reason per record kind. Do not build yet: Nothing after this. ### Out of scope (and why) - Integration with identity providers to revoke access automatically, which would be genuinely better than a checklist and is a per-system integration project. - Payroll, benefits and the human resources information system this would sit beside. - Employment advice of any kind. This tracks tasks people decided on; it decides nothing. - Mobile applications, which matter for owners who are rarely at a desk. ### README must contain - That an access revocation is complete only when the system owner confirms it with a note - That a journey cannot close while revocations are outstanding - That template owners are roles resolved at launch rather than named people - That reminders batch per person and escalate rather than repeating - The retention policy per record kind, and why the revocation audit outlives personal details ===== ARCHITECTURE.md ===== # Architecture · Workelo ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Node 22 with PostgreSQL | journeys are relational, the audit trail is append only, and both want a real database | | Deployment | Self-hosted and private | this holds employee names, start dates, leaving dates and who has access to what | | Time | Dates rather than instants, with an explicit timezone for reminders | a task due on someone's first day is a date, and treating it as an instant shifts it across a timezone | | Email | A transactional provider with a verified domain | reminders that land in spam are tasks that do not get done | | Audit | Append-only completion records with the confirming person | who confirmed a revocation is the record that matters later | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Templates | Tasks as offsets and roles. | Any storage. Roles rather than named people is the property that keeps templates alive. | | Reminders | Batched delivery and escalation. | Any scheduler, provided state is checked at send time. | | Access register | What must be revoked and who owns it. | This list is the asset. Integrating with an identity provider would improve it and does not replace it. | | Confirmation | Verified completion of revocations. | Not swappable for a tick. It is the whole reason offboarding here is different from a checklist. | | Audit | Who confirmed what and when. | Append only. An audit trail with an update path is not a trail. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DATABASE_URL` · required, secret · Employee records and the audit trail. Personal data, kept private. - `PUBLIC_URL` · required · Internal only. Not publicly indexed. - `REMINDER_TIMEZONE` · required · When the daily reminder job runs. Task due dates are dates rather than instants. - `ESCALATE_AFTER_DAYS` · required · Overdue days before a task escalates to the manager. - `REVOCATION_ESCALATE_DAYS` · required · Shorter for access revocations, because an open account is a live risk. - `RETENTION_MONTHS_PERSONAL` · required · Personal details, per your written policy. - `RETENTION_MONTHS_AUDIT` · required · Revocation audit. Longer, because it is the security record. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Workelo product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Node 22 with PostgreSQL, Self-hosted and private, Dates rather than instants, with an explicit timezone for reminders, A transactional provider with a verified domain, Append-only completion records with the confirming person. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. - Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. - A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. - Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. - A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. - Deleting an offboarding journey when its employee record expires destroys the evidence that access was revoked, which is the record you most need if something surfaces later. Separate the retention periods: personal details expire, the revocation audit persists in a form that does not identify the person more than necessary. ===== MILESTONES.md ===== # Delivery milestones · Workelo Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Templates with offsets rather than dates A journey template describes timing relative to a date, so it works for anyone. ### Steps 1. Model tasks as offsets from an anchor Negative offsets for things due before a start date, which is most of onboarding. Files: `src/templates.js` 2. Express owners as roles Resolved at launch, recorded on the task. Named people on templates rot. Files: `src/roles.js` 3. Block launch on an unresolvable role Rather than assigning to nobody, which is how tasks disappear. Files: `src/journeys.js` ### Done when - [ ] Offsets produce correct dates for two different start dates - [ ] Roles resolve to people at launch and the resolution is recorded - [ ] A role change does not alter launched journeys - [ ] An unresolvable role blocks launch and is named ### Watch out - Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. ## M2 · Reminders that reach the right person An overdue task produces exactly one clear reminder to whoever owns it. ### Steps 1. Batch per person per day One message with everything, rather than one per task. Files: `src/reminders.js` 2. Check state at send time Not at queue time. Reminding someone about a task they just finished is the annoying failure. Files: `src/reminders.js` 3. Escalate rather than repeat To the manager after a period. Repetition alone is ignored. Files: `src/escalation.js` ### Done when - [ ] An owner with several overdue tasks receives one email - [ ] Completing before the send removes the task from it - [ ] Escalation happens after the configured period - [ ] A completed journey generates no reminders ### Watch out - Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. ## M3 · Offboarding, and access that is verified Every system someone had access to is revoked and confirmed by the person who owns it. ### Steps 1. Register systems with owners Name, owner role, criticality. This register is the thing that makes offboarding real. Files: `src/systems.js` 2. Generate revocation tasks per system From what the employee actually had, not from a generic list. Files: `src/offboarding.js` 3. Require confirmation with a note From the system owner. A tick from anyone else does not close it. Files: `src/tasks.js` ### Done when - [ ] A revocation task exists per system the employee had - [ ] Completing requires a note and records the confirmer - [ ] The departing employee cannot complete a revocation - [ ] Outstanding revocations stay visible until confirmed ### Watch out - A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. ## M4 · The report nobody can ignore Outstanding access revocations are impossible to miss. ### Steps 1. Build the standing report Across all journeys, sorted by age and criticality. Not per journey. Files: `src/reports.js` 2. Block completion on outstanding revocations The journey is not finished. Allowing it hides the problem. Files: `src/journeys.js` 3. Email it weekly with escalation To someone accountable, with anything old escalated. Files: `src/reports.js` ### Done when - [ ] The report lists unconfirmed revocations with age and criticality - [ ] A journey cannot complete with revocations outstanding - [ ] The weekly report is delivered - [ ] Old revocations escalate ### Watch out - Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. ## M5 · Who can see what Employee records are visible only to the people who need them. ### Steps 1. Enforce roles in handlers Not in templates. Test with direct requests rather than through the interface. Files: `src/auth.js` 2. Restrict sensitive fields A leaving date is not general information. Filter at the query. Files: `src/queries.js` 3. Log administrative access Seeing everything is a permission worth recording the use of. Files: `src/audit.js` ### Done when - [ ] An owner cannot reach another employee's journey - [ ] A manager sees only their reports - [ ] A leaving date is absent from general responses - [ ] Administrative access is logged ### Watch out - A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. ## M6 · Retention, export and the record you keep (production only) Employee data is kept as long as it should be and no longer. ### Steps 1. Separate retention periods Personal details and the security audit have different reasons and different periods. Files: `docs/retention.md`, `src/retention.js` 2. Build the subject access export Before it is requested. Assembling it by hand under time pressure produces an incomplete answer. Files: `src/sar.js` 3. Back up and restore Including the audit trail, which is append only and must come back intact. Files: `scripts/backup.sh`, `README.md` ### Done when - [ ] Retention removes personal details and preserves the revocation audit - [ ] A subject access export produces everything about one person - [ ] A restore reproduces journeys and an intact audit trail - [ ] The retention policy is written with a reason per kind ### Watch out - Deleting an offboarding journey when its employee record expires destroys the evidence that access was revoked, which is the record you most need if something surfaces later. Separate the retention periods: personal details expire, the revocation audit persists in a form that does not identify the person more than necessary. ===== OPERATIONS.md ===== # Operations · Workelo ## Backup Nightly database backups off the machine, encrypted, since this is personal data. The audit trail must restore intact, because a gap in it is indistinguishable from a revocation that never happened. ## Restore Restore and verify that a completed offboarding journey still shows who confirmed each revocation. That check, rather than the application starting, is what proves the restore. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Outstanding revocation count and age, reminder delivery failures, and journeys stalled past their anchor date. The first is a security metric rather than an operational one. ## Incident checklist If someone who left still has access, the report should have shown it, and if it did not, the system was missing from the register. Adding it is the fix; the register is the thing that decays. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] A revocation is confirmed to require a note and record the confirmer - [ ] A journey is confirmed unable to close with revocations outstanding - [ ] An owner is confirmed unable to reach another employee's journey - [ ] A leaving date is confirmed absent from general responses - [ ] A restore is confirmed to preserve the audit trail intact ## Launch constraint Do not market omitted Workelo capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Employee records and the audit trail. Personal data, kept private. DATABASE_URL=postgres://user:pass@localhost:5432/journeys # Required. Internal only. Not publicly indexed. PUBLIC_URL=https://onboarding.internal.example.com # Required. When the daily reminder job runs. Task due dates are dates rather than instants. REMINDER_TIMEZONE=Europe/London # Required. Overdue days before a task escalates to the manager. ESCALATE_AFTER_DAYS=3 # Required. Shorter for access revocations, because an open account is a live risk. REVOCATION_ESCALATE_DAYS=1 # Required. Personal details, per your written policy. RETENTION_MONTHS_PERSONAL=24 # Required. Revocation audit. Longer, because it is the security record. RETENTION_MONTHS_AUDIT=84
# Workelo · indie build Journey templates of tasks offset from a start or leaving date, launched per employee with owners assigned, reminders for overdue items, and an offboarding view that reports every outstanding access revocation by name and system until each is confirmed by the person responsible for it. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Node 22 with PostgreSQL | journeys are relational, the audit trail is append only, and both want a real database | | Deployment | Self-hosted and private | this holds employee names, start dates, leaving dates and who has access to what | | Time | Dates rather than instants, with an explicit timezone for reminders | a task due on someone's first day is a date, and treating it as an instant shifts it across a timezone | | Email | A transactional provider with a verified domain | reminders that land in spam are tasks that do not get done | | Audit | Append-only completion records with the confirming person | who confirmed a revocation is the record that matters later | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the application and the reminder job. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **PostgreSQL 16 or newer** · free - Why: Journeys are relational and the audit trail is append only. - Get it: Install locally or run it in Docker. - Verify: psql connects and reports 16 or higher - [ ] **A real register of every system people have access to** · free, and it usually takes longer than expected - Why: Offboarding is only as good as this list, and the systems nobody remembers are exactly the ones that stay open. Assembling it is the most valuable part of this build. - Get it: Ask every team what they administer. Include the small ones: a shared inbox, a design tool, a monitoring dashboard, a door code. - Verify: The register exists with an owner per system - [ ] **A transactional email provider with a verified domain** · free tiers cover a small organisation - Why: Reminders and the weekly report both depend on delivery, and internal mail that lands in spam is a task nobody does. - Get it: Create an account, add the DNS records, take the credentials. - Verify: A reminder arrives in an inbox and is not in spam - [ ] **Retention periods for employee records** · free - Why: This holds personal data including leaving dates, and how long you keep it is a decision with legal weight in most places. - Get it: Decide per record kind with a reason. The security audit usually needs a longer period than the personal details, and separating them is the useful part. - Verify: The policy is written with a reason per kind ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Automatic revocation through identity provider integration. - Payroll, benefits and human resources records. - Employment advice of any kind. - Mobile applications. - HRIS and ATS connectors, so employee records get typed in by hand - Automatic account and access provisioning in Google Workspace, Slack, and IT tools, plus the deprovisioning checklist on exit - E-signature and document collection flows that a legal team will accept - The polished employee-facing portal and email design that makes day one feel intentional rather than homemade - Reporting, audit logs, and the GDPR paperwork story your HR lead will ask about If one of those is essential to you, that is the reason to keep paying for Workelo, and the README should say so rather than pretend.
# Build brief · Workelo The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me onboarding and offboarding journeys as templates, with the access checklist actually verified to replace Workelo. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Offboarding is the half that matters and the half everyone builds badly. An onboarding task nobody did means a new person waits for a laptop. An offboarding task nobody did means someone who left still has access to your systems, and nobody notices until it matters. So an access revocation task is not complete because someone ticked a box. It is complete when the person who owns that system confirms it, with a timestamp and a name, and the journey reports outstanding revocations loudly until they are. A checklist without verification is theatre, and this is the one place where that is expensive. This also holds personal data about employees, including their leaving. Retention and access controls are part of the build rather than a later addition. ### Stack (fixed, do not substitute) - Runtime: Node 22 with PostgreSQL · journeys are relational, the audit trail is append only, and both want a real database - Deployment: Self-hosted and private · this holds employee names, start dates, leaving dates and who has access to what - Time: Dates rather than instants, with an explicit timezone for reminders · a task due on someone's first day is a date, and treating it as an instant shifts it across a timezone - Email: A transactional provider with a verified domain · reminders that land in spam are tasks that do not get done - Audit: Append-only completion records with the confirming person · who confirmed a revocation is the record that matters later ### Data model (create this before Phase 1) - employees(id, name, work_email, personal_email, role, manager_id, start_on, leaving_on, state) - templates(id, name, kind, active) · kind is onboarding, crossboarding or offboarding - template_tasks(id, template_id, title, description, offset_days, owner_role, category, requires_confirmation) · offset from the start or leaving date - journeys(id, employee_id, template_id, anchor_date, launched_at, completed_at) - tasks(id, journey_id, template_task_id, title, owner_id, due_on, state, completed_at, completed_by, confirmation_note) - access_systems(id, name, owner_role, criticality) · what has to be revoked and who owns it - audit(id, entity, entity_id, at, actor_id, action, detail_json) · append only, no update path - A task marked requires_confirmation cannot be completed by the person it was assigned to without a note saying what they verified. ### Phase 1 · Templates with offsets rather than dates Build: Templates of tasks with an offset in days from an anchor date and an owner expressed as a role rather than a person. Launching a journey for an employee resolves offsets into dates and roles into people. Trap: Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. Done when: A template with offsets produces correct due dates for two employees with different start dates. An owner role resolves to a person at launch and the resolution is recorded. Changing who holds a role does not alter already launched journeys. A template with an unresolvable role blocks launch and names it. Do not build yet: No reminders, offboarding or audit. ### Phase 2 · Reminders that reach the right person Build: A daily job emailing each owner their overdue and upcoming tasks in one message rather than one per task. Escalate to the manager after a configured period. Stop when the task is completed, checked at send time. Trap: Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. Done when: An owner with several overdue tasks receives one email listing them. Completing a task before the send removes it from that message, verified by completing one after queuing. Escalation to the manager happens after the configured period rather than immediately. A completed journey generates no reminders. Do not build yet: No offboarding or verification yet. ### Phase 3 · Offboarding, and access that is verified Build: An access system register with an owner per system. An offboarding journey generates a revocation task per system the employee had. Completing one requires a confirmation note from the system owner, not a tick from the person leaving or their manager. Trap: A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. Done when: An offboarding journey generates a revocation task per system the employee had access to. Completing a revocation requires a note and records who confirmed. A revocation cannot be completed by the departing employee. Outstanding revocations appear in a report that stays visible until every one is confirmed. Do not build yet: No reporting or retention yet. ### Phase 4 · The report nobody can ignore Build: A standing report of every unconfirmed revocation across all offboarding journeys, sorted by how long it has been outstanding and by system criticality. Email it weekly to whoever is accountable. Do not let a journey close with revocations outstanding. Trap: Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. Done when: The report lists every unconfirmed revocation with its age and criticality. A journey cannot be marked complete while a revocation is outstanding, verified by attempting it. The report is emailed weekly to the accountable person. A revocation outstanding beyond a threshold is escalated. Do not build yet: No access control or retention yet. ### Phase 5 · Who can see what Build: Roles limiting what each person sees: an owner sees their own tasks, a manager sees their reports' journeys, an administrator sees everything. Enforce it in handlers and test with direct requests. Keep a leaving date visible only to those who need it. Trap: A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. Done when: A task owner cannot see another employee's journey, tested by direct request. A manager sees their reports and not others. A leaving date is not visible in any list a general owner can reach, verified by inspecting the response. Administrators see everything and that access is logged. Do not build yet: Retention and export come last. ### Phase 6 · Retention, export and the record you keep Build: A retention policy per record kind with its reason, applied by a job. A subject access export producing everything held about one person. Backups. Keep the offboarding audit longer than the personal details, because it is the security record. Trap: Deleting an offboarding journey when its employee record expires destroys the evidence that access was revoked, which is the record you most need if something surfaces later. Separate the retention periods: personal details expire, the revocation audit persists in a form that does not identify the person more than necessary. Done when: Retention removes personal details past their period while preserving the revocation audit. A subject access export produces everything held about one person. A restore reproduces journeys and the audit trail. The retention policy is written with a reason per record kind. Do not build yet: Nothing after this. ### Out of scope (and why) - Integration with identity providers to revoke access automatically, which would be genuinely better than a checklist and is a per-system integration project. - Payroll, benefits and the human resources information system this would sit beside. - Employment advice of any kind. This tracks tasks people decided on; it decides nothing. - Mobile applications, which matter for owners who are rarely at a desk. ### README must contain - That an access revocation is complete only when the system owner confirms it with a note - That a journey cannot close while revocations are outstanding - That template owners are roles resolved at launch rather than named people - That reminders batch per person and escalate rather than repeating - The retention policy per record kind, and why the revocation audit outlives personal details
# Agent instructions · Workelo indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Node 22 with PostgreSQL, Self-hosted and private, Dates rather than instants, with an explicit timezone for reminders, A transactional provider with a verified domain, Append-only completion records with the confirming person. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. - Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. - A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. - Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. - A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays.
# Build plan · Workelo Journey templates of tasks offset from a start or leaving date, launched per employee with owners assigned, reminders for overdue items, and an offboarding view that reports every outstanding access revocation by name and system until each is confirmed by the person responsible for it. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Templates with offsets rather than dates A journey template describes timing relative to a date, so it works for anyone. ### Steps 1. Model tasks as offsets from an anchor Negative offsets for things due before a start date, which is most of onboarding. Files: `src/templates.js` 2. Express owners as roles Resolved at launch, recorded on the task. Named people on templates rot. Files: `src/roles.js` 3. Block launch on an unresolvable role Rather than assigning to nobody, which is how tasks disappear. Files: `src/journeys.js` ### Done when - [ ] Offsets produce correct dates for two different start dates - [ ] Roles resolve to people at launch and the resolution is recorded - [ ] A role change does not alter launched journeys - [ ] An unresolvable role blocks launch and is named ### Watch out - Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. ## Phase 2 · Reminders that reach the right person An overdue task produces exactly one clear reminder to whoever owns it. ### Steps 1. Batch per person per day One message with everything, rather than one per task. Files: `src/reminders.js` 2. Check state at send time Not at queue time. Reminding someone about a task they just finished is the annoying failure. Files: `src/reminders.js` 3. Escalate rather than repeat To the manager after a period. Repetition alone is ignored. Files: `src/escalation.js` ### Done when - [ ] An owner with several overdue tasks receives one email - [ ] Completing before the send removes the task from it - [ ] Escalation happens after the configured period - [ ] A completed journey generates no reminders ### Watch out - Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. ## Phase 3 · Offboarding, and access that is verified Every system someone had access to is revoked and confirmed by the person who owns it. ### Steps 1. Register systems with owners Name, owner role, criticality. This register is the thing that makes offboarding real. Files: `src/systems.js` 2. Generate revocation tasks per system From what the employee actually had, not from a generic list. Files: `src/offboarding.js` 3. Require confirmation with a note From the system owner. A tick from anyone else does not close it. Files: `src/tasks.js` ### Done when - [ ] A revocation task exists per system the employee had - [ ] Completing requires a note and records the confirmer - [ ] The departing employee cannot complete a revocation - [ ] Outstanding revocations stay visible until confirmed ### Watch out - A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. ## Phase 4 · The report nobody can ignore Outstanding access revocations are impossible to miss. ### Steps 1. Build the standing report Across all journeys, sorted by age and criticality. Not per journey. Files: `src/reports.js` 2. Block completion on outstanding revocations The journey is not finished. Allowing it hides the problem. Files: `src/journeys.js` 3. Email it weekly with escalation To someone accountable, with anything old escalated. Files: `src/reports.js` ### Done when - [ ] The report lists unconfirmed revocations with age and criticality - [ ] A journey cannot complete with revocations outstanding - [ ] The weekly report is delivered - [ ] Old revocations escalate ### Watch out - Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. ## Phase 5 · Who can see what Employee records are visible only to the people who need them. ### Steps 1. Enforce roles in handlers Not in templates. Test with direct requests rather than through the interface. Files: `src/auth.js` 2. Restrict sensitive fields A leaving date is not general information. Filter at the query. Files: `src/queries.js` 3. Log administrative access Seeing everything is a permission worth recording the use of. Files: `src/audit.js` ### Done when - [ ] An owner cannot reach another employee's journey - [ ] A manager sees only their reports - [ ] A leaving date is absent from general responses - [ ] Administrative access is logged ### Watch out - A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. ## Not in this build - Automatic revocation through identity provider integration. - Payroll, benefits and human resources records. - Employment advice of any kind. - Mobile applications. ## After v1, if you want it - Identity provider integration for the systems that support it, keeping the checklist for those that do not - A quarterly access review, which uses the same register and catches what offboarding missed - Equipment tracking, since a returned laptop is the other thing nobody chases
# Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Employee records and the audit trail. Personal data, kept private. DATABASE_URL=postgres://user:pass@localhost:5432/journeys # Required. Internal only. Not publicly indexed. PUBLIC_URL=https://onboarding.internal.example.com # Required. When the daily reminder job runs. Task due dates are dates rather than instants. REMINDER_TIMEZONE=Europe/London # Required. Overdue days before a task escalates to the manager. ESCALATE_AFTER_DAYS=3 # Required. Shorter for access revocations, because an open account is a live risk. REVOCATION_ESCALATE_DAYS=1 # Required. Personal details, per your written policy. RETENTION_MONTHS_PERSONAL=24 # Required. Revocation audit. Longer, because it is the security record. RETENTION_MONTHS_AUDIT=84
# Workelo · product brief ## Problem Strip away the branding and Workelo is a workflow engine: journey templates, tasks with owners and due dates relative to a start date, reminders, and a page the new hire actually sees. That part is genuinely a weekend build, and for a five person company a self-hosted version is fine. What breaks is everything at the edges: HRIS sync, Google Workspace and Slack account provisioning, equipment requests routed to IT, document signing, and the audit trail HR wants when someone leaves. Also worth naming the obvious: this is a tool for coordinating other people, so a personal build only pays off if you are the one running onboarding. If you are, you get most of the value; if you need it to talk to your HRIS, you will spend the weekend on OAuth instead of features. ## Product outcome Journeys that work for any start date, reminders people actually read, and an offboarding process where every access revocation is confirmed by name and stays visible until it is. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Node 20 and a place to run it (a small VPS or a container) - SMTP credentials for reminder emails, or nothing if you accept an in-app inbox only - Someone willing to keep the templates current, which is the actual work ## Explicit non-goals for v1 - Automatic revocation through identity provider integration. - Payroll, benefits and human resources records. - Employment advice of any kind. - Mobile applications. - HRIS and ATS connectors, so employee records get typed in by hand - Automatic account and access provisioning in Google Workspace, Slack, and IT tools, plus the deprovisioning checklist on exit - E-signature and document collection flows that a legal team will accept - The polished employee-facing portal and email design that makes day one feel intentional rather than homemade - Reporting, audit logs, and the GDPR paperwork story your HR lead will ask about ## Success criteria - A revocation is confirmed to require a note and record the confirmer - A journey is confirmed unable to close with revocations outstanding - An owner is confirmed unable to reach another employee's journey - A leaving date is confirmed absent from general responses - A restore is confirmed to preserve the audit trail intact
# Build brief · Workelo The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me onboarding and offboarding journeys as templates, with the access checklist actually verified to replace Workelo. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero Offboarding is the half that matters and the half everyone builds badly. An onboarding task nobody did means a new person waits for a laptop. An offboarding task nobody did means someone who left still has access to your systems, and nobody notices until it matters. So an access revocation task is not complete because someone ticked a box. It is complete when the person who owns that system confirms it, with a timestamp and a name, and the journey reports outstanding revocations loudly until they are. A checklist without verification is theatre, and this is the one place where that is expensive. This also holds personal data about employees, including their leaving. Retention and access controls are part of the build rather than a later addition. ### Stack (fixed, do not substitute) - Runtime: Node 22 with PostgreSQL · journeys are relational, the audit trail is append only, and both want a real database - Deployment: Self-hosted and private · this holds employee names, start dates, leaving dates and who has access to what - Time: Dates rather than instants, with an explicit timezone for reminders · a task due on someone's first day is a date, and treating it as an instant shifts it across a timezone - Email: A transactional provider with a verified domain · reminders that land in spam are tasks that do not get done - Audit: Append-only completion records with the confirming person · who confirmed a revocation is the record that matters later ### Data model (create this before Phase 1) - employees(id, name, work_email, personal_email, role, manager_id, start_on, leaving_on, state) - templates(id, name, kind, active) · kind is onboarding, crossboarding or offboarding - template_tasks(id, template_id, title, description, offset_days, owner_role, category, requires_confirmation) · offset from the start or leaving date - journeys(id, employee_id, template_id, anchor_date, launched_at, completed_at) - tasks(id, journey_id, template_task_id, title, owner_id, due_on, state, completed_at, completed_by, confirmation_note) - access_systems(id, name, owner_role, criticality) · what has to be revoked and who owns it - audit(id, entity, entity_id, at, actor_id, action, detail_json) · append only, no update path - A task marked requires_confirmation cannot be completed by the person it was assigned to without a note saying what they verified. ### Phase 1 · Templates with offsets rather than dates Build: Templates of tasks with an offset in days from an anchor date and an owner expressed as a role rather than a person. Launching a journey for an employee resolves offsets into dates and roles into people. Trap: Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. Done when: A template with offsets produces correct due dates for two employees with different start dates. An owner role resolves to a person at launch and the resolution is recorded. Changing who holds a role does not alter already launched journeys. A template with an unresolvable role blocks launch and names it. Do not build yet: No reminders, offboarding or audit. ### Phase 2 · Reminders that reach the right person Build: A daily job emailing each owner their overdue and upcoming tasks in one message rather than one per task. Escalate to the manager after a configured period. Stop when the task is completed, checked at send time. Trap: Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. Done when: An owner with several overdue tasks receives one email listing them. Completing a task before the send removes it from that message, verified by completing one after queuing. Escalation to the manager happens after the configured period rather than immediately. A completed journey generates no reminders. Do not build yet: No offboarding or verification yet. ### Phase 3 · Offboarding, and access that is verified Build: An access system register with an owner per system. An offboarding journey generates a revocation task per system the employee had. Completing one requires a confirmation note from the system owner, not a tick from the person leaving or their manager. Trap: A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. Done when: An offboarding journey generates a revocation task per system the employee had access to. Completing a revocation requires a note and records who confirmed. A revocation cannot be completed by the departing employee. Outstanding revocations appear in a report that stays visible until every one is confirmed. Do not build yet: No reporting or retention yet. ### Phase 4 · The report nobody can ignore Build: A standing report of every unconfirmed revocation across all offboarding journeys, sorted by how long it has been outstanding and by system criticality. Email it weekly to whoever is accountable. Do not let a journey close with revocations outstanding. Trap: Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. Done when: The report lists every unconfirmed revocation with its age and criticality. A journey cannot be marked complete while a revocation is outstanding, verified by attempting it. The report is emailed weekly to the accountable person. A revocation outstanding beyond a threshold is escalated. Do not build yet: No access control or retention yet. ### Phase 5 · Who can see what Build: Roles limiting what each person sees: an owner sees their own tasks, a manager sees their reports' journeys, an administrator sees everything. Enforce it in handlers and test with direct requests. Keep a leaving date visible only to those who need it. Trap: A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. Done when: A task owner cannot see another employee's journey, tested by direct request. A manager sees their reports and not others. A leaving date is not visible in any list a general owner can reach, verified by inspecting the response. Administrators see everything and that access is logged. Do not build yet: Retention and export come last. ### Phase 6 · Retention, export and the record you keep Build: A retention policy per record kind with its reason, applied by a job. A subject access export producing everything held about one person. Backups. Keep the offboarding audit longer than the personal details, because it is the security record. Trap: Deleting an offboarding journey when its employee record expires destroys the evidence that access was revoked, which is the record you most need if something surfaces later. Separate the retention periods: personal details expire, the revocation audit persists in a form that does not identify the person more than necessary. Done when: Retention removes personal details past their period while preserving the revocation audit. A subject access export produces everything held about one person. A restore reproduces journeys and the audit trail. The retention policy is written with a reason per record kind. Do not build yet: Nothing after this. ### Out of scope (and why) - Integration with identity providers to revoke access automatically, which would be genuinely better than a checklist and is a per-system integration project. - Payroll, benefits and the human resources information system this would sit beside. - Employment advice of any kind. This tracks tasks people decided on; it decides nothing. - Mobile applications, which matter for owners who are rarely at a desk. ### README must contain - That an access revocation is complete only when the system owner confirms it with a note - That a journey cannot close while revocations are outstanding - That template owners are roles resolved at launch rather than named people - That reminders batch per person and escalate rather than repeating - The retention policy per record kind, and why the revocation audit outlives personal details
# Architecture · Workelo ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Node 22 with PostgreSQL | journeys are relational, the audit trail is append only, and both want a real database | | Deployment | Self-hosted and private | this holds employee names, start dates, leaving dates and who has access to what | | Time | Dates rather than instants, with an explicit timezone for reminders | a task due on someone's first day is a date, and treating it as an instant shifts it across a timezone | | Email | A transactional provider with a verified domain | reminders that land in spam are tasks that do not get done | | Audit | Append-only completion records with the confirming person | who confirmed a revocation is the record that matters later | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Templates | Tasks as offsets and roles. | Any storage. Roles rather than named people is the property that keeps templates alive. | | Reminders | Batched delivery and escalation. | Any scheduler, provided state is checked at send time. | | Access register | What must be revoked and who owns it. | This list is the asset. Integrating with an identity provider would improve it and does not replace it. | | Confirmation | Verified completion of revocations. | Not swappable for a tick. It is the whole reason offboarding here is different from a checklist. | | Audit | Who confirmed what and when. | Append only. An audit trail with an update path is not a trail. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DATABASE_URL` · required, secret · Employee records and the audit trail. Personal data, kept private. - `PUBLIC_URL` · required · Internal only. Not publicly indexed. - `REMINDER_TIMEZONE` · required · When the daily reminder job runs. Task due dates are dates rather than instants. - `ESCALATE_AFTER_DAYS` · required · Overdue days before a task escalates to the manager. - `REVOCATION_ESCALATE_DAYS` · required · Shorter for access revocations, because an open account is a live risk. - `RETENTION_MONTHS_PERSONAL` · required · Personal details, per your written policy. - `RETENTION_MONTHS_AUDIT` · required · Revocation audit. Longer, because it is the security record. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Workelo product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Node 22 with PostgreSQL, Self-hosted and private, Dates rather than instants, with an explicit timezone for reminders, A transactional provider with a verified domain, Append-only completion records with the confirming person. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. - Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. - A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. - Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. - A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. - Deleting an offboarding journey when its employee record expires destroys the evidence that access was revoked, which is the record you most need if something surfaces later. Separate the retention periods: personal details expire, the revocation audit persists in a form that does not identify the person more than necessary.
# Delivery milestones · Workelo Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Templates with offsets rather than dates A journey template describes timing relative to a date, so it works for anyone. ### Steps 1. Model tasks as offsets from an anchor Negative offsets for things due before a start date, which is most of onboarding. Files: `src/templates.js` 2. Express owners as roles Resolved at launch, recorded on the task. Named people on templates rot. Files: `src/roles.js` 3. Block launch on an unresolvable role Rather than assigning to nobody, which is how tasks disappear. Files: `src/journeys.js` ### Done when - [ ] Offsets produce correct dates for two different start dates - [ ] Roles resolve to people at launch and the resolution is recorded - [ ] A role change does not alter launched journeys - [ ] An unresolvable role blocks launch and is named ### Watch out - Putting a named person on a template means every template breaks when that person changes role, and nobody notices until a task is assigned to someone who left. Use roles on the template and resolve to a person at launch, recording who it resolved to. ## M2 · Reminders that reach the right person An overdue task produces exactly one clear reminder to whoever owns it. ### Steps 1. Batch per person per day One message with everything, rather than one per task. Files: `src/reminders.js` 2. Check state at send time Not at queue time. Reminding someone about a task they just finished is the annoying failure. Files: `src/reminders.js` 3. Escalate rather than repeat To the manager after a period. Repetition alone is ignored. Files: `src/escalation.js` ### Done when - [ ] An owner with several overdue tasks receives one email - [ ] Completing before the send removes the task from it - [ ] Escalation happens after the configured period - [ ] A completed journey generates no reminders ### Watch out - Sending one email per overdue task means an owner with six overdue items gets six emails and ignores all of them. Batch per person per day, and escalate rather than repeating, because repetition without escalation is just noise. ## M3 · Offboarding, and access that is verified Every system someone had access to is revoked and confirmed by the person who owns it. ### Steps 1. Register systems with owners Name, owner role, criticality. This register is the thing that makes offboarding real. Files: `src/systems.js` 2. Generate revocation tasks per system From what the employee actually had, not from a generic list. Files: `src/offboarding.js` 3. Require confirmation with a note From the system owner. A tick from anyone else does not close it. Files: `src/tasks.js` ### Done when - [ ] A revocation task exists per system the employee had - [ ] Completing requires a note and records the confirmer - [ ] The departing employee cannot complete a revocation - [ ] Outstanding revocations stay visible until confirmed ### Watch out - A revocation task completed by whoever was closest to the checklist is the failure this exists to prevent, because they usually did not check. Require confirmation from the system owner with a note, and record who confirmed, because that name is what someone will want six months later. ## M4 · The report nobody can ignore Outstanding access revocations are impossible to miss. ### Steps 1. Build the standing report Across all journeys, sorted by age and criticality. Not per journey. Files: `src/reports.js` 2. Block completion on outstanding revocations The journey is not finished. Allowing it hides the problem. Files: `src/journeys.js` 3. Email it weekly with escalation To someone accountable, with anything old escalated. Files: `src/reports.js` ### Done when - [ ] The report lists unconfirmed revocations with age and criticality - [ ] A journey cannot complete with revocations outstanding - [ ] The weekly report is delivered - [ ] Old revocations escalate ### Watch out - Letting an offboarding journey be marked complete with revocations outstanding removes the one signal that would have caught it, and the journey then looks finished in every summary. A journey completes when its revocations are confirmed, and not before. ## M5 · Who can see what Employee records are visible only to the people who need them. ### Steps 1. Enforce roles in handlers Not in templates. Test with direct requests rather than through the interface. Files: `src/auth.js` 2. Restrict sensitive fields A leaving date is not general information. Filter at the query. Files: `src/queries.js` 3. Log administrative access Seeing everything is a permission worth recording the use of. Files: `src/audit.js` ### Done when - [ ] An owner cannot reach another employee's journey - [ ] A manager sees only their reports - [ ] A leaving date is absent from general responses - [ ] Administrative access is logged ### Watch out - A leaving date visible to the whole company before it is announced is a real harm to the person leaving, and it leaks through task lists and reports rather than through a profile page. Restrict at the query, and check what a task list actually returns rather than what the interface displays. ## M6 · Retention, export and the record you keep (production only) Employee data is kept as long as it should be and no longer. ### Steps 1. Separate retention periods Personal details and the security audit have different reasons and different periods. Files: `docs/retention.md`, `src/retention.js` 2. Build the subject access export Before it is requested. Assembling it by hand under time pressure produces an incomplete answer. Files: `src/sar.js` 3. Back up and restore Including the audit trail, which is append only and must come back intact. Files: `scripts/backup.sh`, `README.md` ### Done when - [ ] Retention removes personal details and preserves the revocation audit - [ ] A subject access export produces everything about one person - [ ] A restore reproduces journeys and an intact audit trail - [ ] The retention policy is written with a reason per kind ### Watch out - Deleting an offboarding journey when its employee record expires destroys the evidence that access was revoked, which is the record you most need if something surfaces later. Separate the retention periods: personal details expire, the revocation audit persists in a form that does not identify the person more than necessary.
# Operations · Workelo ## Backup Nightly database backups off the machine, encrypted, since this is personal data. The audit trail must restore intact, because a gap in it is indistinguishable from a revocation that never happened. ## Restore Restore and verify that a completed offboarding journey still shows who confirmed each revocation. That check, rather than the application starting, is what proves the restore. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring Outstanding revocation count and age, reminder delivery failures, and journeys stalled past their anchor date. The first is a security metric rather than an operational one. ## Incident checklist If someone who left still has access, the report should have shown it, and if it did not, the system was missing from the register. Adding it is the fix; the register is the thing that decays. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] A revocation is confirmed to require a note and record the confirmer - [ ] A journey is confirmed unable to close with revocations outstanding - [ ] An owner is confirmed unable to reach another employee's journey - [ ] A leaving date is confirmed absent from general responses - [ ] A restore is confirmed to preserve the audit trail intact ## Launch constraint Do not market omitted Workelo capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required · secret. Employee records and the audit trail. Personal data, kept private. DATABASE_URL=postgres://user:pass@localhost:5432/journeys # Required. Internal only. Not publicly indexed. PUBLIC_URL=https://onboarding.internal.example.com # Required. When the daily reminder job runs. Task due dates are dates rather than instants. REMINDER_TIMEZONE=Europe/London # Required. Overdue days before a task escalates to the manager. ESCALATE_AFTER_DAYS=3 # Required. Shorter for access revocations, because an open account is a live risk. REVOCATION_ESCALATE_DAYS=1 # Required. Personal details, per your written policy. RETENTION_MONTHS_PERSONAL=24 # Required. Revocation audit. Longer, because it is the security record. RETENTION_MONTHS_AUDIT=84
$ choose a build depth, inspect the files, then open the complete pack in your agent
HR teams are not buying a task list, they are buying the fact that a start date in the HRIS makes eleven things happen across six systems without anyone remembering to do them. That plumbing is unglamorous, breaks silently, and needs an owner when it does. Add the compliance and audit angle for offboarding, plus the fact that the buyer is usually a two person People team with zero engineering budget, and a self-hosted alternative loses on total cost even when it wins on features.
xHRIS and ATS connectors, so employee records get typed in by hand
xAutomatic account and access provisioning in Google Workspace, Slack, and IT tools, plus the deprovisioning checklist on exit
xE-signature and document collection flows that a legal team will accept
xThe polished employee-facing portal and email design that makes day one feel intentional rather than homemade
xReporting, audit logs, and the GDPR paperwork story your HR lead will ask about
Nothing worth pointing at. That's why the prompt exists.
Vibecode Workelo
Kinda. The core of Workelo is buildable in a weekend with the prompt on this page, but there are real gaps: HRIS and ATS connectors, so employee records get typed in by hand, Automatic account and access provisioning in Google Workspace, Slack, and IT tools, plus the deprovisioning checklist on exit. Read the honest list above before committing.
How much does Workelo cost?
Workelo's pricing is usage-based or varies by plan · No published price anywhere on the site; /en/pricing returns 404, CTAs are "Demander une demo" and free trial.
What do I lose by replacing Workelo?
Honestly: HRIS and ATS connectors, so employee records get typed in by hand; Automatic account and access provisioning in Google Workspace, Slack, and IT tools, plus the deprovisioning checklist on exit; E-signature and document collection flows that a legal team will accept; The polished employee-facing portal and email design that makes day one feel intentional rather than homemade; Reporting, audit logs, and the GDPR paperwork story your HR lead will ask about. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Workelo?
No mature open-source alternative worth pointing at, which is exactly why the one-shot prompt on this page exists.