docs apply STE100 fixes to prose comments

Rewrite doc comments and prose code comments across the app, import, and persistence libraries for Simplified Technical English: split semicolons and multi-clause sentences, convert passive voice to active, remove phrasal verbs and nominalized leads, and replace non-inclusive terms. Documentation-only STE100 compliance pass, no behavior change.

Commit
2a0b23ff1d426236f4fd06f1cbf546bc7ca058e3
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
lib/app/catalog.mli
index 6c0dbdbe..3c07c54a 100644..100644
@@ -1,6 +1,6 @@
1 Removed: (** The routine catalog: the presets the book supports. Shared by every
2 Removed: repository adapter, and not trainee-scoped — a routine is authored, not
3 Removed: owned. *)
1 Added: (** The routine catalog holds the presets the book supports. The catalog is
2 Added: shared by every repository adapter. It is not trainee-scoped, because a
3 Added: routine is authored, not owned. *)
4 4
5 5 val routines : (Repository.routine_id * Prescription.Routine.t) list
6 6 (** Seeded with HD1's Ideal Routine, the one preset the book supports. *)
lib/app/repository.ml
index b9924b46..f8f2a642 100644..100644
@@ -4,7 +4,7 @@
4 4 let routine_id s = s
5 5 let workout_id s = s
6 6
7 Removed: (* Fields are reached through the module type, not from here. *)
7 Added: (* Callers reach these fields through the module type, not from here. *)
8 8 type record = { id : workout_id; workout : Evidence.Workout.t }
9 9 [@@warning "-69"]
10 10
lib/app/repository.mli
index 49e2c9be..b8cb25f9 100644..100644
@@ -1,13 +1,13 @@
1 1 (** Persistence port. A pure module type: no database or web framework.
2 2
3 Removed: Identity lives here rather than in the core, which carries none — a routine,
3 Added: Identity lives here rather than in the core, which carries none. A routine,
4 4 a stored workout, or a trainee needs a name only once something has to
5 5 remember it.
6 6
7 Removed: Every operation is scoped to a {!Trainee.id}. There is no server-wide state:
8 Removed: the active routine and the workout in progress belong to a trainee and are
9 Removed: stored, so nothing is lost across a restart and no two trainees share a
10 Removed: slot. *)
7 Added: Every operation is scoped to a {!Trainee.id}. The port holds no server-wide
8 Added: state. The active routine and the workout in progress belong to a trainee
9 Added: and are stored. Nothing is therefore lost across a restart, and no two
10 Added: trainees share a slot. *)
11 11
12 12 type routine_id = private string
13 13 type workout_id = private string
@@ -102,7 +102,7 @@
102 102 val finish_workout : t -> Trainee.id -> Evidence.Workout.t -> record Lwt.t
103 103 (** Store a finished workout and clear the in-progress slot as one unit. An
104 104 adapter with transactions performs both in a single transaction, so a
105 Removed: workout is never both saved to history and still shown as in progress. *)
105 Added: workout never appears both in history and in progress at once. *)
106 106
107 107 val find : t -> Trainee.id -> workout_id -> record option Lwt.t
108 108
lib/app/service.ml
index 274ee405..5e2e08cc 100644..100644
@@ -7,8 +7,8 @@
7 7
8 8 (* Error types are part of the flat public interface, so they live at the top
9 9 level of the functor. The implementation below is grouped into focused
10 Removed: use-case modules over the shared [t]. The public names are re-exported at
11 Removed: the end, so callers and {!Service.mli} see one flat service. *)
10 Added: use-case modules over the shared [t]. The end of the functor re-exports the
11 Added: public names, so callers and {!Service.mli} see one flat service. *)
12 12
13 13 type register_error = [ `Username of Trainee.username_error | `Username_taken ]
14 14
@@ -82,9 +82,9 @@
82 82 finished later governs, so an imported workout gates rest exactly as a
83 83 native one does. A tie favours the import: it is the more recent evidence
84 84 the trainee entered. When the import governs, HD1's baseline 48h interval
85 Removed: applies, because a bare import carries no cycle position to compute a
86 Removed: tail rest from — the routine's [recovery_after] needs a prescription an
87 Removed: import does not have. Provenance never affects the judgment.
85 Added: applies. A bare import carries no cycle position to compute a tail rest
86 Added: from, because the routine's [recovery_after] needs a prescription that an
87 Added: import lacks. Provenance never affects the judgment.
88 88
89 89 [recommended] is the native recommendation, already computed from the
90 90 routine and log by the caller. *)
@@ -310,8 +310,8 @@
310 310 imported alike. Provenance never changes the verdict: an observation is
311 311 an observation. Merge the two sources, keep only the requested exercise,
312 312 and order oldest-first by [performed_at] so {!Progress.assess} sees a
313 Removed: chronological series. Ties order native before imported — an arbitrary
314 Removed: but fixed rule that keeps the merge deterministic. *)
313 Added: chronological series. Ties order native before imported. That rule is
314 Added: arbitrary but fixed, so it keeps the merge deterministic. *)
315 315 let merged_observations ~log ~records exercise =
316 316 let native =
317 317 Evidence.Log.observations log exercise |> List.map (fun o -> (0, o))
@@ -351,7 +351,7 @@
351 351 workout's prescription, its volume, its clearance, and its extensions.
352 352 A promoted import carries none of that structure: it is a bare series of
353 353 failure observations with no prescription volume or recovery clearance
354 Removed: to measure against. Folding imports in would fabricate habits the record
354 Added: to measure against. An import fold would fabricate habits the record
355 355 cannot support. *)
356 356 let diagnostics t trainee =
357 357 R.log t.repo trainee >|= fun log -> Progress.diagnose log
@@ -458,9 +458,9 @@
458 458
459 459 (* Promote one workout. On [Promoted], write the canonical record first,
460 460 then save the batch that now remembers the promotion. The record upsert
461 Removed: is atomic and carries every observation, so a crash between the two
462 Removed: writes is harmless: re-running promotion is deterministic and replaces
463 Removed: the same record keyed by [(trainee, workout_id)], never duplicating
461 Added: is atomic and carries every observation. A crash between the two writes
462 Added: is therefore harmless. Re-running promotion is deterministic: it replaces
463 Added: the same record keyed by [(trainee, workout_id)] and never duplicates
464 464 observations. [Already_promoted] and [Blocked] leave the record store
465 465 untouched; the batch is saved only when [promote_workout] revised it. *)
466 466 let promote_import_workout t trainee ~batch ~workout =
lib/app/service.mli
index db5b9191..69ccc535 100644..100644
@@ -1,16 +1,17 @@
1 1 (** Application service: orchestrates the core over a {!Repository.S}. The API a
2 Removed: client calls — no HTML or serialization.
2 Added: client calls. It carries no HTML or serialization.
3 3
4 4 Every call is scoped to a {!Trainee.id}. The service holds no mutable state
5 Removed: of its own: the active routine and the workout in progress live in the
6 Removed: repository, so a restart loses nothing and two trainees never share a slot.
5 Added: of its own. The active routine and the workout in progress live in the
6 Added: repository. A restart therefore loses nothing, and two trainees never share
7 Added: a slot.
7 8
8 9 Recovery gating lives here, not in the client. {!Evidence.Workout.start}
9 Removed: demands a {!Recovery.clearance}, and this module is the only thing that
10 Removed: decides how one is obtained: earned by having rested, or taken deliberately
11 Removed: through {!begin_workout}'s [override] acknowledgment. Putting that policy
12 Removed: here means a native client cannot quietly adopt looser rules than the web
13 Removed: one. *)
10 Added: demands a {!Recovery.clearance}, and this module alone decides how a client
11 Added: obtains one. A client earns clearance by having rested, or takes it
12 Added: deliberately through {!begin_workout}'s [override] acknowledgment. This
13 Added: policy lives here so a native client cannot quietly adopt looser rules than
14 Added: the web one. *)
14 15
15 16 module Make (R : Repository.S) (_ : Password_hash.S) : sig
16 17 type t
@@ -21,8 +22,8 @@
21 22 (** {2 Accounts} *)
22 23
23 24 type register_error = [ `Username of Trainee.username_error | `Username_taken ]
24 Removed: (** Why registration was refused: a malformed username or a username already
25 Removed: in use. Passwords carry no policy, so they never refuse registration. *)
25 Added: (** Registration fails for a malformed username or a username already in use.
26 Added: Passwords carry no policy, so they never refuse registration. *)
26 27
27 28 val register :
28 29 t ->
@@ -38,8 +39,8 @@
38 39
39 40 type change_username_error =
40 41 [ `Username of Trainee.username_error | `Username_taken | `Unknown ]
41 Removed: (** Why a rename was refused: a malformed name, a name already in use, or an
42 Removed: unknown account. *)
42 Added: (** A rename fails for a malformed name, a name already in use, or an unknown
43 Added: account. *)
43 44
44 45 val change_username :
45 46 t ->
@@ -50,7 +51,7 @@
50 51 Returns the updated trainee on success. *)
51 52
52 53 type change_password_error = [ `Incorrect_password | `Unknown ]
53 Removed: (** Why a password change was refused: the current password did not verify, or
54 Added: (** A password change fails when the current password did not verify, or when
54 55 the account is unknown. *)
55 56
56 57 val change_password :
@@ -219,7 +220,8 @@
219 220 (** {2 Application feedback} *)
220 221
221 222 type app_feedback_error = [ `Empty_message | `Unknown_feedback ]
222 Removed: (** Why an application feedback operation was refused. *)
223 Added: (** An application feedback operation fails for an empty message or an unknown
224 Added: feedback item. *)
223 225
224 226 val record_app_feedback :
225 227 t ->
@@ -250,18 +252,18 @@
250 252
251 253 (** {2 Workout import}
252 254
253 Removed: Bringing a third-party export into hito is a two-stage flow. A batch is
254 Removed: cleaned — exercises mapped, normal sets acknowledged — then promoted.
255 Removed: Promotion turns a cleaned workout into canonical evidence stored as an
256 Removed: {!Repository.imported_record}, independent of the provisional batch it
257 Removed: came from. Import evidence gates recovery and feeds progression, but never
258 Removed: advances the routine cycle. *)
255 Added: Bringing a third-party export into hito is a two-stage flow. First, a
256 Added: client cleans a batch: it maps exercises and acknowledges normal sets.
257 Added: Then it promotes the batch. Promotion turns a cleaned workout into
258 Added: canonical evidence stored as an {!Repository.imported_record}, independent
259 Added: of the provisional batch it came from. Import evidence gates recovery and
260 Added: feeds progression. It never advances the routine cycle. *)
259 261
260 262 type import_error =
261 263 [ `Unknown_batch | `Duplicate_source | `Import of Workout_import.error ]
262 Removed: (** Why an import operation was refused: no such stored batch, another stored
263 Removed: batch already carries the same source fingerprint, or the cleaning or
264 Removed: promotion step itself failed. *)
264 Added: (** An import operation fails when no such stored batch exists, when another
265 Added: stored batch already carries the same source fingerprint, or when the
266 Added: cleaning or promotion step itself failed. *)
265 267
266 268 val store_import_batch :
267 269 t ->
@@ -318,13 +320,14 @@
318 320 batch:Workout_import.batch_id ->
319 321 workout:Workout_import.workout_id ->
320 322 (Workout_import.workout_result, import_error) result Lwt.t
321 Removed: (** Promote one cleaned workout. On success, one {!Repository.imported_record}
322 Removed: is written carrying every observation of the workout, and the batch is
323 Removed: saved so it remembers the promotion. The record upsert is atomic and keyed
324 Removed: by [(trainee, workout_id)], so re-running promotion after a crash between
325 Removed: the record write and the batch save replaces the same record
326 Removed: deterministically and never duplicates observations. An already-promoted
327 Removed: or blocked workout writes no evidence. *)
323 Added: (** Promote one cleaned workout. On success, this writes one
324 Added: {!Repository.imported_record} carrying every observation of the workout,
325 Added: and saves the batch so it remembers the promotion. The record upsert is
326 Added: atomic and keyed by [(trainee, workout_id)]. A crash can happen between
327 Added: the record write and the batch save. Re-running promotion after such a
328 Added: crash replaces the same record deterministically and never duplicates
329 Added: observations. An already-promoted or blocked workout writes no evidence.
330 Added: *)
328 331
329 332 val promote_import_batch :
330 333 t ->
lib/import/hevy_csv.ml
index 6677edcc..63066e3a 100644..100644
@@ -4,8 +4,8 @@
4 4 failing early on a missing column. Then it walks the data rows once: each
5 5 row becomes a set for some workout, or a batch warning. Rows group into
6 6 workouts by their exact [(title, start_time, end_time)] triple in
7 Removed: first-appearance order, and warnings stay in source order. The source text
8 Removed: is never rebuilt — it flows verbatim into the batch. *)
7 Added: first-appearance order, and warnings stay in source order. The adapter never
8 Added: rebuilds the source text. The text flows verbatim into the batch. *)
9 9
10 10 open Hito_app
11 11 module WI = Workout_import
@@ -73,9 +73,9 @@
73 73
74 74 Hevy writes local wall time as [DD Mon YYYY, HH:MM] with an English month
75 75 abbreviation. The adapter converts to UTC seconds using the caller's fixed
76 Removed: offset: UTC = civil - offset. The calendar date is validated by a round-trip
77 Removed: through [days_from_civil], so an impossible date such as 31 Feb is rejected
78 Removed: rather than normalised. *)
76 Added: offset: UTC = civil - offset. A round-trip through [days_from_civil]
77 Added: validates the calendar date, so the adapter rejects an impossible date such
78 Added: as 31 Feb rather than normalising it. *)
79 79
80 80 let months =
81 81 [|
@@ -317,9 +317,9 @@
317 317 (List.rev !groups_rev, List.rev !warnings_rev)
318 318
319 319 (* Builds one imported workout from a group. [make_workout] can still reject the
320 Removed: times with [Invalid_time]; the classifier already dropped reversed rows, so a
321 Removed: surviving group should not hit it, but the adapter converts any such rejection
322 Removed: into a batch warning rather than raising. *)
320 Added: times with [Invalid_time]. The classifier already dropped reversed rows, so a
321 Added: surviving group should not reach that rejection. The adapter still converts
322 Added: any such rejection into a batch warning rather than raising. *)
323 323 let build_workout ~batch_id ~index (g : group) : (WI.workout, WI.warning) result
324 324 =
325 325 let id =
@@ -343,8 +343,8 @@
343 343 }
344 344
345 345 let parse ~batch_id ~fingerprint ~imported_at ~utc_offset_seconds source =
346 Removed: (* Parse the CSV text. [strip:false] keeps RFC4180 fidelity; quoted commas and
347 Removed: newlines are preserved. A malformed export raises [Csv.Failure]. *)
346 Added: (* Parse the CSV text. [strip:false] keeps RFC4180 fidelity. It preserves
347 Added: quoted commas and newlines. A malformed export raises [Csv.Failure]. *)
348 348 match
349 349 try Ok (Csv.input_all (Csv.of_string ~strip:false source))
350 350 with Csv.Failure (record, field, detail) ->
lib/import/hevy_csv.mli
index 68dba8bb..c58f7c63 100644..100644
@@ -10,9 +10,9 @@
10 10 The adapter enforces the strict native schema. The header must carry all 14
11 11 verified columns, in any order. Rows the adapter cannot promote — warmups,
12 12 non-strength data, unknown set types, malformed fields — become
13 Removed: {!Workout_import.warning} values, not sets. A row is dropped, never guessed
14 Removed: at. Rows group into workouts by the exact [(title, start_time, end_time)]
15 Removed: triple, in first-appearance order. *)
13 Added: {!Workout_import.warning} values, not sets. The adapter drops a row, never
14 Added: guesses at it. Rows group into workouts by the exact
15 Added: [(title, start_time, end_time)] triple, in first-appearance order. *)
16 16
17 17 type error =
18 18 | Malformed_csv of { record : int; field : int; detail : string }
lib/persistence/codec.ml
index 77102ac5..34170ea5 100644..100644
@@ -97,7 +97,7 @@
97 97 | _ -> Error (Malformed "effort arity")
98 98
99 99 (* Decode a stimulus and the slot it filled. The current format prefixes the
100 Removed: slot. A record from before slots were stored omits it, and replays in
100 Added: slot. A record from before hito stored slots omits it, and replays in
101 101 performance order through [add_stimulus] instead. *)
102 102 let decode_stimulus fields =
103 103 let delivery = function
@@ -133,7 +133,7 @@
133 133 (Recovery.duration_to_seconds rested)
134 134 (Recovery.duration_to_seconds recommended)
135 135
136 Removed: (* Recovery exposes only hours/days constructors, so derive an arbitrary
136 Added: (* Recovery exposes only hour and day constructors, so derive an arbitrary
137 137 second-valued duration through elapsed: since=0, now=secs yields exactly
138 138 [secs] seconds. *)
139 139 let seconds_duration secs =
@@ -242,8 +242,8 @@
242 242 Ok { acc with a_stimuli_rev = s :: acc.a_stimuli_rev }
243 243 | fields -> Error (Malformed (String.concat "|" fields))
244 244
245 Removed: (* Parse the whole text into a typed header, or fail. Mandatory fields are
246 Removed: checked here so replay never sees a partial record. *)
245 Added: (* Parse the whole text into a typed header, or fail. This checks the mandatory
246 Added: fields here, so replay never sees a partial record. *)
247 247 let parse text =
248 248 let lines =
249 249 String.split_on_char '\n' text
lib/persistence/codec.mli
index 74e4e362..eadee8e3 100644..100644
@@ -1,15 +1,15 @@
1 1 (** Serialization of performed facts, and their replay through the core.
2 2
3 Removed: The core types are opaque and carry no serializers, by design: the logbook
4 Removed: records, it never interprets. This module is the trust boundary for stored
3 Added: The core types are opaque and carry no serializers, by design. The logbook
4 Added: records. It never interprets. This module is the trust boundary for stored
5 5 data. It captures only what was performed — exercise, load, reps, outcome,
6 6 timestamps, and the basis on which a workout was begun — and rebuilds a
7 7 {!Evidence.Workout.t} by driving the same constructors a live session does.
8 8
9 Removed: A workout's prescription is not stored. It is found again by name in the
10 Removed: routine catalog, so a stored workout stays valid only as long as its routine
11 Removed: and workout names do. That is the intended coupling: prescriptions are
12 Removed: authored, not recorded. *)
9 Added: A workout's prescription is not stored. The decoder finds it again by name
10 Added: in the routine catalog, so a stored workout stays valid only as long as its
11 Added: routine and workout names do. That is the intended coupling: prescriptions
12 Added: are authored, not recorded. *)
13 13
14 14 type error =
15 15 | Malformed of string (** The stored text does not parse. *)
lib/persistence/import_codec.ml
index a46a278e..3ff237f8 100644..100644
@@ -10,10 +10,10 @@
10 10
11 11 (* --- length-prefixed writer ---
12 12
13 Removed: Every field is written as its byte length in decimal, a colon, then its raw
14 Removed: bytes. The length frames the bytes exactly, so a field may hold any byte,
15 Removed: including newlines, commas, and colons, and the reader never has to escape or
16 Removed: guess where a field ends. *)
13 Added: The writer emits every field as its byte length in decimal, a colon, then
14 Added: its raw bytes. The length frames the bytes exactly. A field may therefore
15 Added: hold any byte, including newlines, commas, and colons, and the reader never
16 Added: has to escape or guess where a field ends. *)
17 17
18 18 module Writer = struct
19 19 let create () = Buffer.create 256
@@ -108,8 +108,8 @@
108 108 go [] n
109 109
110 110 (* Resolve an exercise id string into an [Exercise.id]. The id type is
111 Removed: private, so a valid id can only come from the catalog. A stored id the
112 Removed: catalog no longer names is reported as [Unknown]. *)
111 Added: private, so a valid id can only come from the catalog. The reader reports a
112 Added: stored id the catalog no longer names as [Unknown]. *)
113 113 let exercise_id r =
114 114 let raw = field r in
115 115 match Exercise.find_id raw with
@@ -253,10 +253,10 @@
253 253 Writer.list write_warning buf s.warnings;
254 254 Writer.contents buf
255 255
256 Removed: (* Reading a snapshot builds the parser-free {!WI.snapshot}, keeping every
257 Removed: exercise id as a raw string. Restoration through {!WI.restore} resolves the
258 Removed: ids and rebuilds the opaque batch, so an unknown id or an invalid time is
259 Removed: reported once, by the use case. *)
256 Added: (* The reader builds the parser-free {!WI.snapshot}, keeping every exercise id
257 Added: as a raw string. Restoration through {!WI.restore} resolves the ids and
258 Added: rebuilds the opaque batch. The use case therefore reports an unknown id or an
259 Added: invalid time once. *)
260 260 let read_promotion r : WI.promoted_snapshot =
261 261 let title = Reader.string r in
262 262 let started_at = Reader.timestamp r in
lib/persistence/import_codec.mli
index b56ef0a1..5df4b69e 100644..100644
@@ -8,11 +8,12 @@
8 8 evidence.
9 9
10 10 The wire format is a length-prefixed field framing: every field is written
11 Removed: as its byte length, a colon, then its raw bytes. Because the length is
12 Removed: counted in bytes and the bytes are copied verbatim, a field may hold
13 Removed: arbitrary UTF-8, newlines, commas, or colons — the export source, an
14 Removed: acknowledgement, a title, or an unsupported-row reason is preserved exactly.
15 Removed: No {!Marshal}, no {!Obj}: the format is textual and self-describing.
11 Added: as its byte length, a colon, then its raw bytes. The writer counts the
12 Added: length in bytes and copies the bytes verbatim. A field may therefore hold
13 Added: arbitrary UTF-8, newlines, commas, or colons. The format preserves the
14 Added: export source, an acknowledgement, a title, or an unsupported-row reason
15 Added: exactly. No {!Marshal}, no {!Obj}: the format is textual and
16 Added: self-describing.
16 17
17 18 Timestamps reuse {!Timestamp}. Exercise ids resolve through
18 19 {!Hito_core.Exercise.find_id}. A decode failure is a module-specific
lib/persistence/migration_data.ml
index 54955b03..56eb9ed2 100644..100644
@@ -1,14 +1,14 @@
1 Removed: (* The SQL schema migrations, as data. This module holds no logic — only the
2 Removed: ordered list of migrations and the ledger table that records which ones a
1 Added: (* The SQL schema migrations, as data. This module holds no logic. It holds only
2 Added: the ordered list of migrations and the ledger table that records which ones a
3 3 database file already carries. {!Migrations} reads this and applies it.
4 4
5 5 Keeping the SQL here, apart from the apply logic, means a schema change edits
6 6 one place and never touches OCaml control flow.
7 7
8 Removed: The seven incremental migrations that grew this schema were consolidated into
9 Removed: a single initial migration when the deployed database was reset: there was no
10 Removed: file left to migrate, so the drop-and-recreate churn of the old chain served
11 Removed: no purpose. Add the next schema change as version 2. *)
8 Added: Seven incremental migrations first grew this schema. A reset of the deployed
9 Added: database consolidated them into a single initial migration. No file was left
10 Added: to migrate, so the drop-and-recreate churn of the old chain served no
11 Added: purpose. Add the next schema change as version 2. *)
12 12
13 13 type t = { version : int; name : string; statements : string list }
14 14 (** One migration: a version, a human name, and the ordered statements it runs.
@@ -86,8 +86,8 @@
86 86 (* An import batch is provisional: it holds the raw export source and
87 87 the cleaning done so far, encoded as one snapshot blob. A promoted
88 88 record is canonical evidence with an independent lifecycle, so it
89 Removed: carries no foreign key to its batch — deleting the batch must never
90 Removed: cascade to the record. The batch's [imported_at] is a column so
89 Added: carries no foreign key to its batch. Deleting the batch must never
90 Added: cascade to the record. The batch's [imported_at] is a column, so
91 91 listing can order without decoding every blob. A record is keyed by
92 92 (trainee, workout) so a re-promotion replaces it. *)
93 93 {|CREATE TABLE IF NOT EXISTS import_batch (
lib/persistence/migrations.ml
index 50beef12..57b6ad26 100644..100644
@@ -1,7 +1,7 @@
1 Removed: (* Applies the versioned schema migrations. The migrations themselves — the SQL
2 Removed: and the ledger table — live in {!Migration_data}; this module holds only the
3 Removed: logic that runs them: the ledger check that makes applying idempotent, and
4 Removed: the per-migration transaction. *)
1 Added: (* Applies the versioned schema migrations. The migrations themselves live in
2 Added: {!Migration_data}: the SQL and the ledger table. This module holds only the
3 Added: logic that runs them. That logic is the ledger check that makes applying
4 Added: idempotent, and the per-migration transaction. *)
5 5
6 6 type migration = Migration_data.t = {
7 7 version : int;
lib/persistence/migrations.mli
index 8feb395e..76d1f7ae 100644..100644
@@ -1,9 +1,9 @@
1 1 (** Versioned schema migrations for the SQLite store.
2 2
3 3 Each migration is a numbered, named set of statements. A [schema_migrations]
4 Removed: ledger records which versions a database file already carries, so applying
5 Removed: on connect runs only the migrations the file is missing and never repeats
6 Removed: one. Every statement still uses [IF NOT EXISTS], so a database created by an
4 Added: ledger records which versions a database file already carries. On connect,
5 Added: hito runs only the migrations the file is missing and never repeats one.
6 Added: Every statement still uses [IF NOT EXISTS], so a database created by an
7 7 earlier, ledger-less build stays safe.
8 8
9 9 Includes the [dream_session] table {!Dream.sql_sessions} expects. *)
lib/persistence/sqlite_repo.ml
index fdc66fea..feb2c08d 100644..100644
@@ -254,8 +254,9 @@
254 254 | None -> None
255 255
256 256 (* Rename in one transaction: check the name is free for anyone else, then
257 Removed: update. Running both on the same connection keeps the check and the write
258 Removed: from racing another rename. The trainee's own current name is a no-op. *)
257 Added: update. Both statements run on the same connection, so the check and the
258 Added: write cannot race another rename. The trainee's own current name is a no-op.
259 Added: *)
259 260 let update_username t id (username : Trainee.username) =
260 261 let trainee = Trainee.id_to_string id in
261 262 let username_s = Trainee.username_to_string username in
lib/persistence/sqlite_repo.mli
index 8a465a5f..e507d58b 100644..100644
@@ -1,9 +1,9 @@
1 1 (** Durable {!Repository.S} backed by SQLite through Caqti. The production
2 2 store.
3 3
4 Removed: All performed facts are stored through {!Codec}: the adapter never inspects
5 Removed: domain shape, it stores and returns opaque encoded strings. Prescriptions
6 Removed: are not stored — a workout resolves its prescription against the shared
4 Added: All performed facts are stored through {!Codec}. The adapter never inspects
5 Added: domain shape. It stores and returns opaque encoded strings. Prescriptions
6 Added: are not stored. A workout resolves its prescription against the shared
7 7 {!Catalog} on read.
8 8
9 9 Identity is assigned here: trainees and workouts get string ids the adapter
@@ -13,14 +13,14 @@
13 13 include Repository.S
14 14
15 15 exception Corrupt of Codec.error
16 Removed: (** Raised when a stored workout cannot be decoded — a corrupted database or a
17 Removed: catalog that no longer names a stored routine or workout. This is a fault,
18 Removed: not a normal outcome. *)
16 Added: (** Raised when a stored workout cannot be decoded. The cause is a corrupted
17 Added: database, or a catalog that no longer names a stored routine or workout.
18 Added: This is a fault, not a normal outcome. *)
19 19
20 20 exception Import_corrupt of Import_codec.error
21 Removed: (** Raised when a stored import batch or promoted record cannot be decoded — a
22 Removed: corrupted database or a catalog that no longer names a stored exercise. This
23 Removed: is a fault, not a normal outcome. *)
21 Added: (** Raised when a stored import batch or promoted record cannot be decoded. The
22 Added: cause is a corrupted database, or a catalog that no longer names a stored
23 Added: exercise. This is a fault, not a normal outcome. *)
24 24
25 25 val connect : string -> (t, Caqti_error.t) result Lwt.t
26 26 (** [connect uri] opens a connection pool and applies {!Migrations}. [uri] is a