diff --git a/pb_migrations/1789500000_client_logs_tester.js b/pb_migrations/1789500000_client_logs_tester.js new file mode 100644 index 0000000..5b56d7d --- /dev/null +++ b/pb_migrations/1789500000_client_logs_tester.js @@ -0,0 +1,51 @@ +/// + +// Add `tester` to client_logs: was this session OURS, or a real viewer's? +// +// WHY THIS MIGRATION EXISTS: telling our own test runs from the family's sessions +// was impossible. Three `no_first_frame` rows from a development emulator sat in +// the middle of a production log dump for an evening, and the only reason they +// were not read as a family member's television was that an emulator decoder name +// (`c2.goldfish.hevc.decoder`) happened to be visible in a field logged for an +// entirely unrelated reason. That is luck, not a method, and a wrong attribution +// here sends someone chasing a bug that nobody has. +// +// THIS IS THE THIRD TIME the same silent drop has cost us: hidden `profiles.pinHash` +// PATCHes, then `client_logs.flavor` (see the migration beside this one), now this. +// PocketBase drops an unknown key in a create payload with no error, no warning and +// HTTP 200. **A field the client sends does not exist until a migration declares +// it.** It was caught this time only because the added field was read back off a +// measured session instead of being assumed to have arrived — `tester: None` on a +// row whose build definitely sends it. +// +// Not folded into `meta`: this is a dimension of every record regardless of event +// type, exactly like `platform`, `flavor` and `device` beside it, and the query it +// exists for is "hide our own rows", which should not have to reach into a blob. +// +// The reader (`amber-app/tools/reports.py`) deliberately does NOT depend on this +// column — it also recognises an emulator from `device` and a test address from the +// resolved account, so it stays correct before this deploys and if a future client +// omits the field. Belt and braces on purpose, because the column is exactly the +// kind of thing that silently goes missing. + +migrate( + (app) => { + const logs = app.findCollectionByNameOrId("client_logs") + logs.fields.add( + new BoolField({ + name: "tester", + // Not required. A bool field in PocketBase defaults to false, which is the + // correct reading for every historical row: they came from the family's + // devices. Rejecting a record for a missing dimension would trade a whole + // diagnostic away for a label. + required: false, + }), + ) + app.save(logs) + }, + (app) => { + const logs = app.findCollectionByNameOrId("client_logs") + logs.fields.removeByName("tester") + app.save(logs) + }, +)