Build Lucidspark

KINDAback to the verdict

0%0 of 17 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 collaborative board for running workshops, where the canvas is the easy half and the difficult half is the facilitation: anonymity that actually holds, timers everyone sees identically, and voting that cannot be gamed by whoever votes last.

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

CanvasSticky notes and groupings as simple objectsSession stateA server-authoritative phase with a synchronised timerAnonymityAuthorship withheld server side, not hidden in the interfaceVotingAllocations hidden until reveal, enforced server sideRuntimeTypeScript with PostgreSQL and a socket layer

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 Runs the server and the socket layer.

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

    Verify node --version prints v22 or higher

  2. installfree locally

    Why Sessions, votes and notes with server-side visibility rules.

    Get it Docker or your package manager. open ↗

    Verify psql connects and creates a table

  3. have readyfree, and it costs an hour of eight people's time

    Why Every claim here is about group behaviour: whether anonymity is believed, whether the timer feels fair, whether voting changes when totals are hidden. None of it is observable alone.

    Get it A real team and a real question to work on, for an hour.

    Verify A full session runs with eight participants including a vote and a reveal

  4. decidefree

    Why Anonymity is a promise to participants, and whether the facilitator can see authorship changes what people write. Deciding it silently means deciding it against them.

    Get it Decide whether the facilitator can see authorship, state it in the session, and write down why.

    Verify A written rule exists and is shown to participants when a session is anonymous

  5. have readyfree

    Why Timer synchronisation is only testable against skew, and machine clocks in a room full of laptops are genuinely inconsistent.

    Get it Set a test machine's clock several minutes off and join a session with it.

    Verify The skewed client shows the correct remaining time

Data model

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

- boards(id, title, created_by, created_at, archived_at)
- sessions(id, board_id, phase, phase_started_at, phase_duration_seconds, anonymous, facilitator_id)
- notes(id, board_id, author_id, body, x, y, colour, group_id, created_at) · author_id withheld when anonymous
- groups(id, board_id, label, created_by)
- votes(session_id, voter_id, note_id, weight, cast_at) · never returned to participants before reveal
- reveals(session_id, at, revealed_by) · the moment results became visible, recorded
- participants(session_id, user_id, display_name, joined_at, role)
- A vote is stored with its voter for facilitator audit, but no query path returns another participant's votes before the reveal.

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://localhost:5432/workshopsBoards, notes and votes, some written under a promise of anonymity.
SOCKET_PORTrequired3002The live session channel.
ANONYMOUS_DEFAULTrequiredfalseWhether new sessions are anonymous. Stated to participants either way.
FACILITATOR_SEES_AUTHORSrequiredfalseA promise to participants. It is shown to them and it changes what they write.
VOTES_PER_PARTICIPANTrequired5The allocation, enforced server side.
REVEAL_TOTALS_BEFORE_ENDrequiredneverPresent as a constant. Visible running totals change the result.

The build, in order

  1. Notes on a board, together

    Several people add and move notes simultaneously without conflict.

    1. Whole-board broadcast collapses at workshop scale, during the workshop.

      Files src/sync.ts

    2. Two people editing one note is normal in a workshop.

      Files src/reconcile.tssrc/reconcile.test.ts

    3. People join late and the pause is visible to everyone.

      Files src/join.ts

    done when · tick each as it passes
    watch out
    • Broadcasting the whole board on every change works with twenty notes and collapses at four hundred, which is a normal workshop output, and it collapses during the session rather than afterwards. Send per-note changes and reconcile at the note level.
  2. Anonymity that holds at the API

    An anonymous note's author cannot be discovered by anyone but the facilitator.

    1. In the serialiser and the socket path both.

      Files src/anonymity.tstests/anonymity-leak.test.ts

    2. Stored, and reachable only through a path with its own rules.

      Files src/anonymity.ts

    3. Notes written under anonymity were written under a promise.

      Files src/sessions.ts

    done when · tick each as it passes
    watch out
    • Hiding the author in the interface while the socket message still carries it means anyone who opens the developer tools can see who wrote the critical note, and in a workshop about a team's problems that is precisely the harm anonymity was for. Strip it server side and assert it in the suite.
  3. One clock everyone sees

    The timer shows the same remaining time on every screen.

    1. Start and duration from the server, derived locally.

      Files src/phases.ts

    2. Machine clocks are wrong and a workshop timer is visible to everyone.

      Files src/timer.tssrc/timer.test.ts

    3. Not on whichever client reaches zero first.

      Files src/phases.ts

    done when · tick each as it passes
    watch out
    • A timer counted down independently on each client drifts, so participants see different remaining times and someone is cut off mid-sentence believing they had thirty seconds. Send the phase start and duration, derive locally, and correct against the server rather than counting down in isolation.
  4. Voting nobody can watch

    Votes are invisible until the reveal, including in the traffic.

    1. Enforced in the query. Running totals change the result.

      Files src/votes.tstests/vote-secrecy.test.ts

    2. So you know what you have left without seeing anyone else.

      Files src/votes.ts

    3. The moment results became visible is part of the session record.

      Files src/reveal.ts

    done when · tick each as it passes
    watch out
    • Showing a running vote total during voting changes the result, because people converge on whatever is already winning, and that is a well known effect rather than a hypothetical one. Return no other participant's votes and no aggregate before the reveal, enforced in the query rather than the display.
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 whiteboards.