Repository navigation
v1.0.0-rc.17
Pre-releaseLatest five-day study: download the current signed study and Traditional Chinese instructions from the participant page. The study uses stable filenames and URLs: study.partcfg and participant instructions. The current questionnaire contains seven questions. Download and import the study again for each new setup; installing the APK alone does not update an imported study. Existing enrolled studies keep their original configuration. The dated 20261005 participant ZIP is a historical six-question snapshot, not the current study download.
This release improves participant disclosure, collection recovery, and verification of
five-day study exports. Physical-device admission checks and daily data return remain necessary;
these changes do not establish a completed 120-hour physical-device endurance run.
The RC14, RC15 and RC16 tags did not produce public releases because their CI gates failed. RC17
follows RC13 as the next public release candidate.
-
Application dependency construction and session initialization run in the background behind one
explicit readiness boundary. Participant screens, Android results and study-entry intents,
workers and receivers wait for the same initialized components. Unexpected initialization
failures release waiters and show a neutral failure screen; they do not leave a permanent
loading screen or expose incomplete components. Foreground-service notification and startup
acknowledgement remain immediate, before waiting for the application, so recovery cannot
deadlock with service startup. Existing recovery-required state and participant controls remain
unchanged. This removes filesystem, KeyStore and client construction from process binding;
it does not guarantee responsiveness under arbitrary device CPU starvation. -
Interrupted runtime checkpoint writes can recover from an authenticated base and the complete
authenticated commit log even when a staging snapshot was only partly written. Staging files are
retired only after recovery succeeds. Invalid bases, authenticated conflicting documents, damaged
retained commits and pending-input failures still stop recovery; process recovery remains paused
until the participant explicitly resumes. -
Limited traffic uses separate bounded flow queues in both directions before
paced Layer-3 admission. Previously, waiting for uplink credit stopped TUN reads;
sustained transfers could fill the kernel queue and stall TCP delivery even when
their average rate passed. A downlink FIFO also let download data delay upload
acknowledgements. Each direction now has a shared hard limit of 128 packets and
64 KiB across 1024 fixed hash buckets, with byte-deficit scheduling.
Uplink uses CoDel and capacity drops. Limited downlink admission never waits for
queue capacity on a shared TCP processor: it removes the minimum required tail
packets from the highest occupied-MTU score (ceil(backlog_bytes / 1500)),
including the incoming packet in that decision. Equal scores rotate the
resident tail selected for removal, so smaller resident single-packet queues
cannot always reject a full-MTU newcomer. A strictly highest-score incoming
backlog still loses its incoming tail. Scheduling remains byte based; admitting
a large packet can require multiple small-packet drops, and this policy does
not guarantee ACK priority or progress for every flow under arbitrary traffic.
Its effective byte limit follows 100 ms of configured bandwidth,
bounded by one MTU and 64 KiB; the consumer can hold one additional MTU.
A completed limited enqueue yields to ready workers after releasing its locks,
without waiting for the queue to drain. TCP explicitly uses Reno with SACK-based
recovery and leaves RACK disabled; controlled duplex tests exposed repeated
timeout/recovery cycles with the pinned stack's RACK mode after initial queue
loss. This choice applies to every study profile and does not alter the signed
rate settings. Congestion drops and retransmissions remain possible.
Classification keys are temporary; flow identities and hashes are not recorded
or exported. Counters include only admitted Layer-3 packets, including admitted
retransmissions; downlink admission requires a successful TUN write. Unlimited
profiles retain FIFO queue backpressure. Suspension, profile replacement and
shutdown retain the admission fence, and queued packets use the current profile
when delivered. Hash collisions and opaque traffic can share a bucket; strict
per-flow fairness and lossless delivery are not guaranteed. -
WorkManager initializes on demand with its existing default configuration. Its database is no
longer opened by the eager startup provider, and constructing the action and upload adapters
does not initialize it. Durable timer, action and upload scheduling still request the same
application-wide WorkManager instance when needed. -
Emulator throughput diagnostics can explicitly sample native counters, fixture write progress
and kernel drop/retransmission counters, or run a bounded 300-second stress interval. Routine
release gates retain their 60-second duration and original rate bounds, without periodic
foreground diagnostic broadcasts changing the app's process priority. -
Throughput gates also require each selected connection to make progress after a ten-second
warmup: more than five consecutive complete one-second buckets without payload fails the
measurement even if its aggregate rate passes. A simultaneous upload/download case checks
actual Android-received download payload under the same fixed 512/512 kbps profile, with
bounded start synchronization and the original rate bounds in both directions.
A focused 64/64 kbps duplex run uses the corresponding signed fixture and the
same rate and liveness bounds; the default release suite retains its 512 kbps case. -
Emulator readiness waits for completed boot before querying ActivityManager and
allows the stock SDK image to finish its initial HOME setup within the existing
deadline. API 37 cleanup bounds console commands, terminates only the process
owned by that runner, preserves the original failure status, and records whether
the emulator and temporary AVD were actually removed. -
Usage-event queries now check Usage Access and the unlocked-user state before and after each
query, including an empty barrier flush. Lost access fails the collector without advancing its
successful-query coverage, rather than recording an empty result as successful collection. -
A paused study immediately posts a neutral, persistent reminder to open Particeps and check
its status. Resume or study removal clears it. Notification delivery blocked by permissions or
channel settings is retried during access reconciliation; it is not cached as delivered.
Recovery remains paused until the participant explicitly resumes. -
Participants who have started a study get one Study and my data entry, identical in every
study arm, on the collection panel and on the access panel shown while required access is
repaired. It restates the study, researcher contact, consent text and version, signing-key
fingerprint, installation code and any assigned code, what each data category records and does
not record with the Android access it uses, the upload terms, the fixed VPN paragraph for a study
that may adjust transfer speed, and what Pause, Resume, Withdraw, and Delete local data do. It
also shows the state, study day, planned end once the phone's clock is verified, collecting and
paused time, the size and event count of an export made since Particeps last started, and local
storage measured when the page opens. Both sizes are shown in 50 MB steps ("Less than 50 MB",
"50–100 MB", and so on), and no byte count reaches the screen. The steps limit the bytes stored
per event, which depend on the arm's automation through per-commit overhead, to what a 50 MB
difference reveals. They do not hide when collection runs: the export's event count on this page
and the running screen's live event count stay exact. It has no lifecycle control. The Details
toggle is gone, and the signed configuration ID, which differs between study arms, is no longer
shown anywhere. -
Every setup step, from import through Ready, offers Decline and remove this study. After a
confirmation it removes the study and setup progress through the local deletion path; Android
access granted during setup stays on. -
The paused-since time and an ended study's length come from the commit that entered that state,
so later commits in the same state, such as upload acknowledgements, no longer move them. -
Intervention notifications carry a fixed public version: the app name and "A research activity
is available. Unlock your phone to see it." Android shows it in place of the researcher's title
and message only when the lock screen is set to hide sensitive content; Android's default shows
all content. -
Data-category copy states what each category records and does not record and no longer quotes
profile settings, such as sampling rates or poll intervals, that can differ between arms. The app
category is "Particeps app activity". The fixed traffic-shaping paragraph is reworded to be true
for both selected-app and all-app targets, the consent checkbox reads "data collection and
sharing", and the consent upload sentence and the public participant page say that unsent data
"can still be sent" after pausing or withdrawing. -
Upload progress on the running screen and export phase progress are whole percentages. Batch
counts are no longer shown, because how many commits a study makes depends on its automation. -
Automatic upload begins at Start. Nothing is staged, scheduled, or sent during setup, and the
first bundle begins at commit 1 and carries the setup commits with the first collection. If
scheduling the upload chain fails after Start, Start keeps its result and the next Resume or
access reconciliation retries it. -
Gyroscope and accelerometer samples commit in batches through a window of up to 5 s of awake
time when no automation in the signed study matches that sensor's events and the study has no
sequencetrigger orwindow_thresholdtrigger or condition. Otherwise every sample commits
without waiting, as before. Observed time, values, sampling rate, and condition-epoch attribution
are unchanged;committed_atcan trail capture by up to 5 s, and for the accelerometer, which
holds no wake lock, by any CPU suspend while a batch is open. Process death loses the samples
since the sensor's last recorded one, inside thePROCESS_RECOVERYquality gap that recovery
records. A safety pause, a phone clock or time-zone change while the study runs, and a clock
change first seen after the deadline each drop up to 5 s of open-batch samples with no quality
gap that names the sensor. The researcher guide and data dictionary give the analysis rules. -
Callback collectors merge callbacks that are already queued into one observation and, without a
window, never wait for more. When a desired resource would change only after a live batch's first
event, the runtime records the events before it and the collector offers the rest under the next
producer ordinal. A commit records each of the reducer's condition-timer intents in the
reducer's order, as RC13 did, except those for a timer generation that the same commit armed
and then retired or replaced, and only the recorded intents reach WorkManager. A merged batch
that slides a window once per event therefore retires the prior timer once and schedules only
the final one, and recovery of a running study, whose quality-gap reset re-arms each condition
timer before the safety pause retires it, records one retirement per timer and leaves no
wakeup for the re-armed generation. -
A timer wakeup that the runtime rejects as stale, because it holds that timer at a later
generation, now ends. RC13 retried it with a backoff that grows by 10 s per attempt, about 130
attempts in the first day, until the timer was next removed, such as at the next pause. -
Collector API:
EmitBatchResult.AcceptedgainsrecordedEvents, andCollectorContextgains
requiresPromptCommits.SerializedCallbackCollectorhandles both; a collector that implements
Collectordirectly and emits multi-event live batches must offerevents.drop(recordedEvents)
again under the next producer ordinal. -
Cold start authenticates the retained commit log once. The encrypted store caches its engine key
and commit-log byte totals, the runtime reuses per-commit digests and precomputes reducer state
keys, timer identities, local times, literals, and thresholds, and the participant UI state is
built only while the Activity is started. -
A recovery reset now deletes the study's Android Keystore engine key. RC13's reset matched an
alias prefix no key used and left the key in place. -
A commit the store has made durable is always applied to the runtime's memory, even when the
caller is cancelled during the append. Before, such a cancellation could leave every later commit
rejected, with the study still shown as running, until the process restarted. -
The researcher site's participant preview shows only the upload destination host, as the app
does, and its fixed VPN paragraph is checked against the app's string resources.
Analysis tools (no app change):
-
Authenticated bundle JSON, commit reassembly and replay now use streaming reads and private
SQLite staging rather than keeping the complete commit history in memory. The pipeline verifies
the whole participant chain before publishing Parquet, bounds individual commit and JSON tokens,
and removes temporary plaintext on handled failures. Metadata still grows with participants,
epochs and actions; this is not a constant-memory guarantee for every input. -
Materialization retains successful usage-query coverage and hashes its quality summary in the
dataset manifest. The newusage-reportcommand verifies that dataset and reports foreground
intervals, resumed events, censoring and coverage before, during and after the 12:00–17:00
reference window. Missing collection is not counted as zero use. Traffic receipts and snapshots
produce a separate report of applied limited, unlimited and unknown intervals; these intervals
are not evidence of achieved network speed or a causal treatment effect. -
Before this change, no export from RC13 could be verified or analyzed, including the running
five-day pilot's, with either RC13's or this tree's offline tools. Decryption succeeded and the
files are intact, but verification stopped at commit 1.researcher-tools decryptfailed with
"Automation checkpoint reducer cursor diverges from commit" and wrote no output,
particeps-analysisfailed with "automation checkpoint has an incomplete desired resource
vector", and the researcher site's Read step reported the bundle unreadable. Past commit 1, the
tools also rejected shapes that RC13 writes in normal use:- the usage-events cursor stored at a barrier flush;
- the usage-events checkpoint that recovery drops;
- the
CANCELLEDandLIFECYCLE_ENDEDtimer retirements at a window barrier and at the deadline
stop; - a recovery that closes the epoch without a traffic-shaping audit, or in the new boot after a
reboot; - deadline-timer events that carry the epoch being closed;
- the deadline that a Complete or Withdraw from
RUNNINGretires; - usage-events coverage that starts a few milliseconds before its epoch;
- a survey requested at the 17:00 barrier and answered in the next epoch;
- the timer events of RC13's recovery commits.
A chain that passed verification still could not be written: the Parquet sink failed on
json_stringfields and on payload fields namedcondition_epoch_idorsource_id. -
protocol/v1/README.mdnow specifies what RC13 writes, with no branching on the producer's
client version, and the same rules accept this build's exports. Each rule is relaxed only as far
as the observed shapes require.particeps-analysisenforces every rule. The Kotlin verifier and
the researcher site's bundle reader do not replay the reducer and enforce the subset that needs no
replay, which the protocol's Conformance section lists: for example the reducer cursor's bounds,
the complete checkpoint upsert, onePROCESS_RECOVERYgap per recovery, and a recovery close at
the recovery instant. A bundle thatresearcher-tools decryptverifies can therefore still be
quarantined by the analyzer, which names the rule, for example for a timer rendering or a
deadline that only a replay can check. -
The rules also accept RC13 shapes that come from platform failures and that no pilot export is
known to contain yet: a pause or barrier in the same millisecond as a usage-events poll, whose
flush covers the empty interval[t, t); a safety pause after the VPN is revoked or replaced,
whose boundary audit cannot read the traffic counters and so records none; a stop whose resource
release fails, which safety-pauses thePAUSINGstudy and leaves itPAUSEDwithout a deadline
until Resume re-arms it; a process death whilePAUSING, whose recovery re-arms the deadline; and
a reboot without trusted UTC, whose recovery commit keeps the previous boot's clock anchor as
committed_at. Each is reproduced by a checked-in RC13 export. -
The recovery close is bound exactly. An epoch event's boundary must equal its observed time, and
a recovery close must lie at the recovery instant, the time of itsPROCESS_RECOVERYgap. Only
that close may lie in a boot other than the activation's. -
Parquet output renames six payload fields that share a name with a provenance or partition
column topayload_<name>, as the data dictionary lists, and eachjson_stringcolumn holds the
authenticated wire text. -
An
interventions.v1row'ssource_condition_epoch_idis now the epoch in which its
ACTION_REQUESTEDwas recorded, bound by the occurrence identity, instead of the epoch that
contains its scheduled time. A survey requested at the 17:00 barrier therefore belongs to the
closing epoch, not to the next one in which it is answered. -
quality-summary.jsonlists each participant's condition epochs with their preparation bound,
activation, and deactivation. Usage-events rows may begin at the preparation bound, before the
epoch's resources were confirmed; exclude rows beforeactivated_atto keep only time under the
applied condition. -
Eleven real exports are checked in under
particeps-analysis/tests/fixtures/runtime-bundles/:
ten from RC13 and one from this build, all encrypted to the public demonstration key. The Kotlin
verifier and the site's reader must accept every one and each retained range that starts at a
recovery or an epoch boundary, andparticeps-analysismust also materialize each to Parquet.
CI also runs this tree's runtime through a pilot-shaped week and passes its exports to both. That
week includes barriers, pauses, a survey, process death, a reboot, clock changes, and the
deadline stop. A change to the published pilot configuration now also runs the Android CI
workflow, whose real-runtime test reads it. -
The RC13 export-verification correction itself needs no phone update. It does not require a
different event-source registry, bundle format, or reducer; existing RC13 exports verify as they are. Keep the original.partexpfiles and
analyze them with this tree'sparticeps-analysisandresearcher-tools. Keep the pilot phones
on RC13 until the pilot ends: the fix is in the offline tools only.
Application update from v1.0.0-rc.13: install the signed RC17 APK over the existing
app without uninstalling or clearing its data; it does not change the application ID.
Local studies and exported data: there is no local-store migration, mandatory reset,
event-source-registry change, or bundle-format change from RC13: the runtime document layout and
the PTCRUN03, PTCPND03, and PTCENG03 frames are unchanged. Existing signed configurations stay
valid and do not need to be re-signed. Reopen the app after updating, complete normal recovery and
access checks, and explicitly Resume the study.
- A study already started keeps its upload chain, re-armed at every process start as before. RC13
armed automatic upload at import and could stage and send setup commits before Start. For a study
still in setup when the phone updates, a leftover RC13 upload job now ends without sending, a
bundle RC13 staged waits and is sent after Start under its original bundle ID, and setup commits
RC13 already delivered stay delivered. When the phone holds an acknowledgement for them, the
decline confirmation says that setup records were already sent and that declining does not take
them back. - The update keeps the existing notification channels and any settings the participant changed. It
does not change a channel's lock-screen setting; the fixed public version applies to each
intervention notification this build posts. - The 5 s window applies to an existing signed configuration that meets its conditions as soon as
the updated app runs it, including a study already under way. From then on, those sensors'
committed_atvalues and the losses described above follow the windowed rules. A configuration
whose automation matches the sensor, or that has any sequence or window state, keeps per-sample
commits. - Participants who consented under RC13 read RC13's consent-screen wording; the new wording appears
on the consent screen and on Study and my data after the update. - Collector code outside this repository that constructs
EmitBatchResult.Acceptedor
CollectorContextmust pass the newrecordedEventsorrequiresPromptCommitsargument before
it compiles against this tree. - Verify and materialize bundles from this build, and from RC13, with this tree's
particeps-analysisandresearcher-tools. For the same reduced inputs, both builds record the
same condition-timer events for every commit in which no timer generation is armed and then
retired or replaced before the commit ends. That includes every commit that reduces a single
input, such as Start, Resume, or a one-event observation. Only a commit that reduces several
inputs can differ: an observation or barrier whose events change awindow_thresholdor
held_fortimer more than once; a pause, completion, or withdrawal whose flushed events re-armed
a timer that the stop then retires; recovery of a study leftACTIVATINGorRUNNING, as after
an app update, process death, or reboot while it runs, whose quality-gap reset re-arms each
condition timer its still-active conditions need and whose safety pause retires it in the same
commit; or a phone clock or time-zone change first seen after the deadline, which does the same
before completing. For such a commit RC13 records every intent, the complete rendering: a
TIMER_SCHEDULEDfor every generation it armed and, when the timer existed before the commit, a
repeatedTIMER_RETIREDfor each retired generation. This build records the net rendering: that
prior timer's retirement once and the resulting timer's schedule once. Protocol v1 admits exactly
these two renderings for each commit and rejects a mixture, so this tree's tools accept RC13
chains, chains from this build, and a chain started under RC13 and continued on this build. The
five-day pilot's automation uses onlystudy_local_windowandstudy_session_activeconditions,
so its RC13 commits can differ only at such a stop, recovery, or late clock change; every RC13
recovery of the running pilot is such a commit, because its resource bindings keep a window timer
armed throughout the study. This build also groups inputs differently: it merges queued callbacks
into one observation and splits a live batch at its first resource-changing event, so for
automation that matches collector events the two builds need not make the same commits from the
same callbacks. - Each RC13 recovery of a running study left a WorkManager wakeup for every condition timer it
re-armed and retired. One whose target passes while the study runs retries until the next pause
under RC13; after the update it ends at its next attempt.
Fresh install: install the signed RC17 APK, then scan a research-team QR code or import
its signed study file. Review the study and data collection, provide consent, complete required
access setup, and explicitly Start.
Verification scope: publication requires this tag's own Android 14 and Android 17 device gates,
Web/receiver/analysis checks, native race gate, release build and signing/packaging verification.
Earlier candidates' passing checks do not override a failure in this run. A complete 120-hour
physical-device endurance study has not been performed; per-device admission and daily readable
exports remain necessary.