Repository navigation
Development
The git history was filtered before this repository was made public, to remove personal data and third-party material that had been used for private testing. Commits, order and dates are otherwise unchanged.
Questions and ideas are welcome in Discussions, and bugs as issues. Pull requests are currently limited to collaborators (open an issue first); see CONTRIBUTING.md. Third-party licenses are listed in THIRD_PARTY_NOTICES.md.
Everything that reads "now" for schedule or playback must go through viewNow() (in renderer/js/core/state.js) and never Date.now(). It is time-machine-aware, so scrubbing and tuning in produce the right schedule with no separate history to keep. Schedule resolution is a pure function of (channel, timestamp), which is what makes the time machine possible. Treat a bare Date.now() in schedule or playback code as a bug unless it is one of the few documented real-time exceptions.
Every config edit in the renderer is a named command (with an inverse, for undo). Do not add bare saves. Main-process edits go through the config host, which applies commands with revisions so two windows or a paired phone cannot overwrite each other. Do not cache anything derived from config without keying it on the config generation counter.
The choices that shaped the codebase (the full reasoning is in docs/design/):
-
One clock. Everything about "what is on now" reads time through a single
viewNow(), neverDate.now(). Schedule resolution is a pure function of(channel, timestamp), so the time machine (scrub backward or forward, tune in, get the right schedule) needs no playback log: it re-evaluates the same functions at another time. - Arithmetic, not state. A channel never "restarts." What is playing is computed from an anchor time, so the app does no background work and closing it for a week costs nothing.
- Every config edit is a command. Edits are named commands with computed inverses, which gives undo/redo without storing snapshots, and the main process applies edits through one config host with revisions so two windows or a paired phone can't silently overwrite each other.
- Plugins are explicit about power. A plugin declares the IPC channels, binaries, ports and paths it needs, the user approves them, and plugins that ask for real capabilities run in a separate OS process. LAN pages need a per-boot pairing code and get view-only or control scope; administration is never remote.
- Local first. Playback and scheduling work offline. Anything that talks to the network (updates, weather, metadata lookups) is opt-in or clearly scoped. See SECURITY.md.
- Specs before big changes. The larger reworks (usage modes, the programming timeline, the command layer, the library, households, eras) each started as a numbered design document.
-
Tested against the real app. About 450 unit and contract tests run in CI, and a separate suite
drives the real Electron window over the Chrome DevTools Protocol (
tests/ui/).
Read CONTRIBUTING.md and the Code of Conduct. Questions and ideas are welcome in Discussions and bugs as issues. Pull requests are currently limited to collaborators, so open an issue first to propose a change. In short, for a change:
- Keep changes focused and add or update a test when behavior changes.
- Match the surrounding code.
- Never add copyrighted media, logos, trademarked branding or real network schedules. Use made-up names in fixtures and examples.
- Report security issues privately (Security).
- If a change is visible to users, add a dated entry to the in-app What's New (the
CHANGELOGinrenderer/js/setup/settings.js).
The repository has a pull request template, issue forms, Dependabot updates, CodeQL scanning, and a CI workflow.
Maintainers: the release checklist is RELEASING.md.
More: Architecture · Plugin Development · Building from Source
RerunTV is MIT licensed, in beta, and not affiliated with any broadcaster, network or studio. Releases · Issues · Repository