This is a personal fork, maintained for one Pod and one household. You're welcome to open issues and PRs, and useful fixes will get merged, but there's no roadmap, no SLA, and no promise that a feature request goes anywhere. If you want changes for the broader project, take them to throwaway31265/free-sleep (coordinate on their Discord first, per their CONTRIBUTING.md) or to jmew/free-sleep, which this fork is based on.
Welcome, and treated no differently from any other kind. AGENTS.md is kept current as the entry point for coding agents (layout, commands, hardware cautions), so point your tool there first. Whatever wrote the code, you own the PR: you have read it, tested it, and you are the one accountable for review feedback and for any issues it causes. Nothing else in this document changes based on how the code was produced.
Conventional Commits, matching the history already here:
type(scope): subject
- Types:
feat,fix,ui,docs,build,ops,refactor,chore,test. - Scope is optional; existing ones include
biometrics,schedule,server,base,versioning. - Subject is lowercase and imperative, with no trailing period.
- Plain messages with no trailers; the body explains why, not just what.
Semver, MAJOR.MINOR.PATCH, always all three parts. One source of truth:
server/src/serverInfo.json.
- PATCH: bug fix or internal change, no new behavior.
- MINOR: new user-facing feature, backward compatible.
- MAJOR: breaking change to data schemas, the API, or on-pod config that needs manual attention when deploying.
Nightstand's stream started at 3.0.0 at the hard fork from jmew's fork (which
was at 2.1.4, tracking the original project's 2.x line). upstreamBase in the
same file records the original-project release this build was made from;
update it when a build moves to a newer base, not after every review.
Every release is also recorded in releases.json (repo root) with a channel
of stable or beta, a kind of agent or bundle, and tagged in git as
v<version> (e.g. v3.2.0).
Cut a release when a coherent, user-facing bundle of work is ready, not once
per change. Work accumulates on main under a ## [Unreleased] heading in
CHANGELOG.md; when there's enough to justify a version, that heading becomes
the release. A steady trickle of one-commit releases makes the changelog noise
and the version number meaningless, so resist it.
Every release is born on beta. Promotion to stable is part of the ritual,
not an afterthought: at each release, sweep the existing betas and promote any
that have soaked at least seven nights on real hardware with no regressions.
Use scripts/promote_release.sh <version> to flip a single entry's channel in
releases.json (it prints the matching gh release edit command to run). Two
rules keep the channels honest:
- Stable floor. The newest
stablerelease must never lag behind a beta that has already cleared its seven-night soak. Betas are for soaking, not for parking finished work indefinitely. - Trivial or doc-only releases can be born
stabledirectly.
Downgrades and rollbacks never reverse a Prisma migration: the older server just runs against the newer schema. This works because migrations are additive, and that's a standing rule: a new migration must never drop or rename a column/table that an older, still-installable release reads.
- Bump the version in
server/src/serverInfo.json. - Add the new release to the top of
releases.json, copying the shape of the entry below it:kind(agentorbundle),version,date, and channel (betaunless there's a reason to ship straight tostable). A bundle also carries its ownupstreamBase, the release it was built from, and itsfeatureslist. - Add a matching entry at the top of
CHANGELOG.md. - Rebuild both halves (
npm run build:prinserver/andapp/) and commit the output. - Commit everything together, then tag:
git tag -a v<version> -m "...". - Push with tags:
git push origin main --tags. - Create the GitHub Release:
gh release create v<version> --title "v<version>" --notes-file <path>with that version'sCHANGELOG.mdsection as the notes,-R LTimothy/nightstandifgh's default-repo detection picks the wrong remote (this repo has several forks configured as remotes for cherry-picking). Pass--prereleasefor abeta-channel release; when a release is later promoted tostableinreleases.json, also rungh release edit v<version> --prerelease=false(and--latestif it's the newest stable one) to match.
New code comes with tests. The inherited tree is mostly untested and gets
backfilled incrementally as things are touched, but anything added now should
land with coverage for its logic. The server uses node:test with test files
next to the code they cover (src/**/*.test.ts); when the interesting part
of a change is hard to test directly (shell scripts, UI wiring), extract the
logic into a plain module and test that, or at minimum gate the invariants
that would break silently (see src/updaterScripts.test.ts for the pattern).
Lint and typecheck both halves, and run the server test suite.
cd server && npx tsc --noEmit && npm run lint && npm test
cd app && npx tsc -b && npm run lint
The pod runs prebuilt code, so server/dist/ and server/public/ (the app's
build output) are committed. If your change touches source, rebuild
(npm run build:pr in both server/ and app/) and commit the output,
otherwise the deploy ships stale code.