Skip to content

TaskNotes settings record (dev.tasknotes, tasknotes.task rc.6) - #311

Merged
callumalpass merged 4 commits into
mainfrom
agent/app-settings-record
Oct 27, 2026
Merged

callumalpass merged 4 commits into
mainfrom
agent/app-settings-record

Conversation

@callumalpass

@callumalpass callumalpass commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Moves TaskNotes app behaviour into the mdbase.app-settings record at _settings/dev.tasknotes.md: app dev.tasknotes, scope collection, settings_version 1. This implements tasknotes.task rc.6 and spec §12.8.1.

Related:

Order: contracts → model publish → app ships before the plugin upgrades collections.

Unpublished dependencies. This consumes an unpublished model tarball, vendor/tasknotes-model-0.3.0-rc.20.tgz, packed from model PR #25. Swap it for the registry version once rc.20 is published. The embedded pack vendor/mdbase-contracts/tasknotes.task-0.3.0-rc.19.json comes from contracts PR #18. pnpm contracts:sync fails until rc.19 is published at mdbase.dev.

What changed

  • Dependencies: @tasknotes/model now points at file:vendor/tasknotes-model-0.3.0-rc.20.tgz, and the canonical pack is rc.19. The manifest now requires tasknotes.task rc.6, tasknotes.settings 1.0.0 and mdbase.app-settings 1.0.0. The manifest validator requires every contract the pack provides to be listed.

  • Contract versions: the app reads both rc.6 and rc.5 contracts and digests and prefers rc.6. It uses TASKNOTES_LEGACY_CONTRACT_DIGESTS and the newest accepted implementation in a type.

  • Settings store: a new storage-neutral AppSettingsStore, modelled on Reader's store.

    • It reads the record by path. A missing record means defaults.
    • It creates the record on the first save, then patches changed fields against the revision it read.
    • It retries once on a race. A refusal makes it read-only. Fields someone else changed since the form was prepared refuse the save.
    • It has two ports:
      • native SDK (next-app-settings.ts): not_found means no record; forbidden means read-only; conflict with reason revision or path_taken is a race.
      • Connect (mdbase-app-settings.ts): file_not_found means no record; not_authorized and access_denied mean read-only; concurrent_modification and path_taken are a race.
    • It uses the model's tasknotesSettingsDefinition, patch builder, new-record builder and conflict check.
  • Reads: the precedence is record, then the legacy rc.5 binding, then the default, via the resolver's settings option. A missing or late record falls back to the binding, so no value is lost.

  • Writes: the moved fields go to the record and everything else goes to the type. Only values that changed are written, and the type write is skipped when nothing in it changed. The moved fields are nlp triggers, template_*, move_on_archive, archive_folder, auto_stop_time_tracking_on_complete, reset_body_checkboxes_on_recurrence, link_write_format, and status_behavior.<status>.{next_status, exclude_from_cycle, auto_archive, auto_archive_delay_minutes}.

  • No tasknotes-settings type: the moved fields are shown read-only with a one-line explanation and are never written to the binding.

  • Tests: six lean regressions:

    • precedence: record over rc.5 binding over default
    • an rc.5 collection still resolves
    • a save creates the record, then writes only changed fields
    • a conflicting save is refused without writing
    • a settings save sends moved fields to the record and the rest to the type, keeps only changes, and rejects invalid paths and delays
    • a status-behaviour change merges into the record without dropping other statuses (these last two also keep domain coverage above the pnpm verify thresholds)

    Existing tests were updated for rc.6 and rc.19.

Decisions (least-surprising option where the brief was open)

  • Save order: when both are needed, the record is saved first and the type second. If the record is refused (conflict or read-only), the type is not touched.
  • Native writes: they go through the existing confirmed mutation stream and wait for confirmation. Unlike Reader, there is no replay of a pending create.
  • Native reads: they use client.find({path}), so a missing record is null rather than an error.
  • Values matching a default: a moved field is written only when it differs from the current resolved value. A collection that never saves settings never gets a record.
  • rc.5 bindings: values in an rc.5 binding are not copied into the record. They stay readable as the fallback until someone saves that field.

Risks / open questions

  • rc.5 setup: the manifest validator forces rc.6 into the manifest requirements, so SDK readiness puts rc.5 collections into setup and offers the rc.19 pack upgrade. Unedited seeded types upgrade via upgrade_from. A user-edited rc.5 type is preserved, which may leave rc.6 unimplemented and setup unable to finish. The app-level code still reads rc.5. Is the upgrade prompt the intended behaviour, or should readiness accept rc.5?
  • contracts:sync and the vendored model tgz must be refreshed when contracts PR Add opt-in native mdbase change notifications #18 and model PR Show running timers in task rows #25 publish.
  • No LAB or e2e run: this was verified only by unit tests and stand-ins.

Related PRs

Order: contracts → model publish → App ships → plugin upgrades collections.

…ings record

Move templates, archiving, checklist resets, link format, time-tracking
stop and per-status automation to the mdbase.app-settings record at
_settings/dev.tasknotes.md (tasknotes.task rc.6, settings_version 1).

- Vendor @tasknotes/model 0.3.0-rc.20 and the tasknotes.task pack
  0.3.0-rc.19; require rc.6, tasknotes.settings and mdbase.app-settings.
- Read rc.6 and rc.5 collections; prefer rc.6. Reads resolve the record,
  then the legacy rc.5 binding, then the default.
- Add a storage-neutral AppSettingsStore (Reader's semantics) with native
  SDK and Connect ports: missing record is defaults, create on first save,
  patch against the read revision, retry once on a race, refusal is
  read-only, conflicting fields refuse the save.
- Settings saves split moved fields to the record and the rest to the
  type; without a tasknotes-settings type the moved fields are read-only.
… record

The record/type routing and status-behaviour merge had no domain tests,
which dropped domain coverage below the verify thresholds on iOS/Android.
…d tarball

The npm package (published with provenance from tasknotes-model v0.3.0-rc.20)
has the same integrity hash as the vendored tarball it replaces. contracts:sync against the
now-published mdbase.dev tasknotes.task 0.3.0-rc.19 pack produces no change.
@callumalpass
callumalpass marked this pull request as ready for review October 27, 2026 19:52
@callumalpass
callumalpass merged commit c51c086 into main Oct 27, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant