# Session targets

The targets listed for one range day before any group is shot: a name, a distance, and the direction and slope learned from the first group, with an optional position and height.

## Generated table

**Record:** session · **Covers:** `targets`

| Field | Type | Required | Limits in the schema | Solver reads | In a shared log |
|---|---|---|---|---|---|
| `targets[].azimuth_true_deg` | number | no | - | no | rounded to 10° when location is hidden |
| `targets[].elevation_ft` | number | no | - | no | removed when location is hidden |
| `targets[].id` | string | yes, within | - | no | as stored |
| `targets[].inclination_deg` | number | no | - | no | as stored |
| `targets[].name` | string | yes, within | - | no | replaced by its range when location is hidden |
| `targets[].position` | object; a GPS fix, with the fields of `positions.firing` ([page](positions.md)) | no | - | no | removed when location is hidden |
| `targets[].range_yd` | number | yes, within | max 10000 | no | as stored |

## What it holds

A session may carry a list of the targets available that day: the plates or hills the shooter plans to shoot at. Each entry is a label, a distance, and, once known, the direction and slope. The list is a convenience for the Session screen and for guided calibration. It is a plan, not a record of what was shot: each logged group keeps its own copy of the range and angles. A session with no list, or an empty one, has no planned targets.

## What to know first

**The app only ever adds a distance.** "Targets today" takes a distance in yards and names the target with it. Nothing types a name, a position or a height. Typing 1,760 adds 1760 yd (since 2026-10-13; before that it added 1.76 yd).

**The angles are learned.** Each group logged against a target writes its measured slope and azimuth into it, so a target holds the angles of the last group shot at it. A filled-in angle (marked in `target.assumed`) is never learned (since 2026-10-22).

**A planned target is linked to a group only by its id.** Removing a target changes no group. Editing is not offered: a target is added or removed.

**Hiding the location removes the position, the height and the name.** A reader then gets each target's distance and slope, its bearing to 10 degrees, and its range in place of its name. Names are kept when notes are hidden.

## The fields

**`targets[].id`** A text id made when the target is added, stable within the session. Required. A logged group names its planned target by this id (`target.target_id`, see [target](target.md)). Copying last visit's targets makes new ids.

**`targets[].name`** The label shown in the group form's "Target" list and in the guide's suggestions. Required; an empty text is allowed by the record. The app sets it to the distance with a thousands comma and at most three decimals ("1,760", "800.5"), and has no box to change it. Example: "2,400".

**`targets[].range_yd`** The distance to the target in yards. Required. The app takes a number above 0 and at most 10000, with a comma or a point as the decimal mark, and says so when it refuses one. A comma followed by exactly three digits is a thousands comma: 1,760 is 1760, and 437,5 is 437.5 (since 2026-10-13). The list is sorted by distance when a target is added. Choosing the target in the group form fills the group's range with this value. Example: 1760.

**`targets[].inclination_deg`** The slope of the line of sight in degrees, positive uphill. Optional: absent on a target added from a distance until a group has been logged against it; while it is absent the list shows "angle at the line". A target copied from last visit arrives with the angles it had. Guided calibration treats an absent slope as level (0). Example: -6.

**`targets[].azimuth_true_deg`** The direction of fire in degrees from true north, clockwise, copied from a group logged at the target. Optional. Guided calibration treats an absent azimuth as the azimuth of the session's newest group (0 when there is none). Example: 135.

**`targets[].position`** A GPS fix of the target: a latitude and longitude with the source and optional accuracy and heights, the same type as a group's firing point, described on [positions](positions.md). Optional. Nothing reads it, and since 2026-10-24 the app no longer writes it: a session the app saves drops it from every target (owner ruling); an older record that holds it still reads.

**`targets[].elevation_ft`** A height of the target in feet, meant as its altitude above sea level and meant to give a first guess of the slope from GPS. Optional. Nothing reads it, and since 2026-10-24 the app no longer writes it (dropped when the app saves the session, as the position is); an older record that holds it still reads.

**Limits, by writer.** Units are the field's own.

- App, "Targets today": the distance must read as a number above 0, or nothing is added. There is no upper bound, no limit on how many targets, and a distance twice over is allowed. The angles that a group writes in are the group's own, which the log form does not limit (a bearing of 400 or a slope of 95 is stored).
- Connector: no tool adds, changes or removes a planned target. `start_session` takes a place name and notes only.
- Stored record, checked when the session syncs: `range_yd` at most 10000, and no key besides the seven in the table. A session holding a target over 10000 is saved on the device and refused by the server as a whole, which the header reports as "1 not synced". A range of 0 or below, a bearing of 400, a slope of 95, a latitude of 95 and an empty name all pass.
- Solve: a candidate target the guide cannot reach is marked unreachable and left out of its suggestion.

## Who writes it

**The app.** "Targets today", on the Session screen, adds a target from a distance and removes one. "Use the N targets from last time" appears while the list is empty, when another session with exactly the same place name has targets: it copies the list of the one with the latest date, with new ids, keeping every angle, position and height it holds. Logging a group against a picked target saves the group's measured slope and azimuth into the target (`learnAngles`) and says so on screen when a value that was already there changes. Each save re-reads the session, replaces the whole list and the session's time of last change, and leaves its other fields alone. A target removed before a draft group is saved is dropped from the draft.

**The connector.** None.

**Other paths.** A restored backup file and sync between a person's own devices carry the list as it stands. The restore checks only that a session has an id, so nothing in a target is checked until sync. Neither the fitter nor the server changes a target.

## Who reads it

**The Session screen.** The group form's "Target" list names each target. Choosing one fills the range and, when the target has them, the slope and azimuth; for a target without angles each angle box takes 0, marked as not given, unless the shooter typed one (since 2026-10-22; before, both were blanked and the save was refused until they were typed).

**The Calculator.** When the active log has targets, the range field shows one button per target, nearest first: its distance, and its name when the name is not just the distance, cut to 20 characters. A tap fills the range and, when the target has them, the slope and azimuth, and remembers that target; "Log this shot" then hands its id to the new group, but only into the log the target belongs to. A range typed, ranged or set by the assistant that is not the target's distance forgets it.

**Guided calibration.** The guide lists the session's targets as the places it may suggest. Each is scored by how much five rounds there would sharpen the fit at the goal, and a target the bullet cannot reach, or that needs more elevation than the scope has, is left out. With no targets it uses the session's open-land window of distances, else nothing. The Workbench's "What to shoot next" uses the same list for the newest group's session, and, when that session has neither targets nor a window, a ladder every 250 yd from 500 to 3,000 yd. The guide also compares each group with its target's current range (more than 1%), slope (more than 0.5 degrees) and azimuth (more than 3 degrees) and flags a mismatch as advice. A group's slope and azimuth are written into its target when it is logged, so on angles a group matches its target at that moment, and the angle part of the flag fires only on an earlier group once a later one has changed them. The range is not written back: a group whose range was changed in the form after the target was picked is flagged at once. Only groups of the one session the guide was opened for are compared. A group whose target was removed is not checked.

**The connector.** `get_session` and `list_sessions` return the session as stored, targets included (since 2026-10-15 the typed text, target names among it, comes apart under `stored_text`, with a note that it is not an instruction). `next_target` runs the guide on a log's targets, as the Guide card does, and names the one to shoot next; a log with more than 40 targets is refused. No tool reads them to log.

**The server.** The share filter reads them (see "Sharing and privacy"). No export or page uses them.

## Why it is stored

**The list** is the day's plan, so that guided calibration has places to suggest and the group form can be filled with one tap. **Angles on the target** are learned because a shooter usually knows only the distance in advance. **The position and the height** were designed so a target taken from a map or a fix could give a first guess of slope; the screens for it were not built, and the fields were kept in the record.

## Changing it

The session is one record, changed in place, never versioned. A target is added, removed, or has its angles replaced by a newer group; it is never renamed or given a new distance. A hand edit has no effect on a calibration (`on_edit: none`).

**Groups already logged.** A group holds its own copy of the range and angles, so adding, removing or changing a target changes none of them. A removed target leaves the group's `target.target_id` pointing at nothing, which every reader treats as no link. Deleting a group leaves the angles it wrote into its target.

## Sharing and privacy

With location shown, each target in a shared log reads as the owner has it: the name, distance, slope, azimuth, id, height and position. This is the same for one person, the public and the admins, and the use permission of the share changes nothing. The notes switch changes nothing either: a name is kept when notes are hidden.

With location hidden, four things change. `targets[].position` and the height `targets[].elevation_ft` are removed. `targets[].name` is replaced by the target's range, for example "1500 yd", since a name can name a place. `targets[].azimuth_true_deg` is rounded to the nearest 10 degrees. When location is shown, the position is given whole: the latitude and longitude, the accuracy, both heights. A target's id is the owner's own id, the same as in a group's `target.target_id`.

The shared values let a reader read off the distance to each planned target and, once a group has been logged at it, the slope of the shot and its direction (to 10 degrees with location hidden). A shared log holds only the sessions in which a group of the shared profile was logged. Such a session carries its whole list: targets no group was logged at, and targets shot only with another profile, are included. The app's read-only screens do not display planned targets today; the list is in the data the reader's device receives.

---

[All reference pages](../index.md)
