Testing, release, decisions and handover
12. Testing, docs and release
Section titled “12. Testing, docs and release”Rule. Test what a person can observe, keep the failing-test baseline at zero, never delete a test, keep
docs in step with code, bump the version on every release, and leave merging to main to the maintainer.
Why. A test that is “always red” teaches people to ignore reds. Deleted tests erase memory. Docs that lag the code are worse than none.
How to apply.
- Tests. Test external behaviour from the product requirements. Unit tests for logic and view models, a database-backed integration suite on a disposable database (never a real one), browser tests for routing, auth and public flows, accessibility checks in both themes. Native apps: tests for every view model and networking core, a mock-server test for every API call used, a UI test for each phase’s main flow.
- Every change ships with its tests — no tests, no merge. Which tests a change needs:
- a bug fix: a regression test that fails before the fix and passes after it;
- a new endpoint or action: the allowed case, the refused case for a role without the capability, the other-tenant case (must not leak), the module-off case, the typed error shapes, and the audit entry it writes;
- a new area/module: on and off for the platform and per space, hidden everywhere when off, purge plan;
- money: idempotent retries, webhook signature and replay, currency and minor units, refunds, provider down;
- web and native parity: the same route with a web session and a native device token, same authority;
- a migration: forward on a copy of real-shaped data, and backward where it is reversible;
- UI: component tests for states (loading with the box loader, empty, error, success), keyboard and focus, both themes, reduced motion; an end-to-end test for each main flow;
- invariants: a guard test that fails the build when the rule is broken (file size, module keys, untranslated strings, logo drawn without the shared component, colours not from tokens, stale previews);
- native apps: view-model tests, a mock-server test for every API call used, a UI test per main flow;
- design system: token check (site colours equal the tokens), preview freshness check, links validator. Tests describe behaviour a person can observe, not implementation details.
- Zero reds. Any failure is a new regression. Run changed and affected areas while working and the full suite before a release.
- Never delete a test. If a feature is not in this release, keep the test and skip it with a written reason.
- Docs ship with code. Docs, OpenAPI snapshot, generated client types, translation catalogues and the
agent-instruction file are updated in the same commit as the code. Where
CLAUDE.mdandAGENTS.mdboth exist they must stay identical (a test or diff guards it). - Commits. One logical change per commit, one-line conventional subject (
feat(area): ...,fix: ...). Credit a co-author only on work they actually wrote. - Branches. Branch from
dev; one feature per branch where used; open a pull request intodev. Never push tomain. Only the maintainer (GitHubShaan-Shoukath) can push to or merge intomainanddevin every repository: branch protection restricts both branches to that one account, applies to admins too, and forbids force-pushes and deletion (applied across the org on 2026-11-13). Everyone else works on feature branches and opens pull requests. - Version.
VERSIONis raised (strictly higher thanmain) on every pull request intomain, with a release note (docs/release-<version>.md) and the version strings updated. A release is cut by CI the first time a newVERSIONreachesmain; images are built and published by CI, not by hand. - Maintainer merges to
main.CODEOWNERSrequests the maintainer’s review on every path; branch protection (applied to admins too) restricts who may merge or push tomain. Agents prepare the merge and stop. - Migrations chain off the real latest migration, are reviewed as SQL, and never use a schema-push shortcut for deployable changes.
- CI required checks cover backend, contracts (OpenAPI regeneration), frontend, the rules guards and the version bump; a security audit workflow runs on the Next.js apps.
- Never read, print or commit secrets in tests either: use fake ids.
- Pipelines and releases. The full pipeline per stack (checks, version bump, release builds, signing, Pages) and where each repository stands is in Pipelines, versions and releases.
Across the org. SpaceWorks: required CI checks backend, contracts, frontend, rules,
version-bump; CODEOWNERS. Events, Inventory, Workspace: CI, quality and release workflows,
CODEOWNERS, branches dev and main, release only on a VERSION change. Machines, Bookings: tests,
security-audit and release workflows, CODEOWNERS. Organization: tests, security audit and release workflows;
CODEOWNERS not found. Android, iOS: phase-based delivery on dev, CI-green rule, a pull request into
main merged by the owner; CODEOWNERS not found. The organisation’s shared contributing guide lives in
the .github repository. (Some contributing text still says the old organisation name; update it to
SpaceWorks-Labs.)
12.1 Landing page and feature docs (every project except the native apps)
Section titled “12.1 Landing page and feature docs (every project except the native apps)”Rule. Every Space Works project other than the Android and iOS apps has its own landing page and its own feature docs, built the way SpaceWorks builds them. The landing page carries the project’s links — its GitHub repository, releases and install, docs, licence and changelog. Landing page and docs take their rules, palette, type, components, logo, animation and copy voice from this design system; nothing is designed separately per project.
Why. A visitor meets a project on its landing page or in its docs before they ever sign in. If each project
invents its own look, the family stops reading as one and the rules drift. SpaceWorks already has the pattern
(a hub landing page with a scroll story and its GitHub link, and a docs viewer at /docs with a page per
area). The separate SpaceWorks-Docs site is retired, so documentation lives in each repository and changes in
the same pull request as the code (decided 13 November 2026).
How to apply.
- Landing page. Served by the project itself (on by default, switchable off like SpaceWorks’
HUB_LANDING_ENABLED): hero with the project name and one-line promise, the box-mark splash and logo hover, the scroll-reveal story, a section per feature area, screenshots in light and dark, and the links — GitHub repository, latest release and the install one-liner, docs, licence (AGPL) and changelog — in the header and the footer. Built only from this system’s tokens (tokens/), components and motion templates (route transition, page enter, scroll reveal, theme reveal), in both themes, with reduced motion honoured. - Feature docs. A docs viewer inside the project (SpaceWorks:
/docs) with one page per feature area: what it does, who can use it (roles), how to switch it on or off, screenshots in both themes and the API reference link (rule 9). Sources live in the repository’sdocs/folder, versioned with the code; no separate docs site. - Native apps (Android, iOS) have no landing page or docs of their own: they link to the SpaceWorks hub’s landing page and docs.
- Before shipping a landing page or docs change: check it against the new-feature checklist, in light and dark, at phone and desktop widths.
12.2 Entry points follow the data (every product)
Section titled “12.2 Entry points follow the data (every product)”Rule. Every entry point — the product’s /, an area’s home, a picker — decides what to show from what actually
exists for this visitor, and never strands them:
- Nothing yet → introduce. With no items (a fresh install, an empty area) or when the backend cannot be reached,
show the introduction: the landing page (§12.1) for
/, a helpful empty state with the next action for an area. - Exactly one → go straight to it. Never show a list or picker with a single choice; open that item directly.
- Several → list. Show the list (searchable when long), with sign in and the main action at hand.
- The way out is always one click away. Wherever an entry point lands — including a single item it jumped into — the product’s own links stay reachable: docs, source (GitHub), features, the full list and the introduction (when switched on), in the header, a slim product bar or menu, and the footer, without hiding the item’s own navigation.
- Sign-in never loses the place. If an action needs an account, sign-in or sign-up returns to the same action with the visitor’s choices kept (item, quantity, dates, form input).
- Switchable introduction. A deployment can turn the introduction off (SpaceWorks:
HUB_LANDING_ENABLED); then rules 2 and 3 apply from the first visit.
| Product | Entry point and the items it counts |
|---|---|
| SpaceWorks | / and public makerspaces: none → landing (/about keeps it reachable), one → /m/<slug>, several → space list |
| Events | / and published upcoming events (or organising spaces): one → that event page, several → the list |
| Bookings | / and bookable spaces/rooms: one → its calendar, several → the room list |
| Inventory | / and public stock rooms: one → that catalogue, several → the list |
| Machines | / and public workshops/machine groups: one → its catalogue, several → the list |
| Organization | / and organisations or units open to join: one → its join page, several → the directory |
| Workspace | after sign-in, the user’s workspaces: one → that workspace, several → the switcher |
| Android, iOS | after sign-in, the user’s spaces: one → that space, several → the space picker (no landing in-app) |
Why. A fresh install has nothing to show but the product, so it introduces itself; a running deployment should open on what people came for. A one-item list is a wasted click, a dead end loses people, and a sign-in that forgets where you were breaks the flow.
How to apply. Decide from the public (or the user’s) item list with a loading state first — never flash the introduction before the answer arrives; treat a failed request like “nothing yet”; test all three cases, the link set on every page the entry point can land on, and the sign-in return path (unit + end-to-end).
12.3 Product README structure (every repository)
Section titled “12.3 Product README structure (every repository)”Rule. Every product README follows one structure, modelled on the SpaceWorks hub README, in this order:
- Banner. The rounded brand banner from
brand/readme/, light and dark through<picture>(prefers-color-scheme), with descriptive alt text that says what the product is. - Centred block. The one-line promise, worded exactly as on the landing page (§12.1); badges in the brand colours (version, licence, CI status); quick links: Documentation · Design system · Get started, plus the landing page or live site when one exists.
- Intro paragraph. What it is, who it is for, the core guarantee, the self-hosted and multi-tenant facts, and where it came from.
- A look around. A screenshot gallery of the key screens in light and dark, each with alt text and a one-line caption, refreshed every release.
- Quickstart. The one-line install and the requirements.
- Who it is for.
- What you can do. One subsection per area.
- Integrations.
- Modules / areas. Linking the product’s modules document.
- Install options and Hosting. Local, outside or hybrid per part (§2.3); release images are the frontend and the backend only (§2.5); the pinned infrastructure, Silo included.
- Architecture. The stack and a diagram.
- API. OpenAPI, Swagger UI and ReDoc links (§9).
- Roles and access.
- Design system. A link to the site, and the
design-system/submodule pinned to a release tag. - Documentation. The in-repo
docs/and the in-app docs (§12.1). - Roadmap.
- Contributing. Plus the security policy, the code of conduct, and the decisions and handover files (§13).
- Licence. Plus credits.
Also: no stale version and no dead link (a CI link check covers the README); screenshots in both themes; the wording kept in step with the landing page. The native apps adapt it: store links, phone screenshots and no install one-liner. The design-system repository itself uses the relevant subset (banner, centred block, intro, gallery, quickstart, documentation, contributing, licence).
Why. The README is the front door on GitHub. One order everywhere means a reader who knows one Space Works product can find install, hosting, API and licence in any of them, and it keeps the README, the landing page and the docs saying the same thing.
How to apply. Start a new README from the SpaceWorks hub README and keep the section order; add a section only where the product has something the list lacks, never drop one (write “Not yet” with a link to the roadmap instead). Review the README in every release: version badge, screenshots in both themes, links, and the promise against the landing page.
13. Decisions and handover (project memory)
Section titled “13. Decisions and handover (project memory)”Rule. Every repository keeps two kinds of project memory:
docs/decisions.md— committed. Every decision that shapes the product, its design or its process, with its reason. Numbered, dated, newest first, with an index table, a status (current, or superseded by N) and where it is implemented. A decision is never deleted: a later one supersedes it and both stay.- A
handover/folder — git-ignored, never committed. It lets whoever works next (a person or an agent, on any machine or account) carry on without losing context:handover/CONTINUE-HERE.md— read first: the current state (branch, what is pushed, what is uncommitted, open pull requests), the work in progress, and the next steps in order.handover/TODO.md— the live checklist ([x]done,[~]in progress,[ ]queued).- optional working notes, logs and reports next to them.
Why. Code shows what was built, not why. Reasons that live only in chat or in someone’s head are lost, and the next person repeats old debates or undoes a deliberate choice. A handover that is updated as work happens means a session can stop at any moment and the next one continues from the exact state.
How to apply.
- Ignore the folder: add
/handover/to.gitignore(or.git/info/excludewhen the ignore file must not change). Never put secrets, tokens or personal data in it either. - Update
CONTINUE-HERE.mdandTODO.mdafter every change: each merge, push, lane or task report and decision. Put the newest state at the top; older blocks stay below as history. - Record each decision in
docs/decisions.mdin the same change that implements it, with the reason. If a reason was never written down, write “Reason not recorded” rather than inventing one. - Number decisions per repository; when a decision comes from another repository’s ledger, keep its original
number as a prefix (for example
SW-380) so they can be cross-referenced. - Agent instruction files (
CLAUDE.md/AGENTS.md) point at both files so every new session reads them first.
Across the org. SpaceWorks: decisions ledger and handover kept in a git-ignored handover/ folder (the
committed docs/decisions.md is still to add). SpaceWorks-Design-System: docs/decisions.md committed; handover
folder to add. Every other repository: verify, and add both.
Part of Space Works product rules, published from docs/product-rules.md. Edit it there.