From 1df71f22bc5fddc66638374bc5e18acbebeccef1 Mon Sep 17 00:00:00 2001 From: trsdn Date: Sat, 19 Sep 2026 20:51:46 +0200 Subject: [PATCH] docs: reassess after the quality pass (B13, P08, S03, L04, L06, X01, X03 pass) Each result cites what was built or measured. S02 stays partial and says what is still uncovered. R01, R03, R05, R07 and R08 wait on standard 1.13.0 being tagged. Adds the translation provenance to the README and a concurrency group to stats.yml. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Ez14nyXC16CaNvU6z7vN6W --- .github/conformance.yml | 14 +-- .github/workflows/stats.yml | 5 ++ README.md | 8 +- docs/self-assessment.md | 166 +++++++++++------------------------- 4 files changed, 68 insertions(+), 125 deletions(-) diff --git a/.github/conformance.yml b/.github/conformance.yml index 3bb5513..117858c 100644 --- a/.github/conformance.yml +++ b/.github/conformance.yml @@ -24,7 +24,7 @@ criteria: B10: pass B11: pass B12: pass - B13: partial + B13: pass B14: pass B15: pass B16: pass @@ -37,7 +37,7 @@ criteria: P05: pass P06: pass P07: pass - P08: partial + P08: pass P09: pass P10: pass P11: pass @@ -45,7 +45,7 @@ criteria: # --- Software Repositories -------------------------------------------- S01: pass S02: partial - S03: partial + S03: pass S04: pass S05: pass S06: na @@ -114,15 +114,15 @@ criteria: L01: pass L02: pass L03: pass - L04: partial + L04: pass L05: na - L06: partial + L06: pass L07: pass # --- Accessibility ----------------------------------------------------- - X01: partial + X01: pass X02: pass - X03: partial + X03: pass X04: pass X05: pass diff --git a/.github/workflows/stats.yml b/.github/workflows/stats.yml index 37bc0e3..b3a91e6 100644 --- a/.github/workflows/stats.yml +++ b/.github/workflows/stats.yml @@ -10,6 +10,11 @@ on: permissions: contents: read +# Two runs pushing to the same generated branch race each other. +concurrency: + group: repo-stats + cancel-in-progress: false + jobs: stats: # The one workflow here with write access. It can only reach the generated diff --git a/README.md b/README.md index 2b1325d..f05a308 100644 --- a/README.md +++ b/README.md @@ -364,8 +364,12 @@ translated. Counts use plural forms rather than string interpolation. Two things to know: -- **The German was written by an AI assistant and has not been reviewed by a - native speaker.** Corrections are a catalog edit. +- **The German is machine-translated and has not been reviewed by a native + speaker.** A large language model (Claude, by Anthropic) translated it from the + English source strings while the app was being developed (September 2026), and + read it again in the running Settings window on 2026-09-19. That is the whole of + its provenance. Each catalog entry is keyed by its English source string, so the + source of every translation is the entry itself. Corrections are a catalog edit. - **Check the bundle, not the build log.** `scripts/build-app.sh` and the release broker's `openswitchr` adapter both put the compiled `.lproj` directories into the bundle. The first localized release shipped without them because the hosted diff --git a/docs/self-assessment.md b/docs/self-assessment.md index 21c297d..e393b75 100644 --- a/docs/self-assessment.md +++ b/docs/self-assessment.md @@ -100,71 +100,20 @@ locally by construction: the local machine trusts its own developer certificate. ## Partials -### `B13` — each fact has one home - -`README.md` and `AGENTS.md` still overlap. Both describe the shared-index -architecture, both tell the `⌘-Tab` story, and both state the current-Space -scope. The audiences differ — one is for a user deciding whether to install, the -other for someone changing the code — but the same fact is written twice in -places, and the two can drift. - -Moving the conformance assessment out of `AGENTS.md` removed the worst instance. -The remaining overlap is smaller and has not yet caused a contradiction. - -### `P08` — status badges - -The required five badges are present, in the required order, and each links to -what it reports. Values are not hand-maintained: - -| Badge | Where the value comes from | -| --- | --- | -| License | GitHub's own licence detection for this repository | -| macOS 15+ | Hardcoded, but `scripts/check.sh` fails when it disagrees with `Package.swift` or `scripts/build-app.sh` | -| CI | GitHub's first-party workflow badge for `main` | -| Latest tag | The repository's tags | -| Conformance | Rendered from `.github/conformance.yml` and committed as `.github/badges/conformance.svg`; the Conformance workflow fails when the two drift apart | - -What keeps this at `partial` is the image host. The conformance badge is served -from this repository and the CI badge is first-party, but the licence, platform, -and tag badges are rendered by `img.shields.io`, and the standard asks for -images served from the repository or a first-party source *where practical*, -because a third-party image host observes every reader. Serving those three -from here would mean generating and committing more SVGs on a schedule. That is -now possible with the same generated-branch arrangement `P09` uses, but it has -not been done. - -The consequence is disclosed rather than hidden: the Privacy section of the -README names `img.shields.io` as the one host that viewing the README contacts, -and states that nothing in the app itself contacts it. - ### `S02` — automated test coverage -234 tests across 27 suites, all pure logic: the AX-to-`CGWindowID` linker, MRU -ordering, the window matcher, filters and per-app rules, the selection and -session logic, thumbnail retention and capture limiting, Dock panel lifecycle and -placement, performance budgets, update scheduling, and a localization test that -reads both String Catalogs and the views. They cover the parts where a subtle bug -is invisible, and several encode a bug that actually shipped. - -They cannot cover anything that talks to another process or draws: accessibility -enumeration, ScreenCaptureKit, the event tap, and every SwiftUI view. That gap -is filled by `openswitchr-diag`, which is run by hand and is not in CI, because -it needs the Accessibility permission and real windows. Three release-blocking -bugs have gone through exactly that gap — the app never starting, settings that -could not be changed, and a Dock hover that worked only the first time — while -every test stayed green. The release gap is the same kind: 0.2.0 shipped without -its German localization while every test was green, and only mounting the -published DMG showed it. - -### `S03` — automated static analysis - -`swift build -Xswiftc -warnings-as-errors` runs in CI on both runners, and this -project has repeatedly found real bugs behind warnings, so that is genuine -static analysis rather than a formality. Markdown is now linted in CI against -the same `.markdownlint.jsonc` the standard publishes. - -No Swift formatter or linter is configured. There is no `swift-format` -configuration and no SwiftLint, so formatting is consistent only by habit. +241 tests across 28 suites, all pure logic, plus a CI step that builds the app +bundle unsigned on both runners and fails when it lacks its German localization or +notices. That step exists because 0.2.0 shipped English-only while every test was +green (the runner's SwiftPM copies string catalogs instead of compiling them), so the +release path is now covered as well as the logic. + +What keeps this at `partial` is what tests still cannot reach: accessibility +enumeration, ScreenCaptureKit, the event tap, and every SwiftUI view. That gap is +filled by `openswitchr-diag`, which is run by hand and is not in CI, because it needs +the Accessibility permission and real windows. Three release-blocking bugs have gone +through exactly that gap (the app never starting, settings that could not be changed, +a Dock hover that worked only the first time) while every test stayed green. ### `R01` — package metadata @@ -223,57 +172,6 @@ the release bundle is assembled by the broker's `openswitchr-swiftpm` adapter rather than by `scripts/build-app.sh`. Doing it in only one of the two would make them disagree, so this needs a broker-side change first. -### `X01` — keyboard operability - -The switcher overlay is fully keyboard-driven: hold the modifier, `Tab` and -`⇧-Tab` move the selection, typing filters by app name or window title, `Escape` -cancels, releasing the modifier commits. The selection is visibly indicated. - -Dock hover previews have no keyboard route at all. They are triggered by the -pointer entering a Dock icon, which is inherent to the gesture rather than an -oversight — but it does mean one of the two frontends is pointer-only. The -switcher reaches every window on the current Space without a pointer, so nothing -is unreachable; it is the Dock-adjacent workflow specifically that is not. - -This is now stated in the Accessibility section of the README rather than left -for a user to discover. - -### `X03` — contrast, text sizing, and colour - -Meaning never rests on colour alone. The quit control on a preview tile is red -*and* a distinct glyph placed in the opposite corner from the close control, and -a minimized window is dimmed *and* explicitly marked. - -Behaviour under enlarged platform text sizes is unverified. Tiles size -themselves from the preview-size preference rather than from text metrics, so a -large accessibility text size may clip a long window title. Reduced-motion and -increased-contrast settings are not specifically honoured either; the panels use -system materials and standard SwiftUI controls and inherit whatever those do. -Both limitations are stated in the README under `X05`. - -### `L04` — catalogs kept complete, missing and orphaned keys detected - -The app has two String Catalogs (`Localizable.xcstrings`, `UI.xcstrings`), and -`LocalizationCatalogTests` runs in CI and fails on the *missing* half: an entry -with no German value, a translation that drops a placeholder, an incomplete -plural, a lost product name or modifier symbol, and a plain literal in the views -that never reached a catalog. It was mutation-tested by deleting an entry and -confirming the test named it. - -What keeps this at `partial` is the *orphaned* half. Nothing notices a catalog -entry that no code uses any more, so a removed string leaves a dead translation -behind. Interpolated literals also have generated keys the scan does not -reconstruct, so those are covered by the entries being present rather than by the -code being scanned. - -### `L06` — translations traceable to their source and origin - -Each entry is keyed by its English source string, so the source is traceable by -construction. The *origin* is not recorded per entry: the German was written by an -AI assistant and has not been reviewed by a native speaker, and that is stated in -the README's Language section rather than on each string. A catalog can carry a -comment per entry, but not a reviewer or a review date. - ## Results that are `na`, and why - **`D01`–`D06`** — nothing is deployed. There is no environment, no runtime @@ -355,6 +253,44 @@ These are recorded because they took work, not because they were free. protected branches. The card is served from `raw.githubusercontent.com` for this repository, not from a third-party image service. Caveat: a branch that only a workflow writes is not reviewed the way a pull request is. +- **`B13`** — each fact has one home. A paragraph-similarity scan of `README.md` + against `AGENTS.md` found four overlaps; three were the same fact written twice + (what `scripts/check.sh` checks, why one shared foundation, the name rationale) and + each now lives in one file and is linked from the other. The fourth, the + diagnostics commands, serves two audiences and is not a duplicated fact. The scan is + not exhaustive. +- **`P08`** — status badges. The licence, minimum-macOS and release badges are + generated by `scripts/badges.py` in `stats.yml` and committed to the generated + `repo-stats` branch; the CI badge is GitHub's own and the conformance badge is + committed from the record. Nothing in the README loads from a third-party image + host any more, and the README's Privacy section says so. +- **`S03`** — static analysis. `swift build -Xswiftc -warnings-as-errors` and + `swift format lint --strict` (config in `.swift-format`) run in `scripts/check.sh` + and on both CI runners, and Markdown is linted against the standard's own config. The + formatter is checked, never run, by the gate, and the fix command is in its message. +- **`L04`** — catalogs kept complete. `LocalizationCatalogTests` fails on a missing or + incomplete German value and, since 2026-09-19, on an *orphaned* entry that no code + refers to. The orphan test was mutation-tested: an injected dead key is reported by + name. Interpolated literals are matched with each format specifier standing for the + interpolation, which is generous by design. +- **`L06`** — traceability. Each entry is keyed by its English source string, so the + source is traceable by construction. The origin is stated in the README's Language + section as translation notes: machine-translated by a large language model from the + English strings, not reviewed by a native speaker, with the date. +- **`X01`** — keyboard operability. The switcher is fully keyboard-driven, and the + Dock preview's function, choosing among one application's windows, is reachable + without a pointer through the second hotkey (the switcher scoped to the current + application), which is documented in the README. The hover itself is a pointer + gesture on a pointer-native system surface, the Dock, and gives no window the + keyboard cannot also reach. This is a pass, not a not-applicable: the criterion + applies to this app and is met. +- **`X03`** — contrast, motion and colour. The panels honour Reduce Motion, Reduce + Transparency and Increase Contrast through `AccessibilityAppearance` (pure, tested; + with every setting off the panels draw exactly as before). Meaning never rests on + colour alone. macOS has no system text size that reaches third-party apps: a render + at the largest SwiftUI text size came out identical to the default, so there is no + enlarged-text mode to clip. Verified by unit tests and by rendering the high-contrast + appearance; **not** verified by toggling the real system settings on a live desktop. - **`S13`** — no workflow here references a `secrets.*` context, and none triggers on `pull_request_target`, so there is no path from an untrusted fork's pull request to a secret this repository holds (it holds none). @@ -409,5 +345,3 @@ In order of how much each one moves: clipping or keep the limitation documented. Moves `X03`. 4. **Resolve the version derivation** with the broker so `I06` stops depending on a human typing the same number in two files. -5. **Serve the remaining badges from the repository** using the `repo-stats` - branch pattern, which would close `P08`.