Build MeisterTask

KINDAreplaces $9/mosaves $108/yrback to the verdict

0%0 of 18 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A team board whose hard problems are ordering cards two people can drag at once, enforcing roles in one place rather than in every route, and generating recurring work from rules that do not invent dates the calendar does not have.

estimated effort multi-daythe files for this build are in the project pack

RuntimeNext.js 15 and TypeScript with Docker Compose for the whole stackStoragePostgreSQL with Drizzle migrations, ordering held as sortable ranksAccessOne authorisation function every query passes throughRecurrenceA subset of the calendar recurrence rule, instances generated on completionHistoryAn append-only activity log that outlives deleted entities

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why The application, the database and the job runner come up together, which is the difference between a tracker a team uses and one nobody installs.

    Get it Docker Desktop or your distribution's packages. open ↗

    Verify docker compose up brings the stack to a healthy state

  2. installfree

    Why Runs the application and the migration tooling outside the container during development.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  3. installfree locally, a few pounds a month managed

    Why The ordering, the append-only log and the authorisation scopes all rely on constraints and permissions the database enforces.

    Get it Included in the compose file, or a managed instance for a shared deployment. open ↗

    Verify Migrations apply and the activity table refuses an update

  4. API keyfree tiers are common

    Why Invitations and mention notifications are the only outbound path, and a tracker nobody can be invited to is a personal list.

    Get it Any transactional email provider, or a mailbox with application-specific credentials.

    Verify An invitation arrives at a real address

  5. have readya few pounds a month

    Why A project board on one person's laptop is a private list, and the permission model only means anything with more than one account on it.

    Get it A small virtual server with a domain and HTTPS.

    Verify Two accounts in different roles can use it simultaneously

  6. decidefree

    Why The authorisation tests are generated from this list, and deciding it while writing the routes is how viewers end up able to edit.

    Get it Write a table of role against operation before Phase 2, including the awkward ones like closing someone else's issue.

    Verify Every row in the table has a corresponding test

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- orgs(id, name), memberships(org_id, user_id, role) · role is owner, member or viewer
- projects(id, org_id, name, archived_at), columns(id, project_id, name, rank)
- issues(id, project_id, column_id, title, body, assignee_id, rank, due_at, recurrence_rule, recurrence_parent_id, created_at, closed_at)
- labels, milestones and watchers as join tables, all scoped to a project
- comments(id, issue_id, author_id, body, created_at, edited_at, deleted_at) · soft deleted, because a thread with holes is unreadable
- activity(id, org_id, project_id, actor_id, entity_type, entity_id, verb, before_json, after_json, at) · append only, never updated or deleted
- The rank columns are sortable strings rather than integers, so inserting between two cards touches one row and two simultaneous drags cannot collide on a value.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DATABASE_URLsecretrequiredpostgres://user:pass@db:5432/boardsIssues, activity and memberships.
APP_BASE_URLrequiredhttps://boards.example.comUsed in invitations and mention notifications.
SESSION_SECRETsecretrequireda long random string, redactedSigns sessions. Rotating it logs everyone out, which is the point.
SMTP_URLsecretrequiredsmtps://user:pass@smtp.example.com:465Invitations and mentions only. No marketing path exists.
BACKUP_DIRrequired/var/backups/boardsNightly dumps, with a restore exercised in the suite.
RANK_REBALANCE_CRONrequired0 4 * * *When rank rebalancing runs. It must never change visible order.
ADMIN_RECOVERY_TOKENsecretrequireda long random string, redactedGrants owner access to a locked-out organisation. Store it away from the server.

The build, in order

  1. One issue model and an order two people can change at once

    Dragging a card writes one row, and concurrent drags do not fight.

    1. The drag should cost one update, not a column rewrite.

      Files src/lib/rank.tstests/rank.test.ts

    2. This is the bug people report as the board being flaky.

      Files tests/concurrent-move.test.ts

    3. A maintenance job that changes the order is worse than no job.

      Files src/jobs/rebalance.ts

    done when · tick each as it passes
    watch out
    • Storing position as an integer means every drag rewrites every card below it, so two people reordering the same column at the same time produce interleaved writes, cards that snap back after a refresh, and a board nobody trusts. Compute a rank between the neighbours, write one row, and rebalance on a schedule rather than on every move.
  2. Roles decided in one place

    Every read and write passes the same authorisation function, and each mutation has a test.

    1. Routes should not be the place this is decided.

      Files src/lib/authz.ts

    2. The check that survives the next hurried feature.

      Files tests/authz-coverage.test.ts

    3. Otherwise the error message is an existence oracle.

      Files src/db/scope.ts

    done when · tick each as it passes
    watch out
    • Checking permissions in the route handler means the one endpoint somebody adds in a hurry becomes the way a viewer edits the backlog, and nothing in the test suite notices because the test suite tests the routes that were written carefully. Put the check in the data access layer and write an enumeration test that fails when a mutation exists without a matching authorisation test.
  3. Recurring work that respects the calendar

    A repeating task generates the next instance correctly, including on dates that do not exist.

    1. The specification already decided this; do not improvise a friendlier answer.

      Files src/lib/recurrence.tstests/recurrence.test.ts

    2. A board full of future cards is a board people stop reading.

      Files src/jobs/next-instance.ts

    3. Because most people asking for the thirty-first mean the last day.

      Files src/ui/RecurrenceEditor.tsx

    done when · tick each as it passes
    watch out
    • Materialising a year of future instances for every repeating task fills the board with cards nobody wants to see, and editing the rule afterwards leaves you deciding which of three hundred existing rows to rewrite. Generate one instance at a time on completion, keep the rule on the parent, and store the link from child to parent.
  4. Conversation, and a log that outlives the entities

    Comments, mentions and assignments all land in an append-only history.

    1. History that depends on live rows is not history.

      Files src/lib/activity.ts

    2. A revoked update permission beats a code comment saying do not update.

      Files migrations/00x_activity.sql

    3. Editing a comment should not page the team again.

      Files src/lib/mentions.tstests/mentions.test.ts

    done when · tick each as it passes
    watch out
    • Rendering history by joining the activity log to the entities it mentions produces blank lines the moment something is deleted, and an issue nobody can find is exactly the one the history was needed for. Denormalise the display values into the log row at write time and accept the duplication.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more project management.