From 4764b2557233811cf9ef5d4603186a950d99bf66 Mon Sep 17 00:00:00 2001 From: will wade Date: Wed, 9 Sep 2026 22:23:27 +0100 Subject: [PATCH 1/2] docs(rfcs): index 0018; correct 0017's GTK implementation status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README index: add the missing 0018 row (merged in #36 without an index entry — the file exists on main but was unreachable from the index) - 0017: Dasher-GTK ships the self-managed check (UpdateChecker + Privacy opt-out); the 'not implemented anywhere' table was stale on arrival. History entry added per the keep-RFCs-current convention. Signed-off-by: will wade --- rfcs/0017-update-checking.md | 9 +++++++-- rfcs/README.md | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/rfcs/0017-update-checking.md b/rfcs/0017-update-checking.md index 7beb425..49ae3ad 100644 --- a/rfcs/0017-update-checking.md +++ b/rfcs/0017-update-checking.md @@ -33,7 +33,8 @@ notification with a link. No silent downloads, no self-installation. ## Implementation status -Not implemented anywhere. Dasher-Windows has a `UpdateChecker` service +Dasher-GTK ships the check for self-managed builds (`UpdateChecker`, plus the +Settings → Privacy opt-out); Dasher-Windows has a `UpdateChecker` service (GitHub Releases API + in-app dialog) that is the natural starting point for the shared behaviour; it is compiled out in `STORE` builds, which matches the dual-track split. @@ -43,7 +44,7 @@ dual-track split. | Dasher-Apple (iOS) | TestFlight | None (TestFlight notifies) | | Dasher-Apple (macOS) | GitHub Releases | None | | Dasher-Windows | GitHub Releases; STORE variant planned | `UpdateChecker` (non-STORE builds only) | -| Dasher-GTK | GitHub Releases (Flatpak + AppImage artifacts) | None | +| Dasher-GTK | GitHub Releases (Flatpak + AppImage artifacts) | Implemented — `UpdateChecker` (self-managed builds; Flatpak skips) + "Check for updates" toggle in Preferences → Privacy | | Dasher-Android | GitHub Releases APKs; Play Store intended | None | | dasher-web | Always "latest" by nature of the browser | N/A | @@ -206,3 +207,7 @@ Per [RFC 0011](./0011-testing.md): manual verification primarily. - Decision: Not yet accepted. Dasher-Windows' UpdateChecker is the seed implementation; no other frontend has shipped the behaviour. - Open sub-questions: all (see Unresolved questions). + +## History + +- _2026-09-09_ — _Implementation status corrected: Dasher-GTK ships the check for self-managed builds (UpdateChecker + Privacy opt-out), which the initial table missed._ diff --git a/rfcs/README.md b/rfcs/README.md index f346e16..3362d4b 100644 --- a/rfcs/README.md +++ b/rfcs/README.md @@ -79,3 +79,4 @@ means "landed on some platforms, not all." | [0015](./0015-direct-entry-mode.md) | Direct-entry mode (typing into other applications) | proposed | apple, windows, gtk, android, core | | [0016](./0016-version-in-settings.md) | Report the app version in Settings (Privacy tab for now) | active | apple, windows, gtk, android, web | | [0017](./0017-update-checking.md) | Update distribution and in-app update checking (dual-track) | proposed | apple, windows, gtk, android, web | +| [0018](./0018-startup-loading-states.md) | Startup loading states — no frozen or black windows | proposed | apple, windows, gtk, android, web, core | From 9cfd116040bc72199026e9835085d2d31dbc1605 Mon Sep 17 00:00:00 2001 From: will wade Date: Wed, 9 Sep 2026 22:26:30 +0100 Subject: [PATCH 2/2] ci: RFC index consistency guard scripts/check_rfc_index.py + a minimal workflow: every NNNN-*.md in rfcs/ must have a README index row (0000-template exempt), every row must resolve to a real file, numbers must not repeat, rows ascend. Stdlib only; runnable locally as python3 scripts/check_rfc_index.py Verified both directions: green on this branch (with the 0018 row this PR adds), red when the row is removed. Ends the missing-index-row class (0018 merged unreachable; 0019's open PR repeated the gap). Signed-off-by: will wade --- .github/workflows/rfc-index.yml | 25 +++++++++++ scripts/check_rfc_index.py | 78 +++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 .github/workflows/rfc-index.yml create mode 100755 scripts/check_rfc_index.py diff --git a/.github/workflows/rfc-index.yml b/.github/workflows/rfc-index.yml new file mode 100644 index 0000000..30aa8bf --- /dev/null +++ b/.github/workflows/rfc-index.yml @@ -0,0 +1,25 @@ +name: RFC index + +# Guard (scripts/check_rfc_index.py): every NNNN-*.md in rfcs/ must have a +# README index row, every row must resolve, rows ascend. Keeps merged RFCs +# reachable from the index — the gap this caught post-hoc on 0018. + +on: + pull_request: + paths: + - 'rfcs/**' + - 'scripts/check_rfc_index.py' + - '.github/workflows/rfc-index.yml' + push: + branches: [main] + paths: + - 'rfcs/**' + - 'scripts/check_rfc_index.py' + +jobs: + index: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check RFC index consistency + run: python3 scripts/check_rfc_index.py diff --git a/scripts/check_rfc_index.py b/scripts/check_rfc_index.py new file mode 100755 index 0000000..e71dd8a --- /dev/null +++ b/scripts/check_rfc_index.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +"""Guard: every RFC file has a README index row, and every index row resolves. + +Catches the class of bug where an RFC merges without its `rfcs/README.md` +index entry (0018 shipped unreachable from the index; 0019's PR repeated it) +and the reverse — an index row left pointing at a renamed or deleted file. + +Run locally from anywhere: + python3 scripts/check_rfc_index.py +Exits 1 with a readable list on any mismatch. Stdlib only. +""" + +from pathlib import Path +import re +import sys + +ROOT = Path(__file__).resolve().parent.parent +RFCS = ROOT / "rfcs" +README = RFCS / "README.md" + +# The template is not an RFC; its pseudo-row in the index is informational +# and not a link, so it is exempt from the link-format contract. +EXCLUDE = {"0000-template.md"} + + +def main() -> int: + pattern = "[0-9][0-9][0-9][0-9]-*.md" + files = {p.name for p in RFCS.glob(pattern)} - EXCLUDE + + if not README.is_file(): + print(f"RFC index check failed: {README} not found") + return 1 + text = README.read_text(encoding="utf-8") + + # Index rows look like: + # | [0018](./0018-startup-loading-states.md) | Title | status | platforms | + row_re = re.compile(r"^\|\s*\[(\d{4})\]\(\./([^)]+)\)", re.M) + + problems: list[str] = [] + indexed: dict[str, str] = {} # number -> linked filename + order: list[str] = [] + for num, fname in row_re.findall(text): + if num in indexed: + problems.append(f"duplicate index row for {num}") + indexed[num] = fname + order.append(num) + + # Direction 1: file without a row (or the row points at a different file). + for f in sorted(files): + num = f.split("-", 1)[0] + if num not in indexed: + problems.append(f"rfcs/{f} exists but has no README index row") + elif indexed[num] != f: + problems.append( + f"index row {num} points at {indexed[num]}, but the file is {f}" + ) + + # Direction 2: row without a file (renamed/deleted RFC). + for num, fname in indexed.items(): + if not (RFCS / fname).is_file(): + problems.append(f"index row {num} links ./{fname}, which does not exist") + + # Ordering: rows should ascend; a shuffled index hides entries from scan-readers. + if order != sorted(order): + problems.append("index rows are not in ascending numeric order: " + ", ".join(order)) + + if problems: + print("RFC index check failed:") + for p in problems: + print(f" - {p}") + return 1 + + print(f"RFC index OK ({len(files)} RFC files, {len(indexed)} index rows)") + return 0 + + +if __name__ == "__main__": + sys.exit(main())