Skip to content

v0.2.0

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 15 Sep 23:57
· 114 commits to main since this release
14ecc0e

A stranger can install this now, and can see what it does without reading Thai.
Phase 1 of the plan in the backlog, plus the PDPA minimum a
pilot needs.

Added

  • First-run setup for a clean install (CW-022). npm run db:init creates an
    organisation, the eight system roles and one administrator — and nothing else.
    Until now the only way to get an account was db:seed, which the README
    itself labels demo data, so installing Cwork for real people meant loading a
    fictional company with published credentials and then cleaning up after it.

    • npm run db:init -- --web prints a single-use token instead and setup
      finishes in a browser at /setup. Minting a token requires shell access to
      the server, which is what keeps a deployment that is up but not yet set up
      from being claimed by whoever reaches it first. There is deliberately no
      endpoint that issues one, and no "if no organisation exists, let anyone
      through" check anywhere.
    • Both paths refuse once an organisation exists, including a token that was
      valid moments earlier, and two concurrent attempts are serialised by a
      Postgres advisory lock.
    • The first administrator holds role:manage, so it enrols a second factor at
      its first sign-in. Nothing in setup prints a TOTP secret.
  • GET /config, a public endpoint reporting the feature flags a client
    needs before it can draw its shell. One flag today, assistantEnabled.

  • npm run db:demo, a second half to the demo seed that gives the fictional
    company a history: last month's payroll run, leave that was requested and
    approved, expense claims, a hiring pipeline mid-flight and an open review
    cycle. db:seed chains the two. It drives the application's own services
    rather than inserting rows, so the payslips come from the real Thai tax code
    and the run had to be prepared and approved by two different people.

  • A recorded walkthrough in both READMEs, and the script that produces it
    (docs/demo/record.mjs). It drives the real console against the seeded
    company, second factor included, so the demo cannot drift from the product.
    English captions are burned in, because the interface is Thai until CW-016
    lands and thirty seconds of a language you do not read tells you nothing.

  • The PDPA minimum a pilot needs (CW-026). Personal data
    lists every class of personal data the schema holds and where, the retention
    the software actually enforces and the far longer list it does not, and a
    step-by-step erasure procedure that was run against a real database before it
    was written down. A draft employee notice in
    Thai goes with it.

    • Two facts that procedure had to establish rather than assume: an employee
      who has ever clocked in cannot be deleted — the append-only trigger on
      attendance_punches blocks the cascade — and the audit trail cannot be
      erased at all without destroying the property it exists for. Both are now
      written down, with what to do instead.

Changed

  • The assistant is hidden when it is switched off (CW-027). ASSISTANT_ENABLED=false
    is the default and every role carries assistant:use, so the standard install
    put an assistant entry in front of everybody that could only fail — which
    reads as a broken product rather than a disabled option. The console now drops
    the sidebar entry and the app drops the bottom-navigation tab. A bookmark from
    before it was switched off still resolves, to the screen that explains why it
    is off. The boot-time rule is unchanged.
  • db:seed now says it is demo data when it runs, and refuses to install itself
    beside an organisation it did not create (SEED_FORCE=1 overrides).
  • The demo seed no longer leaves most of the console empty. Payroll,
    expenses, recruitment and performance all showed "nothing here yet" after
    the documented five-minute setup, which did not match the screenshots in the
    README and told an evaluator nothing about whether any of it works.

Fixed

  • An account could not use an access token issued in the same wall-clock second
    as the account row was created: User.sessionsValidFrom is stored with
    millisecond precision and is compared against a JWT iat in whole seconds, so
    the token read as older than the account and every request came back "session
    has been invalidated". Unreachable before — no account could be created and
    signed into that quickly — and reachable the moment setup existed.