Secretary

Timeline
Login

Timeline

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

50 most recent check-ins

2026-10-13
07:37
Android notes links tasks contacts: folder header distinct, entries indented with guide line leaf check-in: 6a41034a97 user: beowulf
07:37
Android news 14: article list in reading order, unread before read, swipe follows it check-in: 19273b86ae user: beowulf
2026-10-12
18:36
secretary-server-news: timezone abbreviations CET/CEST -> numeric offsets; 0.37.0 check-in: 3424ffe70d user: beowulf
17:38
Android contacts 76: fix blank screen after save, single category field with spaces check-in: 6c63f1b3b0 user: beowulf
16:43
Android news 13: swipe between articles in the detail view, fix blank screen when saving a feed check-in: b035e51cc2 user: beowulf
16:28
Android news 12: article detail falls back to feed URL when the feed title is empty check-in: 99b3f68703 user: beowulf
16:12
Android links 42: untagged links are shown unfiled, no Ohne Ordner group header check-in: 633fd0338b user: beowulf
14:35
Android calendar journal launcher secrets: crash handler must not block termination check-in: 8c33a10432 user: beowulf
14:24
Android notes tasks contacts: crash handler must not block termination, guard contacts merge without a server book check-in: 8bf65d8b66 user: beowulf
2026-10-11
16:08
news: sort articles unread/read by date, robust feed title lookup check-in: 907ec9cabc user: beowulf
15:18
news: zoomable fit-width article image, drop read toggle button check-in: a860ee5b97 user: beowulf
14:36
news: read articles from the feed instead of a webview check-in: 9a1dc77cf9 user: beowulf
2026-10-10
19:25
cards: make tap work without long-press check-in: 0311c21132 user: beowulf
19:14
news: read articles in a webview check-in: f131129e4a user: beowulf
08:06
news: move OPML import to settings, add export check-in: dc19b45234 user: beowulf
07:55
news: import feeds from OPML check-in: cd0dc6f584 user: beowulf
07:19
news: feeds as groups, keep articles until read and gone check-in: b7b0bec7dd user: beowulf
2026-10-09
10:47
server version bump 0.34.0 to 0.35.0 check-in: 6ddbdcc4bd user: beowulf
09:43
links/news checker insecure_tls option to trust self-signed certs, version 0.34.0 check-in: 9cc1a3142f user: beowulf
08:12
links checker baseline, changed-flag reset and retries; shared collapsed-folder setting; version bumps check-in: c36da87b7c user: beowulf
05:58
folders: shared folder helpers and row, folder grouping for notes/tasks/contacts with move-to-folder, contacts pills removed, links use shared row, version bumps check-in: f12d157cb4 user: beowulf
2026-10-08
20:47
ai: collapsible folder rows in links/secrets, contacts list back from carousel (android)

links: the FolderHeader text line becomes an entry-styled FolderRow (folder icon, name, item count, rotating chevron) inside the same Card look; a collapsed-folders set toggled per row hides that folder's items, everything expanded by default.

secrets: group drill-down (path navigation via Browse.Group) replaced by an in-place expandable tree on the root screen. Group rows show folder icon, name, direct count and a chevron; tapping expands/collapses recursively, entries are indented by depth and open the detail view. The group menu gains 'add entry here'/'add group here' alongside rename/delete; add-buttons in the header target the vault root. New strings browse_new_entry_here, browse_new_group_here, browse_group_expand, browse_group_collapse (values + values-de).

contacts: the VerticalPager carousel (ContactCarousel, CarouselContactCard, ContactInfoField and pager helpers) is removed again; the compact ContactRow + LazyColumn from 8d736ff5c6 is restored, keeping the showMove gating on collectionsUsage. Carousel-only imports dropped.

versionCode: contacts 71->72, links 39->40, secrets 73->74. All three modules compile, unit tests green, no new lint errors. check-in: 26149ca434 user: beowulf

19:36
server: PUT into a missing collection returns 409 (RFC 4918, 9.7.1) - put_resource_locked created the missing parent directories itself (create_dir_all), so a client with a stale collection selection resurrected a deleted or migrated collection with a single PUT (contacts-rzte rebuilt three times on tremere after the merge into contacts-main); new test test_put_into_missing_collection_returns_conflict, test_sync_start_and_status and test_sync_status_survives_subsequent_starts create their collection before the first PUT; 307 unit/Server + 61 integration + 12 rp_site green, cargo fmt and cargo clippy -D warnings clean; 0.31.0 -> 0.32.0 check-in: daa4ae6a47 user: beowulf
16:53
links 39: collections-usage gating completed - showMove in calendar, contacts, journal, notes, tasks keyed to the shared setting, tasks empty-state Sammlungen action gated; all nine apps bumped, SRS common 1.5.0 in the wiki check-in: aeb7afdccc user: beowulf
16:33
links 38: tags as folders, single main collection behind the shared collections-usage setting, per-check and aggregated change notifications; SRS links 1.0.0 and SRS common 1.4.0 in the wiki, all nine apps bumped, CONTEXT stubs read-only with the wiki export and import workflow check-in: 4232a66e6c user: beowulf
12:26
docs: CONTEXT and SRS migrated to the fossil wiki (Server, Docs, Apps/*/context, SRS/*); local CONTEXT.md are pointer stubs, SRS.md files deleted check-in: 0d695b2f5a user: beowulf
10:36
links 37: Kartenzeile strikt dreizeilig (Titel, URL, Zuletzt geprueft)

Die RRULE-Zeile zwischen URL und Pruefdatum in der Listenkarte ist gefallen - die Zeile zeigt jetzt genau drei Textzeilen hinter den Warnsymbolen und dem Favicon. Die Wiederholung bleibt im Editor unter "Recurrence" und bestimmt weiterhin die Pruef-Frequenz.

956 Unit-Tests gruen, :links:compileDebugKotlin --rerun-tasks ohne Warnung. versionCode 36 -> 37, Dokumentation in android/links/CONTEXT.md. check-in: fb0a144a9c user: beowulf

10:25
links 36: X-LINK-LAST-ERROR lesen, Warnsymbole gross links neben dem Favicon

Seit Server 0.31.0 traegt jeder kaputt geprüfte Link seinen Abruffehler als X-LINK-LAST-ERROR in der DESCRIPTION. Die App kannte die Property nicht und zeigte nur die Änderungswarnung.

(1) Link.lastError: neues Feld, aus X-LINK-LAST-ERROR gelesen (fromNote) und in toNote zurueckgeschrieben, wenn gesetzt. Ohne den Roundtrip wuerde ein Speichern aus dem Editor die Zeile loeschen -- der Server schreibt bei unveranderter Meldung bewusst nichts mehr, der Defekt waere bis zum naechsten Zustandswechsel weg.

(2) LinksListScreen: die Kartenzeile beginnt mit den Warnsymbolen (28 dp, colorScheme.error), danach Favicon, danach die Textzeilen (Titel, URL, Zuletzt geprueft). CloudOff mit contentDescription "Nicht erreichbar: <Meldung>" bei gesetztem lastError, Warning bei changed -- nur im Fehler- bzw. Aenderungsfall. Das bisherige kleine 14-dp-Warn-Icon am Ende der Zeile entfaellt.

956 Unit-Tests gruen (gradlew testDebugUnitTest --rerun --offline), :links:compileDebugKotlin --rerun-tasks ohne Warnung. versionCode 35 -> 36, Dokumentation in android/links/CONTEXT.md. check-in: 8e6c6ff658 user: beowulf

09:54
server 0.31.0: defekte Links werden als X-LINK-LAST-ERROR markiert

Der Link-Checker lief bei jedem defekten Link mit Status 1 aus und speicherte die Meldung nur auf stderr; der vom Server gestartete Addon-Lauf meldete dadurch jedes Mal "Lauf fehlgeschlagen".

Nun schreibt record_defect() eine fehlgeschlagenen Abruf (Verbindungs- fehler, Timeout, HTTP != 2xx, Lesefehler) als X-LINK-LAST-ERROR:<Meldung> in die DESCRIPTION der VJOURNAL-Ressource (analog zu X-NEWS-LAST-ERROR, einzeilig, clean_message() kuerzt auf 300 Zeichen) und liefert den neuen PollResult::Broken. Das Summary-Feld broken zaehlt sie getrennt von errors, die Ausgabe endet mit "N defekt, M Fehler", und main.rs gibt nur noch bei sum.errors > 0 Status 1 -- Config-, Storage-, ICS- und Schreibfehler bleiben Fehler, ein defekter Link nicht mehr. Auch ein Fehlschlag beim Auflisten des /links/-Roots zaehlt jetzt als Fehler.

Nach einem erfolgreichen Abruf wird der Wert geleert (X-LINK-LAST-ERROR: leer = defektfrei, wie bei X-NEWS-LAST-ERROR). Geschrieben wird nur bei geaenderter Meldung oder touch_last_checked, damit ruhige Laeufe den Sync-Token des Links nicht anstossen.

Drei neue Tests in checker.rs: Defekt-Markierung ohne Lauf-Fehler, keine Zweit-Schreibung bei gleicher Meldung, Ruecksetzen nach Erholung. 435 Tests gruen (23 links + 307 Server + 33 news + 60 Integration + 12 rp_site), fmt und clippy sauber.

Dokumentation: server/CONTEXT.md, docs/CONTEXT.md, android/links/CONTEXT.md. Version 0.30.0 -> 0.31.0 (drei Cargo.toml + Cargo.lock). check-in: 87357ea1bb user: beowulf

08:47
server 0.30.0: postinst migration note; version 0.29.0 -> 0.30.0

The 0.29.0 deb was already built, so the postinst migration instructions get their own version instead of rebuilding 0.29.0 in place. secretary-server.postinst prints the upgrade steps (set [links]/[news] active = true + interval, disable legacy secretary-check-links/secretary-news timers, restart secretary-server); docs/CONTEXT.md carries the same note. Versions 0.29.0 -> 0.30.0 in the three Cargo.toml files and Cargo.lock. (user: beowulf) check-in: c68f7a873e user: beowulf

08:39
server 0.29.0: postinst prints addon migration steps

Upgrading the deb alone leaves the addons off (active defaults to false) and the running server keeps the old binary until restarted. secretary-server.postinst now prints the migration steps: set [links]/[news] active = true + interval, disable the legacy secretary-check-links/secretary-news timers, and restart secretary-server. docs/CONTEXT.md gets the same note. (user: beowulf) check-in: e795cfb7ba user: beowulf

08:25
server 0.29.0: addon interval instead of delay_secs

[links]/[news] now use interval = "30m"/"15m" (humantime, std::time::Duration via humantime-serde) instead of delay_secs, matching the podcache server. The server starts each active addon on that interval; the first run fires immediately (tokio::time::interval). active = true with interval = "0s" is rejected as a config error. Example config, debian control/changelog and CONTEXT.md/docs updated. Tests: 307 server unit + 33 news + 20 links + 60 integration + 12 rp_site green; cargo fmt clean, cargo clippy --workspace --all-targets -D warnings warning-free. Versions 0.28.0 -> 0.29.0 in server/Cargo.toml, both helper Cargo.toml files and Cargo.lock. (user: beowulf) check-in: e9981b692a user: beowulf

07:48
server 0.28.0: rename helper crates to secretary-server-*, ship one deb, let the server run the addons

Rename check-links -> secretary-server-links and news -> secretary-server-news (directories, Cargo packages, lib/bin names, user-agent defaults, news PRODID). Ship server plus both addons as a single secretary-server deb; the old secretary-check-links/secretary-news packages are removed on upgrade via Conflicts. The addons no longer have their own systemd units: the server starts the binaries in the same user context, driven by [links]/[news] active + delay_secs (new src/addons.rs: resolves the binary next to the server executable else PATH, passes -c <config>, captures stdout/stderr into the log). active defaults to false, delay_secs to 1800/900; active with delay_secs = 0 is rejected as a config error. Android app secretary-news-android and module android/news are untouched. Tests: 306 server unit + 33 news + 20 links + 60 integration + 12 rp_site green; cargo fmt clean, cargo clippy --workspace --all-targets -D warnings warning-free. Versions 0.27.0 -> 0.28.0 in server/Cargo.toml, both helper Cargo.toml files and Cargo.lock, with matching debian/changelog and CONTEXT.md entries. (user: beowulf) check-in: a4758f2912 user: beowulf

06:46
news: Titelbilder und Aufraeumen alter Artikel (server 0.26.0 -> 0.27.0) X-NEWS-IMAGE: the fetcher now writes the article's image URL as a DESCRIPTION property ahead of body: (layout URL/FEED/PUBLISHED/READ/IMAGE/body), sourced from media:content/media:thumbnail with an image mime type, then image enclosures, then the first ; only absolute http(s) URLs pass, relative and data: URIs are dropped, and articles stay read-only for clients. Pruning runs at the end of every run even when nothing was fetched: [news] prune_max_age_days (default 0 = off) drops articles older than N days, [news] prune_max_per_feed (default 500, 0 = off) keeps at most N articles per subscription, always the oldest first over X-NEWS-PUBLISHED; --dry-run only counts and the summary field is pruned. secretary-server.example.toml documents both options. Tests: 33 news + 297 server unit + 60 integration + 12 rp_site + 20 check-links green, cargo fmt --check clean, clippy --all-targets -D warnings warning-free. Versions in the three Cargo.toml files and Cargo.lock, 0.26.0 -> 0.27.0, with the matching debian/changelog and CONTEXT.md entries. check-in: 339e8f7b0f user: beowulf
2026-10-07
16:38
secrets: full en/de localization (UiMessage) and the FR-S-250 unlock/save side paths (70 -> 71)

FR-S-250 Nebenwege: a locked vault answers with an authentication response (IntentSender + RemoteViews), onSaveRequest offers the save path (SaveInfo + process-local payload) with its own unlock/save activity, caller guard and FLAG_SECURE. UnlockDialog extracted; AutofillResponseBuilder centralizes field/save/response handling. Passkey transport via CredentialProvider (androidx.credentials 1.6.0): PasskeyProviderQueries matches getCredential() to stored key+entry pairs, PasskeyGetActivity/PasskeyCreateActivity run use/create flows, PasskeyProviderIntents maps to the existing PasskeyCeremony. The <meta-data> provider declaration is deliberately absent (its XML schema is only verifiable on a device); noted in todo.md section 7.

Localization (en/de): values/strings.xml (English, source) and values-de/strings.xml (German) now each carry 265 strings with identical keys and placeholder positions, wired via xml/locale_config.xml and android:localeConfig. The new UiMessage model (resource id plus arguments, resolved only in the UI against a Context - the counterpart of common AppError) replaces the ~61 German literals in VaultViewModel; snackbar, unlock dialog, login error and the import/export progress labels render per language. Deliberately still German/diagnostic: exceptions in the data/ and non-provider passkey/ layers, the CacheIssueBanner texts, SaveCallback.onFailure and AutofillSave.titleFor (persisted entry data). todo.md section 3 and milestone M3 updated.

Tests: 687 green in :secrets, lint 0 errors, debug APK builds. versionCode 70 -> 71. check-in: a037d33052 user: beowulf

16:38
news: a feed reader and the server-side fetcher that fills it (server 0.25.0 -> 0.26.0, new client versionCode 1) Server: /news/ becomes the seventh top-level namespace root. resolve_namespace() classifies /news/... as Calendar("news"), so every CalDAV route works unchanged; DELETE /news/ is refused with 400 like the other roots, init(), recover_sync_state() and the root PROPFIND cover it. The fetcher creates two VJOURNAL collections idempotently - /news/subscriptions/ (Abonnements) and /news/articles/ (Artikel) - and keeps its metadata inside the DESCRIPTION via upsert_description so foreign lines survive: X-NEWS-FEED, X-NEWS-LAST-FETCH, X-NEWS-LAST-READ, X-NEWS-LAST-ERROR, X-NEWS-ETAG, X-NEWS-LAST-MODIFIED, X-NEWS-INTERVAL and body: for subscriptions, X-NEWS-URL, X-NEWS-FEED, X-NEWS-PUBLISHED, X-NEWS-READ plus the server-stripped plaintext body for articles. New crate server/news with its own binary secretary-news (-c/--config, -n/--now, --dry-run, -v): conditional GET with ETag/If-Modified-Since, dedupe over a stable entry_key() and a deterministic sha256-derived UID, articles are created and never overwritten so a client read state cannot be lost, fetch errors become a single line of at most 300 characters. Debian gains Package secretary-news with a 15-minute oneshot timer (RandomizedDelaySec=120, Persistent, Nice=10) and a postinst that creates both collections through secretary-news --now. Android: new module android/news (org.beowulf.secretary.news) with NewsViewModel over CrudManager and OfflineSyncEngine, an article list filtered by feed, unread flag and search with unread counts, an article detail that marks the item read, shares it and opens X-NEWS-URL in the browser (no HTML rendering - the server already reduced the item to text), a feeds screen with an editor for URL, title, interval and note that deletes the feed's articles along with it, login and settings as wrappers over the common secretary screens, VIEW/SEND intents prefilling a new feed URL, both collections created client-side when missing and flushed through the write-behind queue, X-NEWS-LAST-READ touched best-effort on read, ochre theme and the [!] icon. Registration: settings.gradle.kts, KNOWN_NAMESPACE_ROOTS in CaldavClient, generate-icons.sh entry (news|#7A5218|[!]|0), the README apps list, the docs/CONTEXT.md table and status entry, and the server/CONTEXT.md log, tree and storage root. Tests: 21 news + 20 check-links + 297 server unit + 12 rp + 60 integration green (59 -> 60), cargo fmt --check and clippy -D warnings clean; Android 956 JVM tests over the modules that have them, :news:assembleDebug builds, :news has no src/test yet. check-in: 830fd43fa5 user: beowulf
07:09
secrets: OTP and passkeys in the vault, autofill, and the merge base that was not there (66 -> 70) v67: a real 412 conflict discarded the user's local edit while the sync reported success. SecretSync.resolveConflict took its merge base from the offline cache, which after applyChanges already holds the edited body, so client == base meant 'unchanged - keep the server'; the pre-edit body now travels in SecretAction.PutResource as baseBody, and the JVM test that seeded the cache with a state the app never writes was replaced by tests that build the state it does write. Closes the UI-crash and e2e block of the roadmap: VaultUiCrashTest and RealServerSyncTest in androidTest, tools/e2e/run.sh starting a fresh server and a fresh AVD per run. v68: TOTP and HOTP work for real. The full FR-K-50 read side (KeePassXC otpauth URI, otp-settings, TOTP Seed/Settings, TimeOtp-Secret-{Base32,Hex,Base64}, TimeOtp-{Period,Length,Algorithm}, KeeOtp with its 1-based otpHashMode, Steam) and the write side moved out of the ViewModel into OtpWriter/OtpReader in the data layer, where the write-then-read roundtrip is testable. Found on the way: the Steam encoder indexed a 26-character alphabet with 31 bits and threw every seventh window; Base32 rejected valid padding and accepted broken padding; a protected otp-settings was written back in plaintext whenever masterKey was null; the third component of TOTP Settings is the time-correction URL, not the algorithm; HOTP received a countdown it cannot have. The passkey card (SRS 9.2) shows website, user, key location and use count while PasskeyFields.isInternal hides the machinery. Autofill fills OTP now (FR-S-250) with the role-based exclusion of FR-S-259, registered in the manifest with BIND_AUTOFILL_SERVICE; HOTP is never filled, because a Dataset is a snapshot and incrementing a counter would be a write in a foreign app. v69: passkeys are usable - create and remove. Platform passkeys are pinned to BE=0 (a non-exportable key cannot promise a backup), PlatformKeyStore sits behind a small backend seam so the JVM test verifies the signature instead of provider bookkeeping, describe() no longer reports 'locked' for every platform passkey, and PasskeyAuthenticator signs through a signer entry rather than exporting the key. v70: the HOTP counter is persisted - advanceCounter replaces exactly counter=, leaves path, secret and issuer alone, and refuses both non-hotp URIs and Long.MAX_VALUE; a hotp URI without counter= no longer crashes the entry screen, it becomes a read-time Broken with a reason; and withUriParams replaces managed parameters in place instead of rebuilding the query, so foreign parameters such as image or lock survive an edit. The SRS drops the Quick Settings tile (FR-S-252) deliberately, with the reason written down: it is the only surface that exposes the vault without naming an entry, and its copy gesture is the one FR-S-258 then has to defend. Tests: 405 -> 670 in :secrets, 939 over all six modules, 20 instrumented tests, androidTest compiles offline. versionCode 66 -> 70, status entries 67-70 in CONTEXT.md, todo.md sections 4 and 7 updated. check-in: 77898845e6 user: beowulf
06:48
server: secretary-rp, a standalone WebAuthn relying party for the passkey tests (0.24.0 -> 0.25.0)

New modules: src/cbor.rs (CBOR subset with decode_prefix, base64url and strict integer label semantics), src/passkey.rs (RP verification for ES256 and attestation fmt none: clientDataJSON, origin, ceremony type, rpIdHash, flags, COSE key, signature, signature counter, backup flags), src/rpsite.rs (test site with password form, passkey ceremony, sessions and HTTP transport) and src/bin/secretary-rp.rs as its own binary. Deliberately separate from the CalDAV server: the test has to measure something that cannot influence the server. The binary is not packaged - debian/rules is untouched and ships neither the site nor the tool.

The interop fix is the part that would have stayed invisible: the RP verified signatures with ring ECDSA_P256_SHA256_FIXED, the PKCS#11 raw r || s form. WebAuthn 6.5.6 and RFC 8152 4.3 require an ASN.1 DER Ecdsa-Sig-Value for ES256. The test authenticator used the same ring, so the complete Rust suite stayed green while every real browser would have failed at authenticate/finish. Now ECDSA_P256_SHA256_ASN1, and the regression test converts a valid signature to r || s as well and demands its rejection.

tools/rp-conformance.py is the counter-test: a WebAuthn client that speaks only HTTP and builds CBOR, COSE, authData, clientDataJSON and ECDSA by hand, driving a full ceremony against the real binary (registration, authentication, challenge reuse, wrong origin, tampered authData, raw-format signature). A test that only checks that 64 zero bytes are rejected would never have seen the signature format bug, because 64 zeros fail in every format.

Hardening of the HTTP parser: fixed limits (64 KiB header, 1 MiB body), refusal of Content-Length together with Transfer-Encoding: chunked (request smuggling), chunk decoding including trailers, and server-side challenge expiry (challenge_expired) instead of a timeout that is only a claim in the JSON. Request now owns its method and path strings instead of leaking one Box per request.

Default origin: --origin is built from the bind address and the port actually occupied when it is not given. The fixed http://localhost default was right only on port 80 and would have rejected every own ceremony in the emulator, where the host is 10.0.2.2 with the port actually in use, as a foreign origin. RP_ORIGIN overrides as before.

Tests: 297 unit + 59 integration (server) + 12 integration (tests/rp_site.rs, WebAuthn over real TCP) green, cargo clippy --all-targets -- -D warnings clean. Version bumped in server/Cargo.toml, server/check-links/Cargo.toml and server/Cargo.lock, 0.24.0 -> 0.25.0, with the Debian changelog entry for it; secretary-rp is documented in CONTEXT.md. check-in: c7a0e4887b user: beowulf

06:43
links: browser choice per link type, and a list that sees every installed browser (33 -> 35)

Settings gain a Browser section with two dropdowns, one for normal links and one for links flagged "Privat oeffnen", persisted in the existing secretary_links prefs as browser_normal / browser_private; empty means the system default. Opening now runs through one place, data/LinkBrowser.kt: options() enumerates the installed browsers, viewIntent() resolves the configured package (falling back to the historical Focus -> Firefox -> default ranking for private links when nothing is chosen) and open() retries with a plain ACTION_VIEW when the preferred browser cannot be launched, so a browser uninstalled between saving the setting and tapping does not fail the tap.

The change-notification path uses the same resolver, and the private flag now travels with the alarm: LinkCheckScheduler carries EXTRA_LINK_PRIVATE from link.privateBrowsing through schedule() and armNext(), the receiver reads it in checkLink, hands it to armNext and showNotification, and builds both the content PendingIntent and the ACTION_OPEN handler from LinkBrowser. The per-link X-LINK-PRIVATE field stays authoritative everywhere.

Private opening actually works now: the extra is private_browsing_mode, which Firefox for Android honors in external ACTION_VIEW intents since 112 (bug 1807531). The old private_tab extra was ignored, which is why private opening only ever worked through Firefox Focus.

The list itself was version 35: options() asked exactly one queryIntentActivities(ACTION_VIEW, http://example.com), so a browser that does not answer that one request was missing from the picker without any error being raised. It now queries http and https and merges the hits, and tops the result up with 18 known browser packages probed by name via getApplicationInfo; the packages are declared as package entries in a new queries block so they stay readable at targetSdk 37, which also keeps the isInstalled() check in viewIntent working. The screen recomputes the list on every composition instead of remembering it once, so a browser installed later appears without restarting the app.

No new tests (:links has no src/test); :links:compileDebugKotlin clean offline, versionCode 33 -> 35, status entries for both versions in CONTEXT.md. check-in: 5d7d76a21c user: beowulf

2026-10-06
16:10
screenshot pipeline: photograph the screen each shot is meant to show.

All 19 screenshots were regenerated and re-attached to the wiki pages Apps/calendar, Apps/contacts, Apps/journal, Apps/launcher, Apps/links, Apps/notes and Apps/tasks; the served bytes were compared against the generated ones and match.

Four things made the pipeline photograph the wrong screen. login() looked for the authenticator registration instead of the account, so it returned early without ever creating one and every app opened its own login form - the check now waits for an actual Account of type org.beowulf.secretary. toolbar_icon addressed the toolbar at a fixed y that lies above every icon of every app, so the taps went nowhere and the stray taps after them opened the Android settings app; it reads the icon row out of the hierarchy now. select_collection pressed BACK after tapping a collection row even though that tap already returns to the list, which left the app behind the screen that should have been photographed. And the launcher drawer does not close on KEYCODE_HOME - the launcher already is the home activity - so the long press landed on an app row and opened its context menu instead of the settings; go_home() closes the drawer with BACK until the clock widget is visible.

tap() used whatever hierarchy the previous lookup left on disk, which is why the event detail was never reached; it refreshes the dump first. The event detail is followed by a BACK before toolbar_icon 1, because the rightmost icon there is delete and asking for the settings icon by position opened the delete confirmation. journal is started directly instead of through select_collection, the launcher-settings expectation no longer matches the hint text of the home screen, and notes-detail accepts the tesseract reading Binkaufsliste.

A capture whose hierarchy matched now gets up to three attempts before OCR rejects it, and shoot.sh aborts when the login failed instead of carrying on with empty screens. verify.sh checks a finished directory for overlays, unreadable text and duplicates, test-lib.sh holds 48 emulator-free checks.

shoot.sh 19/19, verify.sh 19/19, test-lib.sh 48/48. check-in: 434cf4c4a0 user: beowulf

2026-10-04
15:39
scripts: add reproducible screenshot pipeline and fixtures; verified shots included check-in: 7915a48e52 user: beowulf
11:17
docs: build instructions as plain Android SDK/Gradle, no private build system

Das Wiki ist oeffentlich, debian-builds ist privat: alle Bau-Anweisungen sind jetzt normale Android-SDK-/Gradle-Kommandos (JDK 21, Platform 37, Build-Tools 34, Gradle 9). Das Repository hat keinen Gradle-Wrapper, die Beispiele rufen daher 'gradle' direkt und nennen Android Studio als Alternative.

Korrekturen dabei: die Module heissen :calendar, :contacts, ... (nicht ':app' bzw. ':secretary-common-android'), der Wrapper-Aufruf war so nie gueltig. signing.properties ist dokumentiert (eigener Keystore per keytool), die Fehlermeldung in build.gradle.kts nennt keine privaten Pfade mehr. Changelogs behalten ihre Historie, nur die Namen des privaten Buildsystems wurden zu 'das Buildsystem' verallgemeinert. check-in: fae44b2158 user: beowulf

10:48
docs: move README files into the fossil wiki

Die Projekt- und Entwicklerdokumentation liegt jetzt im Wiki: Home (Frontend), Apps/contacts/architecture, Apps/notes/gui-protocol, Apps/tasks/gui-protocol und Apps/secrets/developer-guide. Verweise in CONTEXT.md, SECURITY.md, BENUTZERANLEITUNG.md, todo.md und SRS.md zeigen darauf. check-in: 7fb76f35d8 user: beowulf

10:31
docs: use absolute wiki links in README so fossil renders them cleanly check-in: 62cb9f29d1 user: beowulf
2026-10-03
18:49
ui: add secretary fossil skin with app colors and fonts check-in: 463bc24443 user: beowulf
18:49
cleanup: drop inert .fossil-settings/css override check-in: 7a73218d04 user: beowulf
17:16
ui: remove default skin override check-in: ae25d27aa3 user: beowulf
16:12
ui: add clean skin check-in: c7d6c5fcc2 user: beowulf
16:06
cleanup: remove skin directory check-in: fb612c79a0 user: beowulf
16:05
cleanup: remove old skin and css files check-in: 70a88e314d user: beowulf