Docs menu

Docs Under the hood1.0.0

Shared contracts

Some logic runs on the server and in the web app: the server needs it for REST, MCP, the CLI and imports, and the app needs it offline. Each pair is kept in step by one fixture file that a Go test and a TypeScript test both read. Change one side, run both tests; when a fixture is generated, regenerate it with the command in the table, never by hand.

On this page

scripts/contracts.test.mjs (in make repo-check) checks this table: every fixture exists, every file named under Read in Go and Read in TS exists and names its fixture, and every fixture-like file in the repository (*.vectors.json, *_vectors.json, *_fixture.json, *.fixture(s).json) is listed here or below.

Contract Go TypeScript Fixture Read in Go Read in TS Written by
Op schema (fields per entity) internal/sync/ops.go web/src/lib/ops.ts, types.ts internal/sync/testdata/schema_fixture.json internal/sync/schema_fixture_test.go web/src/lib/schemaFixture.test.ts, web/src/lib/entityTables.test.ts hand
Client clock and last-writer-wins (the Go property test's model of the client) internal/sync/convergence_property_test.go (propDevice), sync.Wins web/src/lib/hlc.ts web/src/lib/hlc.vectors.json internal/sync/hlc_vectors_test.go web/src/lib/hlc.test.ts UPDATE_HLC_VECTORS=1 npx vitest run src/lib/hlc.test.ts
Workspaces (partition of synced rows) internal/sync/partition.go web/src/lib/workspaces.ts internal/sync/testdata/workspaces_fixture.json internal/sync/workspaces_test.go, internal/cli/workspaces_fixture_test.go web/src/lib/workspaces.test.ts hand
End-to-end encryption format internal/e2ee, internal/e2ee/crypt (CLI) web/src/lib/crypto.ts, e2eeEnvelope.ts (also bundled by the Obsidian plugin) web/src/lib/e2ee.vectors.json internal/e2ee/vectors_test.go, internal/e2ee/crypt/crypt_test.go web/src/lib/crypto.test.ts go test ./internal/e2ee -run TestVectors -update
Device relay sealing internal/relay web/src/lib/relay.ts web/src/lib/relay.vectors.json internal/relay/vectors_test.go web/src/lib/relay.test.ts go test ./internal/relay -update
Recurrence internal/recurrence web/src/lib/recurrence.ts web/src/lib/recurrence.vectors.json internal/recurrence/recurrence_test.go web/src/lib/recurrence.test.ts hand
Projected occurrences and exceptions internal/recurrence/occurrences.go web/src/lib/occurrences.ts web/src/lib/occurrences.vectors.json internal/recurrence/occurrences_test.go web/src/lib/occurrences.test.ts hand
Acting on one occurrence (records, exceptions, repair) and splitting a series (lineage bounds, counts, This and following) internal/sync/occurrences.go, lineage.go, split.go web/src/lib/recurring.ts, lineage.ts, seriesSplit.ts web/src/lib/series.fixture.json internal/sync/series_fixture_test.go web/src/lib/series.fixture.test.ts hand
Time-zone-fixed due values internal/zoned web/src/lib/dates.ts web/src/lib/zoned.vectors.json internal/zoned/zoned_test.go web/src/lib/dates.test.ts hand
Inherited custom fields internal/sync/fields.go web/src/lib/fieldTree.ts web/src/lib/fields.vectors.json internal/sync/fields_vectors_test.go web/src/lib/fields.test.ts hand
Inbox membership internal/inbox/inbox.go web/src/lib/inbox.ts web/src/lib/inbox.vectors.json internal/inbox/inbox_test.go web/src/lib/inbox.test.ts hand
Time sectors (validity, the viewer's periods, relative names, carry-over, planning list groups) internal/sector web/src/lib/sectors.ts web/src/lib/sectors.vectors.json internal/sector/sector_test.go web/src/lib/sectors.test.ts hand
Default reminders internal/defaultreminder web/src/lib/defaultReminder.ts web/src/lib/defaultReminder.vectors.json internal/defaultreminder/defaultreminder_test.go web/src/lib/defaultReminder.test.ts hand
Rotating assignees (next person, valid lists) internal/sync/rotation.go web/src/lib/rotation.ts web/src/lib/rotation.vectors.json internal/sync/rotation_test.go web/src/lib/rotation.test.ts hand
Built-in view rules internal/inbox/views.go web/src/lib/viewRules.ts web/src/lib/views.vectors.json internal/inbox/views_test.go web/src/lib/viewRules.test.ts hand
Theme documents (format 1: accepted and refused documents, refusal codes) internal/themes web/src/lib/theme/format.ts internal/themes/testdata/ (expected.json) internal/themes/themes_test.go web/src/lib/theme/format.test.ts, web/src/lib/theme/schema.test.ts hand
Theme templates (the starter file with notes: what the server accepts of it, as written and with every commented line set) internal/themes web/src/lib/theme/template.ts internal/themes/testdata/template/ internal/themes/themes_test.go web/src/lib/theme/template.test.ts UPDATE_THEME_TEMPLATES=1 npx vitest run src/lib/theme/template.test.ts
Built-in theme ids and kinds (the server's check of a default theme) internal/themes/builtin.go web/src/lib/theme/builtin/index.ts web/src/lib/theme/builtin/ internal/themes/builtin_test.go web/src/lib/theme/catalogue.test.ts hand
Filter query parser internal/filter web/src/lib/filter/parse.ts docs/filters.md (its examples) internal/filter/filter_test.go web/src/lib/filter/filter.test.ts hand
Obsidian Tasks lines (the vault's line grammar and its line-only checks) internal/vault/line.go, task.go (LineFields) obsidian/src/vault/line.ts internal/vault/testdata/lines.vectors.json internal/vault/linevectors_test.go obsidian/src/vault/line.test.ts UPDATE_LINE_VECTORS=1 go test ./internal/vault -run TestLineVectors
Quick-add parser internal/quickadd web/src/lib/nlp web/src/lib/nlp/quickadd.vectors.json internal/quickadd/vectors_test.go web/src/lib/nlp/vectors.test.ts hand
Quick-add vocabulary internal/quickadd/vocab.go web/src/lib/nlp internal/quickadd/vocab.json internal/quickadd/vocab.go (embedded) web/src/lib/nlp/vocab.test.ts cd web && npm run nlp:vocab
Importer date vocabulary internal/importer/todoistdate.go web/src/lib/nlp internal/importer/nlpvocab.json internal/importer/todoistdate.go (embedded) web/src/lib/nlp/vocabExport.test.ts UPDATE_NLP_VOCAB=1 npx vitest run src/lib/nlp/vocabExport.test.ts
Task origin (recognition, labels, Obsidian note links) internal/origin web/src/lib/taskOrigin.ts internal/origin/testdata/origin_vectors.json internal/origin/vectors_test.go, internal/origin/obsidian_test.go web/src/lib/taskOrigin.test.ts, web/src/lib/shellBridge.fixture.test.ts hand
Note search (matching, ranking, snippets) internal/notesearch web/src/lib/noteSearch.ts internal/notesearch/testdata/vectors.json internal/notesearch/notesearch_test.go web/src/lib/noteSearch.test.ts hand
Reusable lists (title normalization, the matched item, duplicate losers) internal/reuse web/src/lib/reusable.ts web/src/lib/reusable.vectors.json internal/reuse/reuse_test.go web/src/lib/reusable.test.ts hand
Tracked time: the one-running rule internal/timetrack web/src/lib/time.ts internal/timetrack/testdata/rule_fixture.json internal/timetrack/rule_test.go web/src/lib/time.test.ts hand
Tracked time: CSV export internal/timetrack web/src/lib/time.ts internal/timetrack/testdata/csv_fixture.json internal/timetrack/csv_test.go web/src/lib/time.test.ts go test ./internal/timetrack -update-csv
Activity events from ops internal/activity web/src/lib/activity.ts internal/activity/testdata/activity_fixture.json internal/activity/activity_test.go web/src/lib/activity.test.ts OPENTODO_UPDATE_FIXTURE=1 go test ./internal/activity
Automation rule conditions internal/ruleval web/src/lib/rules/match.ts web/src/lib/rules/match.vectors.json internal/ruleval/ruleval_test.go web/src/lib/rules/match.test.ts hand
Automation rule planner internal/rules web/src/lib/rules/plan.ts, web/src/lib/rules/run/actions.ts web/src/lib/rules/plan.fixture.json internal/rules/plan_test.go web/src/lib/rules/plan.test.ts, web/src/lib/rules/run/run.test.ts hand
Routine runs (every step kind, the device runs of encrypted accounts) internal/rules web/src/lib/rules/run/ web/src/lib/rules/usecases.json internal/rules/usecases_test.go web/src/lib/rules/run/usecases.test.ts, web/src/lib/rules/usecases.test.ts hand
Routine steps (shape, types, the D2 reading, the data steps) internal/ruleval/steps*.go web/src/lib/rules/steps.ts, stepData.ts web/src/lib/rules/steps.vectors.json internal/ruleval/steps_test.go web/src/lib/rules/steps.test.ts hand
Set today's focus (a routine step) internal/ruleval/focus.go web/src/lib/focusSet.ts web/src/lib/focusSet.vectors.json internal/ruleval/steps_test.go web/src/lib/focusSet.test.ts hand
Filter evaluation (the server's Find tasks) internal/filter/evaluate.go web/src/lib/filter/evaluate.ts web/src/lib/filter/evaluate.vectors.json internal/filter/evaluate_test.go web/src/lib/filter/evaluate.vectors.test.ts hand
Weekly digest statistics internal/digest/stats.go web/src/lib/stats.ts internal/digest/testdata/stats_fixture.json internal/digest/stats_test.go web/src/lib/stats.test.ts hand
Available tasks (project flows) internal/flow web/src/lib/flow.ts internal/flow/testdata/flow_fixture.json internal/flow/flow_test.go web/src/lib/flow.test.ts hand
Calendar frame occurrences internal/frames web/src/lib/frames.ts web/src/lib/frames.vectors.json internal/frames/frames_test.go web/src/lib/frames.test.ts hand
Today's closed list (slot fill) internal/closedlist web/src/lib/closedList.ts web/src/lib/closedList.vectors.json internal/closedlist/closedlist_test.go web/src/lib/closedList.test.ts hand
Next action (method views) internal/digest web/src/lib/method/rules.ts internal/digest/testdata/next_action_fixture.json internal/digest/nextaction_test.go web/src/lib/method/rules.test.ts hand
On-device assistant tools internal/assistant web/src/lib/assistantDevice web/src/lib/assistantDevice/manifest.json internal/assistant/manifest_test.go web/src/lib/assistantDevice/manifest.ts go test ./internal/assistant -run Device -update
On-device assistant planners internal/assistant, internal/agenttools web/src/lib/assistantDevice/plan.ts web/src/lib/assistantDevice/plan.vectors.json internal/assistant/manifest_test.go web/src/lib/assistantDevice/tools.test.ts go test ./internal/assistant -run Device -update
On-device assistant prompt internal/assistant web/src/lib/assistantDevice/prompt.ts web/src/lib/assistantDevice/prompt.vectors.json internal/assistant/manifest_test.go web/src/lib/assistantDevice/loop.test.ts go test ./internal/assistant -run Device -update
Describe it on the device (routines-describe-it) internal/assistant (routinedraft.go) web/src/lib/rules/describeDevice.ts web/src/lib/assistantDevice/routineDraft.vectors.json internal/assistant/routinedraft_test.go web/src/lib/rules/describe.test.ts go test ./internal/assistant -run Device -update
Provider error reading (Chat Completions) internal/assistant (chatError) web/src/lib/assistantDevice/providers.ts (statusError) web/src/lib/assistantDevice/errors.vectors.json internal/assistant/errors_test.go web/src/lib/assistantDevice/providers.test.ts go test ./internal/assistant -run Device -update

Thin vector sets give false confidence: when you change one of these modules, add the case you fixed or added to its fixture first.

Other fixtures are not shared between the two languages: the agent tool lists (internal/agenttools/testdata/*.golden.json, also read by the Claude plugin check), the MCP tool list, the CLI schema, the importers' golden outputs, web/src/lib/habits.vectors.json, web/src/lib/briefing.fixtures.json, web/src/lib/accent.vectors.json (the custom accent derivation, client-only: the server never renders the accent) and web/src/lib/theme/derive.vectors.json (the theme derivation, client-only for the same reason).

The v1 surface snapshot#

Not a shared fixture but the same idea one level up: internal/httpapi/testdata/v1_surface.json records what every 1.x release keeps (stability and compatibility): the /api/v1/ routes with their policy, success statuses, request and response fields (read from the handlers' types), the error codes with their statuses, the op schema with each field's kind, the export's members and version, the MCP tools with their arguments and read results, and the ot commands, flags, exit codes and --json schemas (from internal/cli/testdata/schema.golden.json). go test ./internal/httpapi -run TestV1SurfaceIsAdditive fails when an entry is gone or changed and when a new one is not recorded; -update adds new entries and never removes or changes one. After v1.0.0 is tagged, make repo-check also compares the file with the one at that tag. TestGeneratedOpSchema refuses to regenerate the op schema fixture without a field it had.

Upgrade fixtures#

internal/store/testdata/upgrade/ holds one fixture per released minor version: data.tar.gz, the data directory that version wrote while the fixed scenario of scripts/release/upgrade-scenario.mjs ran against it, and manifest.json, what the version itself reported (counts read through the API, the op count, a device's cursor, the credentials, the source commit). scripts/release/upgrade-fixture.sh <version> [ref] builds the server from the tag (or ref) in a temporary checkout and writes both. go test ./internal/tenant -run TestUpgradeFromEveryRelease opens each fixture with the current server and compares. A fixture is never regenerated once its version is released; make release-prepare refuses a new minor without its own.

Client recordings#

internal/sync/testdata/v1_clients/ holds the calls of the web app's sync client (web.json), ot (ot.json) and the browser extension (extension.json) against the server, in order, with sessions and tokens replaced by labels (session:1, <token:1>) and passwords by <password>. They come from the contract tests (web/src/lib/*.contract.test.ts) run through the recorder in web/src/test/recorder.ts: make server cli && cd web && npm run test:contract:record. go test ./internal/tenant -run TestV1ClientsReplay replays each file against a fresh current server (mapping the recorded ids and tokens to the replay's own) and fails on another status, a missing field or a field of another type. They are recorded once per major version and never regenerated within it; make repo-check checks that they hold no secret.

Edit this page on GitHub