docs: record the day the archive learned to come back
Batch 10 closed, and with #58 the tracker has no open issue for the first time. The entry carries the two things that would otherwise be re-derived: a test premise that was wrong in the direction of passing, and a KDoc claim about `clearAllTables` that was confidently wrong until it was checked against the bytecode. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0ae92bd134
commit
ebe5ab8d6f
|
|
@ -32,6 +32,95 @@ written and stay true. It is exempt from review for the same reason a receipt is
|
||||||
|
|
||||||
## Entries
|
## Entries
|
||||||
|
|
||||||
|
### 2026-08-21 — The export stops being a one-way door; the tracker is empty
|
||||||
|
|
||||||
|
`#58` was the last open issue in the repository, and it closed the loop the
|
||||||
|
export left open: the app offered a copy of everything and nothing could read
|
||||||
|
one back. A user changing phone started the prediction model from zero, and the
|
||||||
|
archive's own prose — *"Keep this file. A future version of the app will be able
|
||||||
|
to read it back"* — was a promise nothing checked.
|
||||||
|
|
||||||
|
**What shipped** (`0ae92bd`). `ExportReader` mirrors `ExportDocument.render` in
|
||||||
|
the same module, over a hand-written strict `Json.kt`: no library, because
|
||||||
|
`core/export` has two dependencies and their narrowness is the point, and strict
|
||||||
|
because a parser that accepts a trailing comma is a parser that will one day
|
||||||
|
accept somebody else's file as a cycle history. Refusals are typed —
|
||||||
|
`NotOurFile`, `NewerFormat(v)`, `Damaged` — and unknown *keys* are ignored,
|
||||||
|
which is rule 2's other half and the only reason the format can ever grow.
|
||||||
|
|
||||||
|
`CycleRepository.importHistory` is a deliberate third write path rather than a
|
||||||
|
loop over `confirmPeriodStart`, and the reason is worth carrying forward: an
|
||||||
|
archive is history the app never forecast, so scoring any of it fills §16's
|
||||||
|
figures with numbers the app invented about itself. The backfill guard catches
|
||||||
|
most of that by date and **not all of it** — a file exported this morning
|
||||||
|
carries this morning's period, which would sail through. The test names that
|
||||||
|
case explicitly.
|
||||||
|
|
||||||
|
**Two things learned the hard way, both recorded so nobody re-derives them:**
|
||||||
|
|
||||||
|
- **A test premise can be wrong in the direction of passing.** The first
|
||||||
|
atomicity test fed the importer a file naming the same day twice, expecting
|
||||||
|
the UNIQUE index to abort mid-import. It never threw: the importer's own
|
||||||
|
dedupe skipped the duplicate, so the test was measuring a successful import.
|
||||||
|
Rewritten to fail the *last* step — the recalculation — via a counting engine
|
||||||
|
that can be told to throw.
|
||||||
|
- **A comment can be confidently wrong about a library.** The first version of
|
||||||
|
`replaceEverything`'s KDoc said `clearAllTables` "ends the transaction it is
|
||||||
|
called in", which sounded right and was not: nesting it under Robolectric
|
||||||
|
rolled back cleanly. What is actually true, checked against room-runtime 2.8.4
|
||||||
|
bytecode rather than the documentation, is that `performClear` opens with
|
||||||
|
`assertNotSuspendingTransaction()` and runs its work through
|
||||||
|
`runBlockingUninterruptible` — a blocking coroutine of its own — and finishes
|
||||||
|
with a `VACUUM` SQLite forbids inside a transaction. So the function stays, on
|
||||||
|
a claim that can be checked, and Delete My Data keeps `deleteEverything` and
|
||||||
|
its VACUUM. Replacing a history is not erasing one.
|
||||||
|
|
||||||
|
**Recorded decisions.** The archive cannot touch the app lock: it carries
|
||||||
|
`biometricUnlock` and the importer does not apply it, because there is no PIN in
|
||||||
|
the file and there cannot be. Merge leaves a date already recorded on this phone
|
||||||
|
exactly as it is rather than merging field by field — §14 forbids a file
|
||||||
|
rewriting a record she made here. And the choice between add and replace is
|
||||||
|
asked **before** the picker, not after the file is read where the app could have
|
||||||
|
said how much was in it: the screen that would ask is the screen a re-lock
|
||||||
|
destroys.
|
||||||
|
|
||||||
|
**One correction to a thing filed yesterday.** `#58` was titled "Restore from an
|
||||||
|
encrypted export" and its body said `DataExporter` "writes an encrypted
|
||||||
|
archive". It does not, and never did — `ExportCopy.PROTECTION_BODY` and the
|
||||||
|
file's own `aboutThisFile` both say so in as many words. The issue was wrong when
|
||||||
|
filed; what shipped reads the plaintext archive the app actually writes.
|
||||||
|
|
||||||
|
**Device evidence** (PeriodQA, emulator-5554, debug build of `0ae92bd`): Settings
|
||||||
|
→ Restore from a file → Add to what is here → Downloads →
|
||||||
|
`records-2026-08-19.json` gives *"Restored. 4 periods and 2 spotting days
|
||||||
|
added."*; Insights then shows three cycles with accuracy still unscored; the same
|
||||||
|
file a second time gives *"Everything in that file was already recorded here, so
|
||||||
|
nothing changed."* `uiautomator dump` rather than screenshots — FLAG_SECURE
|
||||||
|
returns black.
|
||||||
|
|
||||||
|
**Also in this commit:** `LockSettingsViewModelTest`'s hang budget went from 60
|
||||||
|
seconds to three minutes. It passed alone and failed when `:app`, `:core:data`,
|
||||||
|
`:core:export` and `:core:datastore` ran in one invocation — three real PBKDF2
|
||||||
|
derivations at 210,000 iterations. The budget catches a stuck coroutine, not a
|
||||||
|
slow one, and a flaky guard is one people learn to ignore.
|
||||||
|
|
||||||
|
- **Closed:** #58. `Batch 10 — Show what you already know` closed with it.
|
||||||
|
**Every issue in the tracker is now closed**, and Batches 09, 10, 11 and 12
|
||||||
|
all landed since yesterday morning.
|
||||||
|
- **Next action:** A release, or the next batch — and the decision between them
|
||||||
|
is Kaspa's. What argues for cutting one now: no known defect is outstanding,
|
||||||
|
both P0 release-blockers found yesterday are fixed and verified on a device,
|
||||||
|
and `./gradlew check` is green including `checkNoHealthLogging` and
|
||||||
|
`checkPermissions`. What argues against: `SECURITY_CHECKLIST.md`'s device rows
|
||||||
|
have been *written* rather than *walked* for the biometric-only and reminder
|
||||||
|
paths, and nothing in this project has ever run on an API 26 device, which is
|
||||||
|
its own `minSdk`. Recommendation: walk the checklist on a 26 image and on
|
||||||
|
PeriodQA before the image is cut, then release.
|
||||||
|
- **Blockers:** Unchanged and both need a person, not an agent — #8's three
|
||||||
|
branding marks, which must not be faked, and #9's Command Center webhook URL
|
||||||
|
and secret, which are not readable from this machine. Until it is registered an
|
||||||
|
opened `P0` raises no alert at all.
|
||||||
|
|
||||||
### 2026-08-20 (evening) — Two release-blockers, and the trap that was reported
|
### 2026-08-20 (evening) — Two release-blockers, and the trap that was reported
|
||||||
|
|
||||||
Kaspa reported one bug — *"on the settings page, if you go to app lock you can't
|
Kaspa reported one bug — *"on the settings page, if you go to app lock you can't
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue