# net.anisota.chronicle.walk

> Published by [anisota.net](https://lexicon.garden/identity/did:plc:lcieujcfkv4jx7gehsvok3pr)

## Links

- [View on Lexicon Garden](https://lexicon.garden/lexicon/did:plc:lcieujcfkv4jx7gehsvok3pr/net.anisota.chronicle.walk)
- [Documentation](https://lexicon.garden/lexicon/did:plc:lcieujcfkv4jx7gehsvok3pr/net.anisota.chronicle.walk/docs)
- [Examples](https://lexicon.garden/lexicon/did:plc:lcieujcfkv4jx7gehsvok3pr/net.anisota.chronicle.walk/examples)

## Definitions

### `net.anisota.chronicle.walk`

**Type**: `record`

Walk record. Created with status 'active' at first engagement (first tracked action on the feed), kept current with putRecord, and updated to 'completed' when the walk closes (idle auto-close, light depletion, or a manual wrap-up). rkey is a TID. Walks are the everyday unit and are never numbered; identity is the record key and display identity is the walk's time. Tracks the full lifecycle of a walk including camps, gathered findings, light consumption, and conditions.

**Key**: `tid`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `light` | `ref` → `#walkLight` | No |  |
| `status` | `string` | Yes | Current walk status. A walk is 'active' while open and 'completed' once it closes; there is no 'abandoned' state because a walk never fails. |
| `closedBy` | `string` | No | How the walk closed. Known values: 'user' (manual 'that's enough for now' tap), 'auto' (idle auto-close after the inactivity window), and 'lamp' (light depleted). Free string with no enum so future close reasons need no lexicon change. Unset while the walk is active. |
| `duration` | `integer` | No | Total walk duration in minutes |
| `findings` | `ref` → `#walkFindings` | No |  |
| `createdAt` | `string` (datetime) | Yes | When the record was created |
| `heartbeat` | `ref` → `#walkHeartbeat` | No |  |
| `signature` | `ref` → `#chronicleSignature` | Yes |  |
| `startedAt` | `string` (datetime) | No | When the walk started (first engagement) |
| `updatedAt` | `string` (datetime) | No | When the record was last updated |
| `campsCount` | `integer` | No | Number of camps made during the walk |
| `conditions` | `ref` → `#walkConditions` | No |  |
| `completedAt` | `string` (datetime) | No | When the walk ended. For idle auto-closes this is the last tracked activity, clamped to be no earlier than startedAt. |
| `restDuration` | `integer` | No | Total camp/rest time in minutes |
| `activeDuration` | `integer` | No | Active exploring time in minutes (excludes camp/rest time) |
| `elsewhereActivity` | `ref` → `#walkElsewhereActivity` | No |  |

### `net.anisota.chronicle.walk#walkLight`

**Type**: `object`

Light resource tracking for the walk

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `atEnd` | `integer` | No | Light level when the walk ended (in minutes) |
| `status` | `string` | No | Light status at start of the walk |
| `atStart` | `integer` | No | Light level when the walk started (in minutes) |
| `consumed` | `integer` | No | Total light consumed during the walk |
| `drainRate` | `integer` | No | Effective drain rate as a percentage (80 = 0.8x drain). Multiply by 0.01 to get the float multiplier. |
| `regenerated` | `integer` | No | Total light regenerated during the walk's camps |

### `net.anisota.chronicle.walk#walkFindings`

**Type**: `object`

What was found/gathered during the walk

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `xpGained` | `integer` | No | XP gained from the walk |
| `likesSent` | `integer` | No | Likes sent during the walk |
| `mothsSeen` | `integer` | No | Total moth encounters (caught + escaped) |
| `postsRead` | `integer` | No | Posts read during the walk |
| `repostsSent` | `integer` | No | Reposts sent during the walk |
| `postsCreated` | `integer` | No | Posts created during the walk |
| `itemsCollected` | `integer` | No | Number of items collected |
| `specimensFound` | `integer` | No | Number of specimens successfully caught |

### `net.anisota.chronicle.walk#walkHeartbeat`

**Type**: `object`

Periodic snapshot of walk progress for cross-device sync

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `light` | `ref` → `#heartbeatLight` | No |  |
| `progress` | `array` | No | Per-device progress buckets, one entry per device that has contributed to this walk. In-app totals are the SUM across buckets, not a max-merge: per-device buckets exist deliberately so multi-device in-app activity is never undercounted (an undercount would falsely inflate the elsewhere figure). |
| `updatedAt` | `string` (datetime) | No | When this heartbeat was written |
| `lastActivityAt` | `string` (datetime) | No | Timestamp of the last tracked activity. Drives the idle clock and is max-merged across devices (heartbeats themselves fire on a timer, so idle detection must key on this field, not updatedAt). |

### `net.anisota.chronicle.walk#deviceProgress`

**Type**: `object`

Per-device progress bucket. Buckets are summed across devices (not max-merged) so in-app activity spread over multiple devices is fully counted.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `counts` | `ref` → `#heartbeatProgress` | No |  |
| `deviceId` | `string` | No | Random per-install device id from userScopedStorage. No fingerprinting. |

### `net.anisota.chronicle.walk#heartbeatLight`

**Type**: `object`

Light state at heartbeat time

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `current` | `integer` | No | Current light level |
| `drainRate` | `integer` | No | Current drain rate as percentage |
| `drainActive` | `boolean` | No | Whether drain is actively ticking (true while exploring, false while resting or camping). Walks have no frozen state. |

### `net.anisota.chronicle.walk#walkConditions`

**Type**: `object`

Environmental conditions during the walk

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `biome` | `string` | No | Forest biome type |
| `weather` | `string` | No | Weather condition during the walk |
| `moonPhase` | `string` | No | Moon phase condition (nighttime only) |
| `userState` | `string` | No | User fatigue state at walk start |
| `lightLevel` | `string` | No | Time-of-day light condition at walk start |

### `net.anisota.chronicle.walk#heartbeatProgress`

**Type**: `object`

Session progress counters at heartbeat time (per-device bucket contents)

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `xpGained` | `integer` | No |  |
| `likesSent` | `integer` | No |  |
| `postsRead` | `integer` | No |  |
| `itemsCount` | `integer` | No |  |
| `repostsSent` | `integer` | No |  |
| `postsCreated` | `integer` | No |  |
| `mothsSeenCount` | `integer` | No |  |
| `specimensCount` | `integer` | No |  |

### `net.anisota.chronicle.walk#chronicleSignature`

**Type**: `object`

ES256 cryptographic signature proving record authenticity

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `alg` | `string` | Yes | Signing algorithm (ES256) |
| `kid` | `string` | Yes | Key identifier for the signing key |
| `sig` | `string` | Yes | Base64-encoded ES256 signature |
| `nonce` | `string` | Yes | Unique random nonce to prevent replay |
| `version` | `integer` | Yes | Signature schema version |
| `signedAt` | `string` (datetime) | Yes | When the record was signed |

### `net.anisota.chronicle.walk#walkElsewhereActivity`

**Type**: `object`

Cross-client activity during the walk window that did NOT happen in anisota: the PDS scan of app.bsky posts/likes/reposts in the walk window minus the in-app per-device summed totals, floored at 0 per field. Because likes and reposts carry no client marker this is a subtraction estimate, so the elsewhere panel labels it 'at least'. This block NEVER earns XP or findings; it is data for the panel only. Optional; unset when there was no elsewhere activity or the scan was unavailable.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `likesSent` | `integer` | No | Likes sent elsewhere during the walk window (scan minus in-app; at-least estimate) |
| `repostsSent` | `integer` | No | Reposts sent elsewhere during the walk window (scan minus in-app; at-least estimate) |
| `postsCreated` | `integer` | No | Posts created elsewhere during the walk window (scan minus in-app) |

## Raw Schema

```json
{
  "id": "net.anisota.chronicle.walk",
  "defs": {
    "main": {
      "key": "tid",
      "type": "record",
      "record": {
        "type": "object",
        "required": [
          "status",
          "signature",
          "createdAt"
        ],
        "properties": {
          "light": {
            "ref": "#walkLight",
            "type": "ref"
          },
          "status": {
            "enum": [
              "active",
              "completed"
            ],
            "type": "string",
            "description": "Current walk status. A walk is 'active' while open and 'completed' once it closes; there is no 'abandoned' state because a walk never fails."
          },
          "closedBy": {
            "type": "string",
            "description": "How the walk closed. Known values: 'user' (manual 'that's enough for now' tap), 'auto' (idle auto-close after the inactivity window), and 'lamp' (light depleted). Free string with no enum so future close reasons need no lexicon change. Unset while the walk is active."
          },
          "duration": {
            "type": "integer",
            "minimum": 0,
            "description": "Total walk duration in minutes"
          },
          "findings": {
            "ref": "#walkFindings",
            "type": "ref"
          },
          "createdAt": {
            "type": "string",
            "format": "datetime",
            "description": "When the record was created"
          },
          "heartbeat": {
            "ref": "#walkHeartbeat",
            "type": "ref"
          },
          "signature": {
            "ref": "#chronicleSignature",
            "type": "ref"
          },
          "startedAt": {
            "type": "string",
            "format": "datetime",
            "description": "When the walk started (first engagement)"
          },
          "updatedAt": {
            "type": "string",
            "format": "datetime",
            "description": "When the record was last updated"
          },
          "campsCount": {
            "type": "integer",
            "minimum": 0,
            "description": "Number of camps made during the walk"
          },
          "conditions": {
            "ref": "#walkConditions",
            "type": "ref"
          },
          "completedAt": {
            "type": "string",
            "format": "datetime",
            "description": "When the walk ended. For idle auto-closes this is the last tracked activity, clamped to be no earlier than startedAt."
          },
          "restDuration": {
            "type": "integer",
            "minimum": 0,
            "description": "Total camp/rest time in minutes"
          },
          "activeDuration": {
            "type": "integer",
            "minimum": 0,
            "description": "Active exploring time in minutes (excludes camp/rest time)"
          },
          "elsewhereActivity": {
            "ref": "#walkElsewhereActivity",
            "type": "ref"
          }
        }
      },
      "description": "Walk record. Created with status 'active' at first engagement (first tracked action on the feed), kept current with putRecord, and updated to 'completed' when the walk closes (idle auto-close, light depletion, or a manual wrap-up). rkey is a TID. Walks are the everyday unit and are never numbered; identity is the record key and display identity is the walk's time. Tracks the full lifecycle of a walk including camps, gathered findings, light consumption, and conditions."
    },
    "walkLight": {
      "type": "object",
      "properties": {
        "atEnd": {
          "type": "integer",
          "minimum": 0,
          "description": "Light level when the walk ended (in minutes)"
        },
        "status": {
          "enum": [
            "bright",
            "steady",
            "dim",
            "flickering",
            "dying",
            "fullCharge",
            "charged",
            "low",
            "critical",
            "dead"
          ],
          "type": "string",
          "description": "Light status at start of the walk"
        },
        "atStart": {
          "type": "integer",
          "minimum": 0,
          "description": "Light level when the walk started (in minutes)"
        },
        "consumed": {
          "type": "integer",
          "minimum": 0,
          "description": "Total light consumed during the walk"
        },
        "drainRate": {
          "type": "integer",
          "description": "Effective drain rate as a percentage (80 = 0.8x drain). Multiply by 0.01 to get the float multiplier."
        },
        "regenerated": {
          "type": "integer",
          "minimum": 0,
          "description": "Total light regenerated during the walk's camps"
        }
      },
      "description": "Light resource tracking for the walk"
    },
    "walkFindings": {
      "type": "object",
      "properties": {
        "xpGained": {
          "type": "integer",
          "minimum": 0,
          "description": "XP gained from the walk"
        },
        "likesSent": {
          "type": "integer",
          "minimum": 0,
          "description": "Likes sent during the walk"
        },
        "mothsSeen": {
          "type": "integer",
          "minimum": 0,
          "description": "Total moth encounters (caught + escaped)"
        },
        "postsRead": {
          "type": "integer",
          "minimum": 0,
          "description": "Posts read during the walk"
        },
        "repostsSent": {
          "type": "integer",
          "minimum": 0,
          "description": "Reposts sent during the walk"
        },
        "postsCreated": {
          "type": "integer",
          "minimum": 0,
          "description": "Posts created during the walk"
        },
        "itemsCollected": {
          "type": "integer",
          "minimum": 0,
          "description": "Number of items collected"
        },
        "specimensFound": {
          "type": "integer",
          "minimum": 0,
          "description": "Number of specimens successfully caught"
        }
      },
      "description": "What was found/gathered during the walk"
    },
    "walkHeartbeat": {
      "type": "object",
      "properties": {
        "light": {
          "ref": "#heartbeatLight",
          "type": "ref"
        },
        "progress": {
          "type": "array",
          "items": {
            "ref": "#deviceProgress",
            "type": "ref"
          },
          "description": "Per-device progress buckets, one entry per device that has contributed to this walk. In-app totals are the SUM across buckets, not a max-merge: per-device buckets exist deliberately so multi-device in-app activity is never undercounted (an undercount would falsely inflate the elsewhere figure)."
        },
        "updatedAt": {
          "type": "string",
          "format": "datetime",
          "description": "When this heartbeat was written"
        },
        "lastActivityAt": {
          "type": "string",
          "format": "datetime",
          "description": "Timestamp of the last tracked activity. Drives the idle clock and is max-merged across devices (heartbeats themselves fire on a timer, so idle detection must key on this field, not updatedAt)."
        }
      },
      "description": "Periodic snapshot of walk progress for cross-device sync"
    },
    "deviceProgress": {
      "type": "object",
      "properties": {
        "counts": {
          "ref": "#heartbeatProgress",
          "type": "ref"
        },
        "deviceId": {
          "type": "string",
          "description": "Random per-install device id from userScopedStorage. No fingerprinting."
        }
      },
      "description": "Per-device progress bucket. Buckets are summed across devices (not max-merged) so in-app activity spread over multiple devices is fully counted."
    },
    "heartbeatLight": {
      "type": "object",
      "properties": {
        "current": {
          "type": "integer",
          "description": "Current light level"
        },
        "drainRate": {
          "type": "integer",
          "description": "Current drain rate as percentage"
        },
        "drainActive": {
          "type": "boolean",
          "description": "Whether drain is actively ticking (true while exploring, false while resting or camping). Walks have no frozen state."
        }
      },
      "description": "Light state at heartbeat time"
    },
    "walkConditions": {
      "type": "object",
      "properties": {
        "biome": {
          "type": "string",
          "description": "Forest biome type"
        },
        "weather": {
          "enum": [
            "clear",
            "overcast",
            "rainy",
            "stormy",
            "foggy"
          ],
          "type": "string",
          "description": "Weather condition during the walk"
        },
        "moonPhase": {
          "enum": [
            "full_moon",
            "new_moon"
          ],
          "type": "string",
          "description": "Moon phase condition (nighttime only)"
        },
        "userState": {
          "enum": [
            "well_rested",
            "tired",
            "exhausted",
            "first_expedition"
          ],
          "type": "string",
          "description": "User fatigue state at walk start"
        },
        "lightLevel": {
          "enum": [
            "dawn",
            "morning",
            "midday",
            "afternoon",
            "dusk",
            "night",
            "deepnight"
          ],
          "type": "string",
          "description": "Time-of-day light condition at walk start"
        }
      },
      "description": "Environmental conditions during the walk"
    },
    "heartbeatProgress": {
      "type": "object",
      "properties": {
        "xpGained": {
          "type": "integer",
          "minimum": 0
        },
        "likesSent": {
          "type": "integer",
          "minimum": 0
        },
        "postsRead": {
          "type": "integer",
          "minimum": 0
        },
        "itemsCount": {
          "type": "integer",
          "minimum": 0
        },
        "repostsSent": {
          "type": "integer",
          "minimum": 0
        },
        "postsCreated": {
          "type": "integer",
          "minimum": 0
        },
        "mothsSeenCount": {
          "type": "integer",
          "minimum": 0
        },
        "specimensCount": {
          "type": "integer",
          "minimum": 0
        }
      },
      "description": "Session progress counters at heartbeat time (per-device bucket contents)"
    },
    "chronicleSignature": {
      "type": "object",
      "required": [
        "sig",
        "alg",
        "kid",
        "signedAt",
        "nonce",
        "version"
      ],
      "properties": {
        "alg": {
          "type": "string",
          "description": "Signing algorithm (ES256)"
        },
        "kid": {
          "type": "string",
          "description": "Key identifier for the signing key"
        },
        "sig": {
          "type": "string",
          "description": "Base64-encoded ES256 signature"
        },
        "nonce": {
          "type": "string",
          "description": "Unique random nonce to prevent replay"
        },
        "version": {
          "type": "integer",
          "description": "Signature schema version"
        },
        "signedAt": {
          "type": "string",
          "format": "datetime",
          "description": "When the record was signed"
        }
      },
      "description": "ES256 cryptographic signature proving record authenticity"
    },
    "walkElsewhereActivity": {
      "type": "object",
      "properties": {
        "likesSent": {
          "type": "integer",
          "minimum": 0,
          "description": "Likes sent elsewhere during the walk window (scan minus in-app; at-least estimate)"
        },
        "repostsSent": {
          "type": "integer",
          "minimum": 0,
          "description": "Reposts sent elsewhere during the walk window (scan minus in-app; at-least estimate)"
        },
        "postsCreated": {
          "type": "integer",
          "minimum": 0,
          "description": "Posts created elsewhere during the walk window (scan minus in-app)"
        }
      },
      "description": "Cross-client activity during the walk window that did NOT happen in anisota: the PDS scan of app.bsky posts/likes/reposts in the walk window minus the in-app per-device summed totals, floored at 0 per field. Because likes and reposts carry no client marker this is a subtraction estimate, so the elsewhere panel labels it 'at least'. This block NEVER earns XP or findings; it is data for the panel only. Optional; unset when there was no elsewhere activity or the scan was unavailable."
    }
  },
  "$type": "com.atproto.lexicon.schema",
  "lexicon": 1
}
```
