Skip to content

Commit 3df1e5b

Browse files
committed
docs: refresh cycle 12 research queue
1 parent 0cdc733 commit 3df1e5b

3 files changed

Lines changed: 126 additions & 3 deletions

File tree

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Cycle 12 Findings - 2026-06-04
2+
3+
## Scope
4+
5+
- Repository: `SwiftFloris`
6+
- Baseline: clean detached worktree at pushed `master` `8b68d3e`
7+
(`docs: refresh cycle 11 research queue`), described as
8+
`v1.8.238-1-g8b68d3e`.
9+
- Sync: `git pull --rebase origin master` reported up to date before this
10+
cycle.
11+
- Constraint: research/docs only. No feature source, tests, build files, or
12+
assets were edited.
13+
14+
## Anti-Duplicate Checks
15+
16+
- Did not duplicate v1.8.234 post-hotfix regression coverage. That release
17+
covers locale-scoped n-gram flush behavior and private-session traces; this
18+
cycle targets the file replacement primitive used by those flushes.
19+
- Did not reopen the n-gram `ConcurrentHashMap` / pending-commit concurrency
20+
fixes from the 2026-06-02 audit summary.
21+
- Did not duplicate R11-1. R11-1 is app startup preference loading; this cycle
22+
is personal n-gram persistence durability.
23+
- Left the `totalEntryCount()` / `resetAndAwait()` race and trigram
24+
tab/control-character normalization audits for later cycles so this row stays
25+
focused on atomic file replacement.
26+
27+
## Local Evidence
28+
29+
- `PersonalBigramStore.kt:303-320` writes a `.tmp` file, attempts
30+
`tmp.renameTo(fileFor(localeTag))`, then deletes the destination and tries a
31+
second rename if the first rename fails.
32+
- `PersonalTrigramStore.kt:305-324` uses the same fallback pattern for trigram
33+
TSV persistence.
34+
- `PersonalNgramFlushIsolationTest.kt:28-50` covers locale-scoped bigram and
35+
trigram flush targeting.
36+
- `PersonalNgramFlushIsolationTest.kt:53-69` inspects the flush body for
37+
per-locale table usage and broad reset cleanup, but it does not reject live
38+
destination deletion or require atomic/replace-existing file moves.
39+
- `docs/AUDIT_2026-05-28.md:31-34` records the data-loss window: deleting the
40+
destination before a successful replacement can remove the last known-good
41+
personal n-gram file if the fallback rename also fails.
42+
43+
## Roadmap Changes Fed
44+
45+
- R12-1: Replace personal n-gram files atomically without deleting live data
46+
first. Implementation should use a shared safe replacement primitive or
47+
equivalent local helper for both bigram and trigram stores, prefer
48+
`Files.move(..., REPLACE_EXISTING, ATOMIC_MOVE)` where supported, and never
49+
remove the destination until the replacement is durable.
50+
51+
## Non-Adds
52+
53+
- No source fix was made in this cycle.
54+
- No new dictionary retention, export, permission, or network behavior was
55+
proposed.
56+
- No broad personal-dictionary refactor proposed. The target is the existing
57+
temp-file replacement path in the two n-gram stores.

RESEARCH_REPORT.md

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SwiftFloris Research Report
22

3-
This report summarizes current research conclusions. The full 2026-05-25 research plan is archived at `docs/archive/research/RESEARCH_FEATURE_PLAN_2026-05-25.md`. Deep-research pass refreshed **2026-06-03** (post-v1.8.204), with 2026-06-04 freshness notes through v1.8.246.
3+
This report summarizes current research conclusions. The full 2026-05-25 research plan is archived at `docs/archive/research/RESEARCH_FEATURE_PLAN_2026-05-25.md`. Deep-research pass refreshed **2026-06-03** (post-v1.8.204), with 2026-06-04 freshness notes through Cycle 12 and v1.8.246 implementation notes.
44

55
2026-06-04 implementation note: v1.8.241 closed R4-3. `MimeTypeFilter`
66
constructor stdout logging is removed, aggregate helper semantics are documented
@@ -38,6 +38,13 @@ must remain local generated output rather than review evidence.
3838
AndroidX Core `1.19.0` remains blocked on the API 37 behavior-gate because the
3939
published `core-1.19.0.aar` metadata declares `minCompileSdk=37`.
4040

41+
2026-06-04 Cycle 12 note: after the Cycle 11 docs push, `master` is clean at
42+
`8b68d3e` (`v1.8.238-1-g8b68d3e`). Cycle 12 rechecked the personal n-gram
43+
persistence data-loss audit against live `PersonalBigramStore`,
44+
`PersonalTrigramStore`, and the locale-isolation flush tests. This cycle adds
45+
R12-1: replace personal n-gram files atomically without deleting the live
46+
destination before a successful replacement exists.
47+
4148
2026-06-04 Cycle 11 note: after the Cycle 10 docs push, `master` is clean at
4249
`31cfa44` (`v1.8.237-1-g31cfa44`). Cycle 11 rechecked the async preference
4350
initialization audit against live `FlorisApplication.init()` and Settings
@@ -159,7 +166,7 @@ F22/F10/F12/API 37 work.
159166

160167
## Executive Summary
161168

162-
SwiftFloris is a mature, heavily-audited privacy-first Android IME (FlorisBoard fork, `dev.patrickgold.florisboard`, `:app` permission-clean with no `INTERNET`). At v1.8.246, the post-v1.8.225 pushed fixes are covered by a release ledger and focused regression tests, the Japanese locale capability typo is fixed, clipboard history search is wired into the keyboard palette, clipboard image/video history tiles have TalkBack labels, non-co-signed addon enrollment now requires explicit Settings trust, the sync sealed-box v1 envelope is pinned by deterministic vector coverage, editor `InputConnection` batch edits now exclude expected-content queue work, stale editor content-generation jobs are cancelled or superseded across input-session boundaries, async preference-store init failures now unblock Settings into the crash recovery path, MIME helper aggregate semantics are documented and pinned without constructor stdout, native string decoding now honors ByteBuffer position/limit windows, localization copy now has Turkish repeated-word/source-label/failure-copy guardrails, visual/manual/release evidence checklists are consolidated, Fastlane changelog drafting guidance is documented, module build-cache cleanup expectations are documented, dynamic incognito toggles re-apply the IME window screen-capture guard immediately, blocked user-dictionary system-back gestures now explain active save/delete/import/export work, Settings search now has TalkBack labels/live result-status/result-row context plus one-shot dismissible destination highlights, and async suggestion candidate generation now uses request-scoped privacy snapshots. The feature surface is broad (autocorrect/prediction, glide typing, clipboard, addons, voice handoff, sync, MCP bridge, hardware-keyboard import). The compatible dependency stack is current for the applied pins (Compose BOM 2026.05.01, Kotlin 2.3.21, AGP 9.2.1, targetSdk 36). Three deep engineering audits (2026-05-28/29 and 2026-06-02) plus the existing roadmap already cover correctness, crypto, resource, and device-gated visual work, so the **net-new** opportunity space is narrow and concentrated on device-gated proof, external actions, and a few docs/build hygiene rows. The **settings search** feature shipped in v1.8.204 (commit `1966c69`) now has drift/no-results/synonym/scroll/accessibility/highlight-lifecycle polish. [Verified]
169+
SwiftFloris is a mature, heavily-audited privacy-first Android IME (FlorisBoard fork, `dev.patrickgold.florisboard`, `:app` permission-clean with no `INTERNET`). At v1.8.246, the post-v1.8.225 pushed fixes are covered by a release ledger and focused regression tests, the Japanese locale capability typo is fixed, clipboard history search is wired into the keyboard palette, clipboard image/video history tiles have TalkBack labels, non-co-signed addon enrollment now requires explicit Settings trust, the sync sealed-box v1 envelope is pinned by deterministic vector coverage, editor `InputConnection` batch edits now exclude expected-content queue work, stale editor content-generation jobs are cancelled or superseded across input-session boundaries, async preference-store init failures now unblock Settings into the crash recovery path, MIME helper aggregate semantics are documented and pinned without constructor stdout, native string decoding now honors ByteBuffer position/limit windows, localization copy now has Turkish repeated-word/source-label/failure-copy guardrails, visual/manual/release evidence checklists are consolidated, Fastlane changelog drafting guidance is documented, module build-cache cleanup expectations are documented, dynamic incognito toggles re-apply the IME window screen-capture guard immediately, blocked user-dictionary system-back gestures now explain active save/delete/import/export work, Settings search now has TalkBack labels/live result-status/result-row context plus one-shot dismissible destination highlights, and async suggestion candidate generation now uses request-scoped privacy snapshots. The feature surface is broad (autocorrect/prediction, glide typing, clipboard, addons, voice handoff, sync, MCP bridge, hardware-keyboard import). The compatible dependency stack is current for the applied pins (Compose BOM 2026.05.01, Kotlin 2.3.21, AGP 9.2.1, targetSdk 36). Three deep engineering audits (2026-05-28/29 and 2026-06-02) plus the existing roadmap already cover correctness, crypto, resource, and device-gated visual work, so the **net-new** opportunity space is narrow and concentrated on small data-integrity/API-contract hardening, device-gated proof, external actions, and docs/build hygiene rows. The **settings search** feature shipped in v1.8.204 (commit `1966c69`) now has drift/no-results/synonym/scroll/accessibility/highlight-lifecycle polish. [Verified]
163170

164171
Top opportunities (one line each):
165172

@@ -188,6 +195,7 @@ Top opportunities (one line each):
188195
23. **Suggestion privacy request snapshot** — async candidate generation now freezes incognito/editor sensitivity and suggestion preference inputs before provider, trace, and ghost-text work runs (R9-1). [Closed]
189196
24. **Editor content-generation lifecycle** — delayed start/selection content jobs are cancelled or superseded across reset/finishInput and input-connection switches before they can publish state or touch a captured `InputConnection` (R10-1). [Closed]
190197
25. **Preference-store init splash recovery** — async `initAndroid` failures now stage a crash report, unblock the splash wait, and redirect to crash recovery before normal Settings content renders (R11-1). [Closed]
198+
26. **Personal n-gram file replacement** — bigram/trigram flush fallback deletes the live file before a successful replacement exists (R12-1, P2). [Verified]
191199

192200
No Critical or Major reliability/security defects were found that are not already on the roadmap or in the deferred audit lists. The remaining heavy work (glide model training, Vosk addon, F-Droid submission, device-only visual verification) stays maintainer-gated as the existing roadmap records.
193201

@@ -266,6 +274,10 @@ Privacy-first multilingual IME. `:app` is Apache-2.0-ceiling, no network permiss
266274
preference-store failures by staging the failure, marking preference loading
267275
complete, and checking the staged crash path before Settings content renders.
268276
[Closed]
277+
- **Personal n-gram persistence (partial):** locale-scoped flushes and
278+
concurrency guards are in place, but bigram/trigram file replacement still
279+
deletes the destination before a second rename attempt. R12-1 keeps the
280+
last-known-good n-gram file until replacement succeeds. [Verified]
269281
- Established surfaces (autocorrect/SymSpell, glide classifier, clipboard, addons, voice handoff, sync, MCP, hardware-keyboard import) are covered by `COMPLETED.md` and the audits; no net-new gap surfaced beyond what the roadmap already tracks.
270282

271283
## Competitive Landscape
@@ -301,6 +313,9 @@ Privacy-first multilingual IME. `:app` is Apache-2.0-ceiling, no network permiss
301313
launched `FlorisPreferenceStore.initAndroid(...)` path is supervised,
302314
error-guarded, staged through crash recovery on failure, and always unblocks
303315
the Settings splash wait for non-cancelled completion.
316+
- **[Medium] Personal n-gram atomic replacement** → R12-1. Replace bigram and
317+
trigram TSV files without deleting the live destination before a successful
318+
replacement exists.
304319
- **[Closed v1.8.219] Remaining diagnostic `printStackTrace()` paths** → R2-2. `RestoreScreen` failure diagnostics now use `flogError`, restore UI copy falls back to the existing "Unknown error" string for null/blank throwable messages, and `CrashUtility.writeToFile` logs through `LogTopic.CRASH_UTILITY`.
305320
- **[High] Local release ledger drift** → R3-1. Three code-fix commits after
306321
the v1.8.225 docs marker are untagged and absent from the release ledger.
@@ -389,6 +404,10 @@ Privacy-first multilingual IME. `:app` is Apache-2.0-ceiling, no network permiss
389404
failures are logged and staged, `preferenceStoreLoaded` unblocks the splash,
390405
and `FlorisAppActivity` checks the staged crash path again before normal
391406
Settings content renders.
407+
- **Personal n-gram durability boundary:** `PersonalNgramFlushIsolationTest`
408+
pins locale-scoped flush behavior, but the stores still need a shared
409+
atomic-replace contract so persistence failures cannot destroy the previous
410+
locale file.
392411
- **User-dictionary navigation policy:** `UserDictionaryEntryPolicy` correctly
393412
centralizes leave/mutation/transfer gates. v1.8.232 keeps that policy and
394413
adds a visible response when Compose back handling blocks the gesture during
@@ -401,7 +420,7 @@ Privacy-first multilingual IME. `:app` is Apache-2.0-ceiling, no network permiss
401420

402421
## Security / Privacy / Data Safety
403422

404-
No net-new permission or data-egress finding. The settings-search additions are display/navigation only; the no-results Browse all settings action (RA-2), synonym keyword coverage (RA-3), and query-change scroll reset (RA-10) do not weaken the no-network posture. R2-1 and R2-2 closed as local diagnostic-safety work without adding network, telemetry, or broad file export. R11-1 closes the async side of startup diagnostics by surfacing preference-store init failures through the existing local crash recovery path without adding storage, permissions, or outbound data. R3-2 is also local-only clipboard filtering. R3-3 closed as sync-crypto contract hardening before transport activation, with no new permission or native dependency. R4-1/R4-2/R4-3/R4-4 are closed local correctness/a11y/API-contract work. WS12 and WS10/WS15 are docs/resource-only and do not change permissions, retention, or storage behavior. R5-1 closed as trust-boundary hardening for optional addon APKs: it keeps the no-network addon screen but requires explicit trust before non-co-signed packages become active. R6-1 is local editor critical-section hardening and does not change storage, permissions, or outbound data. R7-1 closed as privacy posture hardening for the existing incognito mode and `FLAG_SECURE` contract, not a permission change. R9-1 is privacy-state hardening for existing local suggestion and smart-compose paths: it keeps the no-network posture and ensures `IME_FLAG_NO_PERSONALIZED_LEARNING` / incognito decisions are request-scoped across async work. R10-1 is local editor-session lifecycle hardening and does not change storage, permissions, or outbound data. R8-1 is UI feedback for an already-blocked dictionary operation path and does not change data retention, dictionary mutation, or export/import permissions. WS13 now explicitly includes the deferred `StickerMediaProvider.openFile` SAF allow-list validation so forged encoded sticker URIs are rejected without broadening file access. The deferred audit lists (`docs/AUDIT_2026-06-02.md`) remain the authority for crypto/parsing/lifecycle hardening; this pass does not duplicate them.
423+
No net-new permission or data-egress finding. The settings-search additions are display/navigation only; the no-results Browse all settings action (RA-2), synonym keyword coverage (RA-3), and query-change scroll reset (RA-10) do not weaken the no-network posture. R2-1 and R2-2 closed as local diagnostic-safety work without adding network, telemetry, or broad file export. R11-1 closes the async side of startup diagnostics by surfacing preference-store init failures through the existing local crash recovery path without adding storage, permissions, or outbound data. R12-1 is local personal-prediction durability hardening and does not change dictionary retention, export, permissions, or outbound data. R3-2 is also local-only clipboard filtering. R3-3 closed as sync-crypto contract hardening before transport activation, with no new permission or native dependency. R4-1/R4-2/R4-3/R4-4 are closed local correctness/a11y/API-contract work. WS12 and WS10/WS15 are docs/resource-only and do not change permissions, retention, or storage behavior. R5-1 closed as trust-boundary hardening for optional addon APKs: it keeps the no-network addon screen but requires explicit trust before non-co-signed packages become active. R6-1 is local editor critical-section hardening and does not change storage, permissions, or outbound data. R7-1 closed as privacy posture hardening for the existing incognito mode and `FLAG_SECURE` contract, not a permission change. R9-1 is privacy-state hardening for existing local suggestion and smart-compose paths: it keeps the no-network posture and ensures `IME_FLAG_NO_PERSONALIZED_LEARNING` / incognito decisions are request-scoped across async work. R10-1 is local editor-session lifecycle hardening and does not change storage, permissions, or outbound data. R8-1 is UI feedback for an already-blocked dictionary operation path and does not change data retention, dictionary mutation, or export/import permissions. WS13 now explicitly includes the deferred `StickerMediaProvider.openFile` SAF allow-list validation so forged encoded sticker URIs are rejected without broadening file access. The deferred audit lists (`docs/AUDIT_2026-06-02.md`) remain the authority for crypto/parsing/lifecycle hardening; this pass does not duplicate them.
405424

406425
## UX & Accessibility
407426

@@ -427,6 +446,8 @@ The keyboard surface already has a strong a11y baseline (`ACCESSIBILITY.md`, `To
427446
delayed editor-session contract is covered by focused JVM/Robolectric tests.
428447
6. v1.8.240 still needs manual forced preference-init debug smoke on a device;
429448
the async failure contract is covered by focused JVM/Robolectric tests.
449+
7. R12-1 needs a focused file-replacement/flush test; no maintainer product
450+
decision is required.
430451

431452
## Archived Evidence
432453

@@ -460,3 +481,4 @@ The keyboard surface already has a strong a11y baseline (`ACCESSIBILITY.md`, `To
460481
`IME_FLAG_NO_PERSONALIZED_LEARNING`.
461482
- Cycle 10 companion: `.ai/research/2026-06-04/CYCLE_10_FINDINGS.md`.
462483
- Cycle 11 companion: `.ai/research/2026-06-04/CYCLE_11_FINDINGS.md`.
484+
- Cycle 12 companion: `.ai/research/2026-06-04/CYCLE_12_FINDINGS.md`.

0 commit comments

Comments
 (0)