@vrtmrz/livesync-commonlib 0.1.44 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +80 -4
  2. package/dist/API/DirectFileManipulatorV2.d.ts +2 -0
  3. package/dist/API/DirectFileManipulatorV2.js +3 -0
  4. package/dist/API/processSetting.d.ts +1 -0
  5. package/dist/common/configForDoc.d.ts +1 -0
  6. package/dist/common/configForDoc.js +11 -1
  7. package/dist/common/models/db.definition.d.ts +1 -0
  8. package/dist/common/models/setting.const.defaults.js +1 -0
  9. package/dist/common/models/setting.const.preferred.js +1 -3
  10. package/dist/common/models/setting.const.qr.js +1 -0
  11. package/dist/common/models/setting.policy.d.ts +4 -0
  12. package/dist/common/models/setting.policy.js +1 -0
  13. package/dist/common/models/setting.type.d.ts +2 -0
  14. package/dist/common/models/shared.definition.d.ts +20 -0
  15. package/dist/common/models/tweak.definition.d.ts +1 -0
  16. package/dist/common/settingConstants.d.ts +1 -0
  17. package/dist/common/settingConstants.js +6 -2
  18. package/dist/interfaces/FileHandler.d.ts +5 -1
  19. package/dist/interfaces/StorageEventManager.d.ts +5 -0
  20. package/dist/interfaces/StorageEventManager.js +7 -0
  21. package/dist/managers/EntryManager/EntryManagerImpls.js +3 -2
  22. package/dist/managers/StorageEventManager.d.ts +19 -10
  23. package/dist/managers/StorageEventManager.js +259 -189
  24. package/dist/pouchdb/LiveSyncDBFunctions.js +4 -0
  25. package/dist/pouchdb/StreamingFetch.d.ts +5 -1
  26. package/dist/pouchdb/StreamingFetch.js +529 -65
  27. package/dist/replication/LiveSyncAbstractReplicator.d.ts +2 -0
  28. package/dist/replication/LiveSyncAbstractReplicator.js +5 -2
  29. package/dist/replication/RemoteResource.d.ts +23 -2
  30. package/dist/replication/RemoteResource.js +6 -2
  31. package/dist/replication/couchdb/LiveSyncReplicator.d.ts +14 -0
  32. package/dist/replication/couchdb/LiveSyncReplicator.js +171 -3
  33. package/dist/replication/couchdb/fastForward.d.ts +19 -0
  34. package/dist/replication/couchdb/fastForward.js +119 -0
  35. package/dist/replication/couchdb/replicationProgress.d.ts +6 -0
  36. package/dist/replication/couchdb/replicationProgress.js +105 -0
  37. package/dist/replication/index.d.ts +4 -1
  38. package/dist/replication/joinedDevices.d.ts +25 -0
  39. package/dist/replication/joinedDevices.js +123 -0
  40. package/dist/replication/journal/JournalSyncCore.d.ts +2 -1
  41. package/dist/replication/journal/JournalSyncCore.js +11 -1
  42. package/dist/replication/journal/LiveSyncJournalReplicator.d.ts +4 -0
  43. package/dist/replication/journal/LiveSyncJournalReplicator.js +56 -1
  44. package/dist/replication/journal/objectstore/JournalStorageAdapter.d.ts +3 -0
  45. package/dist/replication/journal/objectstore/MinioStorageAdapter.d.ts +2 -1
  46. package/dist/replication/journal/objectstore/MinioStorageAdapter.js +27 -1
  47. package/dist/replication/trystero/P2PRoomSessionOwner.d.ts +1 -1
  48. package/dist/replication/trystero/P2PRoomSessionOwner.js +27 -14
  49. package/dist/replication/trystero/TrysteroReplicator.d.ts +2 -0
  50. package/dist/serviceFeatures/offlineScanner.js +19 -7
  51. package/dist/serviceModules/Rebuilder.js +41 -15
  52. package/dist/serviceModules/ServiceFileAccessBase.js +5 -0
  53. package/dist/serviceModules/ServiceFileHandlerBase.d.ts +8 -5
  54. package/dist/serviceModules/ServiceFileHandlerBase.js +75 -38
  55. package/dist/services/BrowserServices.d.ts +4 -2
  56. package/dist/services/BrowserServices.js +2 -5
  57. package/dist/services/HeadlessServices.d.ts +3 -0
  58. package/dist/services/HeadlessServices.js +2 -5
  59. package/dist/services/ServiceHub.d.ts +4 -4
  60. package/dist/services/base/AppLifecycleService.js +1 -1
  61. package/dist/services/base/ControlService.js +6 -2
  62. package/dist/services/base/FileProcessingService.d.ts +7 -1
  63. package/dist/services/base/FileProcessingService.js +9 -1
  64. package/dist/services/base/IService.d.ts +19 -19
  65. package/dist/services/base/ReplicatorService.d.ts +1 -0
  66. package/dist/services/base/ReplicatorService.remoteResourceResolver.js +1 -1
  67. package/dist/services/base/SettingService.d.ts +8 -18
  68. package/dist/services/base/SettingService.js +28 -46
  69. package/dist/services/implements/base/SvelteDialog.d.ts +2 -2
  70. package/dist/services/implements/base/UIService.d.ts +2 -0
  71. package/dist/services/implements/base/UIService.js +5 -0
  72. package/dist/services/implements/injectable/InjectableServiceHub.d.ts +4 -4
  73. package/dist/services/implements/injectable/InjectableServices.d.ts +2 -2
  74. package/dist/services/implements/injectable/InjectableSettingService.d.ts +0 -3
  75. package/dist/services/implements/injectable/InjectableSettingService.js +0 -11
  76. package/dist/services/lib/HandlerUtils.d.ts +60 -0
  77. package/dist/services/lib/HandlerUtils.js +46 -0
  78. package/dist/string_and_binary/chunks.js +7 -5
  79. package/dist/uiCommands.d.ts +26 -0
  80. package/dist/uiCommands.js +57 -0
  81. package/dist/worker/bgWorker.js +1 -1
  82. package/docs/development.md +15 -3
  83. package/docs/offline-scanner.md +15 -13
  84. package/docs/proven-in-use.md +38 -4
  85. package/docs/releasing.md +20 -13
  86. package/docs/settings-lifecycle.md +39 -5
  87. package/docs/storage-events-and-reflection.md +109 -10
  88. package/package.json +13 -18
  89. package/dist/services/base/ConfigService.d.ts +0 -7
  90. package/dist/services/base/ConfigService.js +0 -6
  91. package/dist/services/implements/browser/ConfigServiceBrowserCompat.d.ts +0 -16
  92. package/dist/services/implements/browser/ConfigServiceBrowserCompat.js +0 -23
@@ -18,6 +18,16 @@ The source tree is larger than the supported package surface. Consumers may impo
18
18
 
19
19
  Files elsewhere under `src` are implementation details unless a focused entry re-exports them or the compatibility inventory names them. Do not use the source layout, TypeScript path aliases, or the presence of generated declarations as evidence of a supported consumer import.
20
20
 
21
+ ## Module filenames
22
+
23
+ For new or renamed TypeScript and JavaScript source modules, start the filename
24
+ with an upper-case letter when the file contains a class with the same name,
25
+ such as `ServiceHub.ts`. Use a lower-case initial when the module primarily
26
+ exports functions, such as `fastForward.ts`. Follow the surrounding filename
27
+ style for the rest of the basename. Corresponding test files use the same
28
+ basename, such as `fastForward.unit.spec.ts`. Retain established names for
29
+ unrelated files and separate conventions for components or other file types.
30
+
21
31
  ## Service composition and state ownership
22
32
 
23
33
  Commonlib uses four related forms of composition. Choose between them by ownership and dependency direction, not merely by whether an implementation has mutable state.
@@ -29,6 +39,8 @@ Commonlib uses four related forms of composition. Choose between them by ownersh
29
39
 
30
40
  When a feature needs state, separate the component which owns that state, its transitions, and its invariants from the code which connects it to application lifecycle events and downstream effects. Give the stateful component narrow collaborators or callbacks rather than the complete Service Hub or application core. Keep application-level handler registration and host-specific presentation in the surrounding serviceFeature. A handler or effect which is part of the state contract, such as querying unresolved errors or reporting their first occurrence, may remain inside the stateful component when its input and output ports are explicit and bounded.
31
41
 
42
+ Settings storage follows the same ownership boundary. Each host selects one exact-key asynchronous `ValueStore & ValueDeleter` at its composition root and supplies it as the setting service's required `deviceValues` dependency. `setting.deviceValues` is that root; `setting.config` wraps it with the existing Vault and database-suffix scope, and the Service Hub `config` property aliases the same view. Commonlib does not discover browser globals or provide a module-global fallback. Keep existing local keys on the exact-key root, preserve configuration key names, and await Value Store operations. See [Settings lifecycle](settings-lifecycle.md#host-storage-for-device-values-and-configuration) for the key mapping and host example.
43
+
32
44
  Existing features illustrate this direction:
33
45
 
34
46
  - `targetFilter.ts` keeps each cache or readiness gate inside the factory which owns the corresponding predicate. `useTargetFilters` composes those predicates and registers them in the required order.
@@ -127,15 +139,15 @@ The compatibility event bridge delegates lifecycle and status requests to the st
127
139
 
128
140
  The physical room, WebRTC peer, and relay-socket boundaries are documented in [P2P transport lifecycle](p2p-transport-lifecycle.md). In particular, normal shutdown leaves the Trystero room without closing raw peer connections directly.
129
141
 
130
- Self-hosted LiveSync remains the principal downstream consumer. A Commonlib change is not ready for publication until the exact packed artefact has also passed the applicable LiveSync type checks, unit and integration tests, application builds, CLI E2E, and focused real-Obsidian E2E.
142
+ Self-hosted LiveSync remains a principal maintained consumer. Commonlib's normal publication gate is its own type, unit, integration, and packed-boundary checks, as described in [the release runbook](releasing.md). Run targeted downstream or real-runtime checks against the exact packed artefact when a named host-specific risk is not established by those owner tests; consumer adoption and downstream test completion are not general release prerequisites.
131
143
 
132
144
  Commonlib owner tests exercise the real implementation behind each focused result and lifecycle contract. A consumer-owned unit test may inject the smallest structural capability it uses and provide its own fake or spy when that current test needs isolation. Do not publish Vitest-specific mocks, expose private service constructors, or refactor a production boundary merely to create possible future test infrastructure. Mock and spy helpers are not a prerequisite for the LiveSync 1.0 package-boundary work; add them only with the consumer test which requires them, or after that release work.
133
145
 
134
- Prefer an object or callback injected at the host composition boundary over replacing an ESM module namespace. If a proposed focused API would require global mutation, import-order coupling, hidden construction, or private nominal types to substitute later, stop and review the production design before stabilising it. Exact-package downstream checks and focused real-runtime E2E remain responsible for proving the real package and host composition together.
146
+ Prefer an object or callback injected at the host composition boundary over replacing an ESM module namespace. If a proposed focused API would require global mutation, import-order coupling, hidden construction, or private nominal types to substitute later, stop and review the production design before stabilising it. Use exact-package downstream checks and focused real-runtime E2E for named host-specific risks which the owner tests cannot establish.
135
147
 
136
148
  Commonlib publishes the typed English messages which it owns and uses them when a host does not provide a translator. A host may inject a translator through `ServiceContext`; the full multilingual catalogue and language-selection state belong to that host and are not part of the Commonlib package.
137
149
 
138
- Keep [the maintained-host evidence](proven-in-use.md) aligned with the downstream source and gates. If a host no longer consumes a focused entry, a test is retired, or a compatibility path receives a reviewed replacement, update the consumer-facing account as part of the same maintenance rather than leaving historical adoption as a current support claim.
150
+ The [maintained-host examples and evidence](proven-in-use.md) are optional context, not a support entitlement or release or adoption gate. When that page is updated, distinguish current use, historical use, Commonlib-owned results, downstream tests, and real-runtime observations, and keep each claim within the evidence.
139
151
 
140
152
  ## Integration tests
141
153
 
@@ -1,7 +1,7 @@
1
1
  ---
2
- date: 2026-09-04
3
- commonlib-version: "0.1.21"
4
- self-hosted-livesync-version: "1.0.24"
2
+ date: 2026-10-25
3
+ commonlib-version: "0.2.0"
4
+ self-hosted-livesync-version: "1.0.40"
5
5
  status: unreleased
6
6
  ---
7
7
 
@@ -31,15 +31,15 @@ The tables below cover only paths which remain after identity validation, the ho
31
31
 
32
32
  The `initialized` marker records that a full-scan invocation has reached its aggregate-result boundary and that later invocations may restore stored storage-event operations. It is not a successful-scan marker: the first invocation records it after an aggregate `false` result, `true`, or an accepted partial result. An exception before the aggregate result is produced does not reach that write.
33
33
 
34
- Storage-event restoration is a bounded start-up recovery step. Restored events bypass their former batch delay, but `restoreState()` waits for their actual file operations to finish and preserves snapshot sentinel ordering before the file collections are compared. Saved events run first because a deletion or rename can carry operation intent which is no longer visible in the current storage listing. An individual restored operation failure is logged and does not prevent the scan from examining the resulting current state. The following full scan then makes the final reconciliation decision.
34
+ Storage-event restoration is a bounded start-up recovery step. Restored events bypass their former batch delay, but `restoreState()` waits for their actual handler outcomes and snapshot ordering before the file collections are compared. Saved events run first because a deletion or rename can carry operation intent which is no longer visible in the current storage listing. An individual restored operation failure remains in the pending snapshot and is logged; it does not prevent the scan from examining current state or independent paths. The following full scan then makes the final reconciliation decision.
35
35
 
36
- This ordering does not mean that storage unconditionally overwrites the replay result. The action matrix remains authoritative: when a replayed operation leaves an ambiguous pair, such as a storage file alongside a deleted database entry, the selected policy may deliberately return `skipped` rather than guess which side is intended. This boundary does not include events which are buffered or received later, and it must not be confused with a host's separate replication-result queue.
36
+ This ordering does not mean that storage unconditionally overwrites the replay result. The action matrix remains authoritative: when a replayed operation leaves an ambiguous pair, such as a storage file alongside a deleted database entry, the selected policy may deliberately return `skipped` rather than guess which side is intended. A failed replay does not become a completed operation; a later scan may still reconcile its current pair. This start-up recovery boundary is separate from later watcher work and a host's replication-result queue.
37
37
 
38
38
  ### Stored event interpretation
39
39
 
40
- The storage-event snapshot contains operations which the previous runtime observed but had not begun or which were still waiting for their batch boundary. It is not a file-content journal and does not discover changes made while the application was stopped. An operation which had already been removed from the queue and entered file processing is also outside the persisted boundary. The following full scan remains responsible for observing the current storage state across all of these gaps.
40
+ The storage-event snapshot retains every accepted operation while it is buffered, waiting for a batch boundary, or in a handler. It remains there until the operation has a durable local result, is proved already represented or safely covered by a successful successor, or ends in a skip explicitly allowed by policy. A `false` handler result or exception leaves the saved operation pending. A failed initial snapshot prevents the handler from starting; a later snapshot failure is reported and cannot be treated as completion. Ordered snapshot writes prevent an older asynchronous save from replacing a newer pending view. The snapshot records operation intent, not file content, and cannot recover bytes or events which the host never persisted or delivered; the full scan continues to observe current storage state.
41
41
 
42
- A restored event is an operation intent which must be revalidated against the current exact storage path before it can cause a database side effect. Its saved file stub, timestamps, and optional editor cache describe the earlier observation; they are not authoritative file content after a restart. Whenever an inclusion is still applicable, the handler reads a fresh stub and content from current storage. Whenever an ordinary deletion's expected absence is contradicted by a current storage item, that deletion is suppressed and the full scan evaluates that current item instead.
42
+ A restored event is an operation intent which must be revalidated against the current exact storage path before it can cause a database side effect. Its saved file stub, timestamps, and optional editor cache describe the earlier observation; they are not authoritative file content after a restart. Whenever an inclusion is still applicable, the handler reads a fresh stub and current content when the operation runs. Whenever an ordinary deletion's expected absence is contradicted by a current storage item, that deletion is suppressed and the full scan evaluates that current item instead.
43
43
 
44
44
  For this check, an exact path means that the canonical path returned by storage has the saved spelling after ordinary separator normalisation. A file returned only through a case-insensitive lookup does not prove the saved inclusion path. A same-ID rename remains the explicit case-change path described below.
45
45
 
@@ -58,7 +58,7 @@ The standard-file revalidation contract is:
58
58
  | Case-only or otherwise same-ID `RENAME` | A current selected file resolves to the renamed canonical path | Update that single document's path and current content without a separate deletion |
59
59
  | Case-only or otherwise same-ID `RENAME` | The current identity does not support the saved rename | Do not replay the rename; let the full scan evaluate the current path |
60
60
 
61
- An inability to inspect the current path is not proof of absence. Revalidation therefore suppresses a destructive half when storage inspection fails, records the failure, and leaves the following scan to report whether reconciliation can proceed. Target-file policy and file-size limits are evaluated from current settings rather than inherited from the saved event.
61
+ An inability to inspect the current path is not proof of absence. Revalidation suppresses a destructive half when storage inspection fails, records the failure, and leaves the operation pending. The full scan can still examine the current state and reports any selected-pair failure under its strict or explicitly partial completion policy. Target-file policy and file-size limits are evaluated from current settings rather than inherited from the saved event.
62
62
 
63
63
  The unreleased rename-exclusion guard retains `renameTarget` when a rename is converted to deletion during target filtering. Unlike an ordinary deletion, this intent can remove a file from synchronisation without removing it from storage. The guard distinguishes deliberate exclusion from transient case-collision rejection, preserves a recreated source, and permits a deliberately excluded same-ID destination without requiring that destination to disappear. Older snapshots without this optional field retain the absence-only rule. See [Storage events and database-to-storage reflection](storage-events-and-reflection.md) for the exact admission conditions and the separate ordinary watcher boundary.
64
64
 
@@ -138,11 +138,13 @@ By default, the complete scan returns `false` when any pair is `failed`, and ret
138
138
 
139
139
  Self-hosted LiveSync enables this policy only for ordinary Obsidian start-up, where one unavailable path must not prevent unaffected files from synchronising. Fast Setup, Fetch, Rebuild, direct scans, and CLI mirror or daemon paths retain the strict default.
140
140
 
141
- ## Equal-time files without a recorded revision
141
+ ## Files in the same timestamp bucket
142
142
 
143
- An equal modification time does not establish that the storage file is unchanged. Before completing such a pair, the scanner asks the file handler to record its current database revision if device-local provenance is absent. The handler reads the actual storage and database content under its document lock and records the revision only when both contain the same bytes, the database has no conflicting leaf, and its current revision agrees with the one observed by the scan. It rechecks the storage snapshot, database revision, and absent provenance before writing the record. This operation writes neither storage content nor a database revision.
143
+ For a `both` pair which selects `NEWER_WINS` (`sync-newer`), equal raw modification times and distinct times within the platform tolerance do not establish that the storage file is unchanged. The scanner first asks the file handler to record provenance for the current database revision only when current storage and one unambiguous live revision have matching content. The handler checks the storage bytes and database revision under the document lock, then revalidates the storage snapshot, current revision, and absent provenance before writing the record. This check writes neither storage content nor a database revision. The existing selection, file-size, and conflict policies still apply, and the `DB_APPLY` action remains unchanged.
144
144
 
145
- An existing provenance record keeps its branch identity even when the file bytes match another revision. An unreadable record, unavailable content, differing bytes, a competing branch, or a changing snapshot leaves provenance untouched. The scan still completes that equal-time pair; a later file event or incoming reflection retains the ordinary conservative conflict policy. If the local database has already advanced past the file's content, the equal-time repair cannot establish its earlier origin.
145
+ The result of `tryRecordUntrackedFileRevision()` is not an edit predicate. Its `false` can mean that provenance already exists, content differs, history conflicts, required content or provenance is unavailable, or state changed during verification. When it returns `false`, the scanner uses the ordinary, non-forced `storeFileToDB` path with bounded storage revalidation; it does not force the Vault file into the current database winner. The file handler retains a known branch when readable provenance identifies it, applies newer database state when the storage bytes match that recorded older revision, and saves a genuine edit on its recorded branch.
146
+
147
+ A valid saved content hash can prove that unchanged bytes still represent a recorded revision whose database content has been pruned. A hash cannot reconstruct ancestry for edited bytes. If the recorded base is pruned and the current bytes do not match its saved hash, the handler may preserve readable storage as an independent branch when it matches no current live leaf; it must not attach those bytes to an arbitrary database winner. This unknown-origin case is distinct from an unavailable comparison: if required storage, provenance, database content, or revalidation cannot be read, the operation remains unresolved and the pair fails for retry. Repeated storage changes are bounded and also remain failed rather than being reported as reconciled.
146
148
 
147
149
  ## Result-state verification
148
150
 
@@ -156,12 +158,12 @@ Focused two-pass coverage starts with a `DB_APPLY` reflection whose file handler
156
158
 
157
159
  Full-scan coverage also verifies that the strict default returns `false`, an ordinary-start-up scan which explicitly continues returns `completed-with-file-failures` while logging the affected path at verbose level, and a later clean scan returns `true`.
158
160
 
159
- The equal-time regression uses real in-memory PouchDB and the file handler. It checks missing and recorded provenance with unchanged files and genuine local edits, then advances the database and reflects the incoming revision. Focused handler tests cover provenance read and write failures, unreadable current content, an existing conflict, and storage or database changes during verification.
161
+ The equal-time regression uses real in-memory PouchDB and the file handler. It checks missing and recorded provenance with unchanged files and genuine local edits at both identical raw timestamps and distinct raw timestamps within one comparison bucket. It verifies that edits are preserved during the scan, then advances the database and checks that an incoming revision retains the local branch. It also verifies that a matching saved hash remains useful after its database revision is pruned, while edited bytes with an unhashed pruned base and no matching live leaf are preserved independently. Focused scanner tests verify that a failed ordinary save returns a failed pair. Focused handler tests cover provenance read and write failures, unavailable recorded-base content, an existing conflict, and storage or database changes during verification.
160
162
 
161
163
  A second two-pass case starts with a `db-only` entry skipped by the size limit. It then removes that limit and verifies that the entry is reflected to storage rather than misclassified as an offline local deletion. Coverage for an existing storage file verifies that a failed `sync-newer` reflection retains the observed storage mtime, and full-scan coverage verifies that the aggregate failure reaches its caller after scanner initialisation completes.
162
164
 
163
165
  Identity coverage verifies early quarantine when no consistent target exists, continued processing when consistent Metadata represents the same logical path, expired-deletion retention, special-namespace routing, actual-ID-first inspection, stale and ambiguous repair rejection, target-first ordering, exact-target retry, source preservation after failure, and last-seen clearing.
164
166
 
165
- Stored-event coverage verifies that current storage content replaces saved observations, a recreated path suppresses a stale deletion, distinct-ID rename halves are admitted independently from current path state, and a same-ID rename never performs a separate deletion. It also verifies that a storage-inspection failure does not become evidence for a destructive operation and that an individual replay failure does not prevent the full scan from running.
167
+ Stored-event coverage verifies that current storage content replaces saved observations, a recreated path suppresses a stale deletion, distinct-ID rename halves are admitted independently from current path state, and a same-ID rename never performs a separate deletion. It also verifies that a storage-inspection failure does not become evidence for a destructive operation, that failed work remains pending, and that an individual replay failure does not prevent the full scan from running.
166
168
 
167
169
  Maintain that interaction boundary when changing file-handler results, last-seen persistence, Fast Setup reflection, or offline deletion detection.
@@ -1,13 +1,43 @@
1
- # Proven in maintained hosts
1
+ # Maintained-host examples and evidence
2
2
 
3
3
  Commonlib is extracted from the implementation shared by [Self-hosted LiveSync](https://github.com/vrtmrz/obsidian-livesync) and its host applications. The package is therefore exercised by a substantial maintained consumer, but it does not yet claim a separate ecosystem of independent applications or a stable end-to-end SDK.
4
4
 
5
5
  The useful evidence is host- and contract-specific. Focused Commonlib tests establish reusable result semantics; Self-hosted LiveSync tests establish how those contracts compose into the Obsidian plug-in, CLI, WebApp, and WebPeer. The Obsidian plug-in and CLI retain the strongest device and long-running synchronisation evidence. The browser applications add focused adapter and runtime tests, browser smoke tests, a cross-application P2P scenario ending at the production CLI, and public Pages smoke validation.
6
6
 
7
- The code paths below identify the maintained Self-hosted LiveSync composition points used to establish this package boundary. The exact reviewed downstream revision and compatibility imports are recorded in [`migration/downstream-imports.json`](migration/downstream-imports.json), which is regenerated by the package-boundary tooling rather than copied into prose links. The integration descriptions must still be reviewed when the maintained downstream changes.
7
+ The code paths below identify maintained Self-hosted LiveSync composition points as examples. The reviewed downstream revision and compatibility imports are recorded in [`migration/downstream-imports.json`](migration/downstream-imports.json), which is regenerated by the package-boundary tooling rather than copied into prose links. These snapshots may become historical as the maintained downstream changes; this page is optional context, not a support entitlement or release or adoption gate.
8
8
 
9
9
  ## Obsidian plug-in
10
10
 
11
+ The unreleased V3 Rabin–Karp correction uses proportional binary thresholds,
12
+ including at values 0 and 1. Regression tests cover natural boundaries, suffix
13
+ reuse, forced caps, explicit minima, EOF tails, and large UTF-8 text. The packed
14
+ old/new CLI matrix verifies 810 file-stage digests over CouchDB, Journal, and
15
+ P2P in both directions. Separate revision tests retain conflict coverage.
16
+ The Linux Obsidian manual-setup workflow verifies value 0, eight 32 KiB binary
17
+ pieces, unchanged Metadata revision and complete local Chunk inventory after
18
+ restart, exact binary reception through Fast Fetch, and a bidirectional note
19
+ round-trip. These checks use the unpublished local candidate with LiveSync
20
+ 1.0.39; they do not establish mobile or Cloudant-service behaviour.
21
+
22
+ Unreleased Fast Fetch tests verify all current conflict leaves, including deleted
23
+ leaves, before advancing the durable stream cursor. CouchDB 3.5.0 integration
24
+ tests exercise revision ancestry retrieval and a fresh-import handoff to the
25
+ ordinary PouchDB checkpoints, followed by a batch-size change and receipt of a
26
+ new revision in LiveSync mode. Unit tests cover unimported local writes, missing
27
+ or replaced checkpoint guards, partial publication, and exclusion of persisted
28
+ resume cursors. A controlled connection delay also verifies that disposing the
29
+ resource during preparation closes the connection when it arrives. Rebuilder
30
+ tests also delay and reject device-local checkpoint
31
+ writes, verifying that handoff, completion, and checkpoint removal wait for
32
+ successful persistence. The handoff requires an empty local database, suspended local
33
+ writers, and a remote database which is not replaced during the operation.
34
+ These package-owned tests do not establish arbitrary-time fast-forwarding,
35
+ On Demand repair, or real Obsidian runtime behaviour.
36
+
37
+ The unreleased joined-device resource and UI-command work adds instance-owned typed UI handlers, legacy-compatible device projections, and device-record removal. Focused unit tests cover missing records, preservation of other devices and unknown fields, self-removal rejection, conflict retries, unsupported writes, verification failures, and handler disposal. CouchDB 3.5.0 integration tests verify that reading or forgetting never creates a missing database, and verify removal and preservation while retaining a fixture which demonstrates that local documents accept stale revisions. Object Storage integration tests against RustFS verify conditional removal and rejection of stale ETags. These checks do not make concurrent CouchDB updates atomic.
38
+
39
+ Before integration with Commonlib 0.2.0, a local packed candidate based on 0.1.44 passed Self-hosted LiveSync 1.0.39 checks, unit tests, and production build. Its focused real-Obsidian 1.12.7 scenario on Linux verifies the settings entry, self-first and locked-out records, current metadata, manual refresh, confirmed removal, and preservation of other records and remote state. Mobile emulation verifies the command-palette entry, scrolling, viewport and safe-area bounds, and touch targets. Self-hosted LiveSync owns the dialogue and advisory version Notices. These results establish the tested build's behaviour; publication and native iOS and Android behaviour remain unverified.
40
+
11
41
  Unreleased remote-inspection tests compare individual and bulk reads against CouchDB 3.5.0. A fixture with 65 Metadata documents and 130 Chunks produces identical integrity reports while reducing HTTP requests from 198 to 8. Encrypted bulk reads preserve current conflicts, exact revision access, missing-document evidence, and unchanged database and Security Seed state. Unit tests cover bounded GET batches, encoded URL limits, per-document failures, cancellation, and explicitly excluded remote coverage. These counts describe the fixture; they do not establish large-Vault elapsed-time improvements.
12
42
 
13
43
  Unreleased [database diagnostics](database-diagnostics.md) tests cover preserved Usage accounting, normal-file identity classification, current conflict branches, byte comparisons, embedded Chunks, missing and unreadable content, cancellation, concurrent changes, and consistent pseudonyms. Real in-memory PouchDB tests verify pagination and conflict revision reads while retaining the original database handle. A CouchDB 3.5.0 integration test verifies GET-only access, encrypted Metadata and Chunk reads with an existing Security Seed, missing-seed failure without initialisation, and unchanged database update sequences and Seed documents. Focused tests also reject ciphertext which remains undecoded after an ID mismatch. Commonlib owns the result contract and compact Integrity Markdown formatter; Self-hosted LiveSync owns the serviceFeature, run-time name choice, dialogue lifecycle, and report copy and download controls. Its real Obsidian 1.12.7 test on Linux verifies cancellation before a run, both name modes, the nine-column Usage report, copy and download payloads, local integrity findings when CouchDB is unavailable, and unchanged settings, database update sequences, and Vault file names. Mobile emulation verifies the choice dialogue's viewport, safe-area, and touch-target layout; native iOS and Android behaviour remains unverified.
@@ -28,10 +58,14 @@ The CouchDB connection fix bypasses browser HTTP caching so encrypted replicatio
28
58
 
29
59
  Unreleased settings tests verify that Object Storage-only credentials, JWT values, and authentication headers reach the host persistence handler only after connection encryption, and that unavailable or failed required encryption preserves the previous persisted settings. The focused `/settings` entry also supplies conditional whole-profile Markdown preparation and import preservation. A clean packed consumer checks the exhaustive property policy and prepared output types with an ES2018 compile fixture. Commonlib owns these result and type contracts; Self-hosted LiveSync owns Markdown file writes, its settings interface, and restoration through a real Obsidian restart.
30
60
 
61
+ The setting-service tests also cover the injected device-value boundary: exact local keys remain unchanged, the existing Vault and database-suffix configuration key layout is preserved, and separately injected stores remain isolated. These are Commonlib-owned asynchronous store contracts. Each host owns selecting its persistent adapter and verifying host storage across restart; this evidence does not claim additional real-runtime coverage.
62
+
31
63
  Unreleased colon-path regression tests establish that Commonlib preserves ordinary file paths through Metadata storage, Chunk retrieval, and storage reflection, with path obfuscation enabled or disabled. They use real PouchDB with injected storage. This result does not establish which filenames a host's file-creation API accepts; that boundary requires host runtime verification.
32
64
 
33
65
  Commonlib's encryption tests verify that an obfuscated Metadata document retains its document ID while its HKDF-encrypted path and file properties round-trip, that V1-obfuscated paths remain readable through the fallback, and that PouchDB replication presents a decoded path to both local storage and change events. The Fast Fetch test verifies decryption before storage and checkpointing. On 26th September 2026, a local packed candidate of this fix passed LiveSync 1.0.31's `npm run check` and unit tests, as well as real Obsidian two-Vault synchronisation, encrypted CLI-to-Obsidian transfer, and encrypted Metadata retrieval through Fast Fetch during manual CouchDB onboarding.
34
66
 
67
+ The EntryManager selection tests distinguish Hidden File Sync documents by their original `i:` prefix. They check that the normal-file allow-list applies to ordinary paths while prefixed hidden documents remain eligible, that the normal-file ignore-list still excludes matching hidden documents, and that hidden-file disablement and the Customisation Sync exceptions retain their behaviour. Hidden File Sync owns its additional target and ignore patterns in Self-hosted LiveSync; storage reflection and native Obsidian behaviour remain downstream verification boundaries.
68
+
35
69
  The unreleased 0.1.30 packed candidate adds encryption of Hidden File Sync and Customisation Sync Metadata when CouchDB, E2EE V2, and Property Encryption are active. Focused Commonlib tests cover the `i:f:`, `ix:f:`, and legacy `ps:f:` representations, legacy and new setting defaults, feature declaration, concurrent version updates, and unknown-feature rejection. With that packed candidate in LiveSync, two real-Obsidian scenarios checked the raw CouchDB Metadata marker, zeroed times and size, empty public Chunk-reference list, and generation 13 feature declaration before restoring the files in a second Vault. The Hidden File Sync scenario also rebuilt the remote and verified the accepted writer's feature declaration and encrypted Metadata before the second Vault fetched them. The CLI-to-Obsidian encrypted note scenario also passed with matching effective settings. A separate real-Obsidian scenario kept continuous CouchDB replication active whilst the remote version document acquired an unknown feature; the local document was observed, the active Replicator retired, another replication was refused, and previously accepted Vault content remained intact. After restarting the Vault, both finite replication and the actual continuous connection attempt were refused by the remote declaration, without a KV feature history. These runs establish the tested local package and host build; they do not validate a published registry artefact or recovery under a future compatible client.
36
70
 
37
71
  Additional real-CouchDB integration tests exercise Fast Fetch admission before local reset, generation 12 acceptance, and generation 13 retrieval with plaintext and encrypted internal Metadata together. A real-Obsidian migration scenario enables the preference through the settings UI without Rebuild, verifies that unchanged Metadata retains its original revision while rewritten entries are encrypted with stable IDs, and checks a second device's mismatch rejection and restoration after alignment. After turning the preference OFF, Fast Fetch still retrieves both representations and their Chunks with matching content; the remote feature declaration remains. These checks use the local packed candidate with LiveSync 1.0.32.
@@ -182,6 +216,6 @@ A new integration should start with the focused entries, not by reproducing Self
182
216
 
183
217
  Use the named consumer paths as integration examples, while retaining the ownership and limitations stated in the [root README](../readme.md), [storage guide](platform-storage.md), [standard-I/O guide](platform-standard-io.md), and [remote configuration profile guide](remote-configurations.md).
184
218
 
185
- ## Maintenance rule
219
+ ## Scope and maintenance
186
220
 
187
- This page records current evidence rather than historical adoption. When a host stops using an entry point, a verification path is retired, or a compatibility import moves to a focused contract, update the description and links instead of retaining a stronger claim than the maintained consumer establishes.
221
+ This optional page preserves examples, current integration evidence, and history. It does not define supported behaviour or create a release or adoption gate; package guarantees are described by the published contracts and release policy. When this page is updated, distinguish current use, historical use, Commonlib-owned tests, downstream tests, and real-runtime observations, and keep each claim within its evidence.
package/docs/releasing.md CHANGED
@@ -2,16 +2,19 @@
2
2
 
3
3
  This document is the developer and maintainer runbook for preparing and publishing `@vrtmrz/livesync-commonlib`. Package-consumer guidance belongs in the root README.
4
4
 
5
- ## Release gate
5
+ ## Commonlib-owned release checks
6
6
 
7
- Run the complete owner gate from a clean checkout:
7
+ Run the normal owner checks from a clean checkout:
8
8
 
9
9
  ```bash
10
10
  npm ci
11
11
  npm run verify:package
12
+ npm run test:integration:managed
12
13
  ```
13
14
 
14
- The gate type-checks Commonlib, runs its complete unit suite, verifies the source boundary, builds the distributable package, installs its exact tarball into a clean consumer, and bundles representative browser and Node entry points. The source `package.json` remains private to prevent publishing the repository root. Only the generated `.package` directory is publishable, and its manifest defaults to public publication on the `next` dist-tag.
15
+ Together these checks type-check Commonlib, run its unit and integration suites, verify the source boundary, build the distributable package, install its exact tarball into a clean consumer, and bundle representative browser and Node entry points. These Commonlib-owned checks are the normal publication criteria. The source `package.json` remains private to prevent publishing the repository root. Only the generated `.package` directory is publishable, and its manifest defaults to public publication on the `next` dist-tag.
16
+
17
+ Downstream or real-runtime checks are appropriate when a named host-specific risk is not established by the owner tests; run those checks against the exact packed artefact and record the risk and result. LiveSync adoption and downstream validation are not general publication prerequisites. The [maintained-host examples and evidence](proven-in-use.md) page is optional context, not a release or update gate.
15
18
 
16
19
  The package builder normalises generated directories to `0755`, ordinary files to `0644`, and declared `bin` targets to `0755` before validation. `test:package` uses a restrictive umask and verifies the packed modes, so local and hosted builds do not differ only because their caller environments use different umasks.
17
20
 
@@ -29,13 +32,17 @@ npm run verify:package
29
32
 
30
33
  Inspect the lockfile diff before committing it. A dependency-only maintenance change must not alter the source package version, and lockfile normalisation must not be described as a package upgrade when resolved versions and integrity values are unchanged. When the workflow selects a newer reviewed npm CLI, update this example in the same change.
31
34
 
32
- Before publication, run the downstream workflow against an exact Self-hosted LiveSync ref which already consumes the package. The workflow installs the tarball produced from the selected Commonlib commit, then runs LiveSync type checks, unit tests, plug-in and application builds, and CLI E2E. Real Obsidian E2E remains local-only and is required when the changed boundary affects actual plug-in composition, storage, UI, or platform behaviour.
35
+ When a named host-specific risk is not established by the Commonlib owner checks, run the relevant downstream workflow against an exact Self-hosted LiveSync ref which consumes the package. It installs the tarball produced from the selected Commonlib commit and runs LiveSync type checks, unit tests, plug-in and application builds, and CLI E2E. Real Obsidian E2E remains local-only and is appropriate when the named risk concerns actual plug-in composition, storage, UI, or platform behaviour. These checks are conditional on the risk; LiveSync adoption is not a general release prerequisite.
36
+
37
+ ## Version compatibility policy
38
+
39
+ The Commonlib 0.2 policy begins with 0.2.0 and does not retroactively change compatibility expectations for published 0.1.x versions. Within 0.2.x, preserve published APIs, types, behaviour, supported persisted data, and exported `compat/*` paths. Intentional breaking changes belong in 0.3.0. Consumers may declare a range such as `^0.2.0`; lockfiles and `npm ci` remain the reproducible install path.
33
40
 
34
41
  ## Preparing a release
35
42
 
36
- Choose the version explicitly. Prefer a `-next.N` prerelease, such as `0.1.32-next.0`, when registry installation must be validated before the stable version. Reserve `-rc.N` for an explicitly chosen release candidate. Package-proof versions are local artefacts and cannot be staged.
43
+ Choose the version explicitly. For the 0.2 line, start at 0.2.0. A stable candidate can use its final version on the `next` dist-tag, then be checked by exact source revision and tarball SHA-256 before the same version is promoted to `latest`. No `-next.N` or `-rc.N` version is required; use a separate prerelease version only when it is useful for a distinct candidate. Package-proof versions are local artefacts and cannot be staged.
37
44
 
38
- Prepare both stable releases and pre-releases on a reviewed pull request based on `main`. A pre-release may be staged from the exact reviewed pull-request branch commit so that the registry artefact can be validated in Self-hosted LiveSync before the Commonlib change is merged. A stable release must be staged only after its exact reviewed release commit is present on `main`.
45
+ Prepare stable releases and any optional prereleases on a reviewed pull request based on `main`. An optional prerelease may be staged from the exact reviewed pull-request branch commit. A stable release must be staged only after its exact reviewed release commit is present on `main`.
39
46
 
40
47
  ```bash
41
48
  npm ci
@@ -44,7 +51,7 @@ npm run verify:package
44
51
  npm publish --dry-run .package --tag next --access public
45
52
  ```
46
53
 
47
- Review `package.json`, `package-lock.json`, the generated manifest, the tarball contents, the test results, and the downstream evidence. Commit only the source manifest and lockfile for the version change; `.package`, `.package-consumer`, and `artifacts` are generated and ignored. Push and open a release pull request only after the usual user checkpoint. Keep a pre-release pull request in draft while its exact registry artefact is validated in Self-hosted LiveSync. Merge and stable publication remain separate later checkpoints.
54
+ Review `package.json`, `package-lock.json`, the generated manifest, the tarball contents, and the Commonlib-owned test results. Add downstream evidence when a named host-specific risk requires it. Commit only the source manifest and lockfile for the version change; `.package`, `.package-consumer`, and `artifacts` are generated and ignored. Push and open a release pull request only after the usual user checkpoint. Keep a pull request in draft while a specifically planned downstream check is in progress; that check is not required when no such risk remains. Merge and publication remain separate later checkpoints.
48
55
 
49
56
  ## Historical initial npm bootstrap
50
57
 
@@ -58,9 +65,9 @@ npm run verify:package
58
65
  npm publish .package --tag next --access public
59
66
  ```
60
67
 
61
- Confirm the authenticated npm account, `@vrtmrz` scope ownership, package name, version, tarball checksum, packed contents, source commit, and target tag immediately before publication. Treat bootstrap publication as a separate user-authorised operation. npm may assign `latest` to the first published version even when `next` is requested; leave the immutable version in place and replace `latest` only after a stable release has passed consumer validation.
68
+ Confirm the authenticated npm account, `@vrtmrz` scope ownership, package name, version, tarball checksum, packed contents, source commit, and target tag immediately before publication. Treat bootstrap publication as a separate user-authorised operation. npm may assign `latest` to the first published version even when `next` is requested; leave the immutable version in place and replace `latest` only after a stable release has passed the Commonlib owner checks and any applicable targeted host checks.
62
69
 
63
- Bootstrap publication was the one historical exception which used direct `npm publish`. Do not repeat that operation for routine releases. Current pre-releases use protected staged publishing and may select an exact reviewed branch commit; stable releases use an exact `main` commit. If validation fails, leave the published version immutable and prepare a new pre-release version.
70
+ Bootstrap publication was the one historical exception which used direct `npm publish`. Do not repeat that operation for routine releases. Optional prereleases use protected staged publishing and may select an exact reviewed branch commit; stable releases use an exact `main` commit. If the owner checks or an applicable targeted host check fails, leave the published version immutable and prepare a new version.
64
71
 
65
72
  ## Trusted staged publishing
66
73
 
@@ -73,9 +80,9 @@ After bootstrap, configure the npm Trusted Publisher for:
73
80
 
74
81
  Protect the GitHub `npm` environment with a required reviewer and permit only `main`. The trusted workflow definition always runs from `main`. A pre-release may package an exact commit from a reviewed draft branch, while a stable release also requires the package source and workflow-triggering commit to be the exact current `main` commit. The workflow confirms that the selected source branch still points to the requested full commit SHA, checks out that SHA, runs `verify:package`, validates the requested version and confirmation text, packs the reviewed output, records its checksum, and passes the same tarball to the protected staging job.
75
82
 
76
- npm's automatic provenance identifies the `main` commit which triggered the trusted workflow. For a pre-release selected from a draft branch, this identifies the trusted workflow source rather than the packaged source commit. Record and verify the separate `source_ref`, exact package SHA, and tarball checksum before approving the staged version. This provenance distinction is accepted for an immutable pre-release used to validate the consumer before merge. A stable release requires the workflow and package source to be the same exact `main` commit.
83
+ npm's automatic provenance identifies the `main` commit which triggered the trusted workflow. For an optional prerelease selected from a draft branch, this identifies the trusted workflow source rather than the packaged source commit. Record and verify the separate `source_ref`, exact package SHA, and tarball checksum before approving the staged version. This provenance distinction is accepted for an immutable prerelease when a consumer check is planned before merge. A stable release requires the workflow and package source to be the same exact `main` commit.
77
84
 
78
- Dispatch a pre-release from its exact reviewed draft branch commit:
85
+ When using an optional pre-release, dispatch it from its exact reviewed draft branch commit:
79
86
 
80
87
  ```bash
81
88
  source_ref=separate-setting-lifecycle
@@ -91,11 +98,11 @@ gh workflow run publish-npm.yml \
91
98
 
92
99
  For a stable release, use `source_ref=main`. The workflow rejects a stable release selected from another branch, rejects a stable release without its trusted workflow commit, and requires the workflow-triggering SHA to equal the packaged `main` SHA. The workflow dispatch itself uses `--ref main` for every release, so an unmerged branch cannot change the trusted publication job or acquire access to the `npm` environment.
93
100
 
94
- The workflow always stages to `next`. Inspect the staged package name, version, access, dist-tag, provenance, checksum, files, selected source branch, and source commit before approving it through npm. Approval and later promotion to `latest` are separate user-authorised operations. Keep the Commonlib and Self-hosted LiveSync consumer pull requests in draft while validating the exact registry pre-release. If validation fails, leave that published version immutable and prepare a new pre-release. After successful consumer validation, merge Commonlib only through its separate maintainer gate. Validate a stable release in Self-hosted LiveSync before promoting it to `latest`.
101
+ The workflow always stages to `next`. Inspect the staged package name, version, access, dist-tag, provenance, checksum, files, selected source branch, and source commit before approving publication to npm. After approval publishes the version, fetch that exact registry version and compare its SHA-256, package name and version, and provenance with the reviewed staged artefact and source revision; confirm that `next` points to that version. Install the fetched registry artefact in a clean consumer and run the relevant package checks before requesting promotion. Stage approval and later promotion to `latest` are separate user-authorised operations. Promote the same version only after those registry checks pass and any applicable targeted host checks are complete. If a check fails, leave the published version immutable and prepare a new version. Keep consumer pull requests in draft only while a specifically planned host check is in progress; consumer adoption is not a precondition for Commonlib merge or release.
95
102
 
96
103
  ## Publishing a GitHub Release
97
104
 
98
- Create a GitHub Release for each stable Commonlib release after the exact registry artefact has passed downstream validation and the version has been promoted to `latest`. Target the exact `main` commit used to build the published package.
105
+ Create a GitHub Release for each stable Commonlib release after the exact registry artefact has passed the Commonlib owner checks and the version has been promoted to `latest`. Complete any applicable targeted host checks before release. Target the exact `main` commit used to build the published package.
99
106
 
100
107
  Protect the GitHub `github-release` environment with a required reviewer and permit only `main`. The manually dispatched workflow first verifies the release with read-only repository access. Only its protected publication job receives `contents: write` access.
101
108
 
@@ -18,7 +18,7 @@ import { createNewVaultSettings } from "@vrtmrz/livesync-commonlib/settings";
18
18
  const initialSettings = createNewVaultSettings();
19
19
  ```
20
20
 
21
- The recommended base currently selects a 50 MB maximum synchronised file size, Rabin–Karp chunk splitting, Plug-in Sync V2, case-insensitive file-name handling, and E2EE V2. Chunk revisions are always derived from their content; the obsolete `doNotUseFixedRevisionForChunks` stored key is retained only for compatibility and is not a recommendation or tweak. The recommended base does not enable synchronisation or encryption on behalf of the user.
21
+ The recommended base currently selects a 50 MB maximum synchronised file size, a `customChunkSize` value of 0, Rabin–Karp chunk splitting, Plug-in Sync V2, case-insensitive file-name handling, and E2EE V2. Chunk revisions are always derived from their content; the obsolete `doNotUseFixedRevisionForChunks` stored key is retained only for compatibility and is not a recommendation or tweak. The recommended base does not enable synchronisation or encryption on behalf of the user.
22
22
 
23
23
  Compared with the conservative stored-setting fallbacks, the current new-Vault object has exactly two semantic differences:
24
24
 
@@ -27,15 +27,23 @@ Compared with the conservative stored-setting fallbacks, the current new-Vault o
27
27
  | `usePluginSyncV2` | `false` | `true` |
28
28
  | `handleFilenameCaseSensitive` | normalised from the legacy absent value to `false` | `false` |
29
29
 
30
- The other headline recommendations are already the conservative fallbacks: `syncMaxSizeInMB` is 50, `chunkSplitterVersion` is `v3-rabin-karp`, and `E2EEAlgorithm` is `v2`. Data Compression, Eden, V1 dynamic iteration, the legacy IndexedDB adapter, Hidden File Sync, and automatic synchronisation remain disabled when their stored values are absent. Explicit stored values take precedence.
30
+ The other headline recommendations are already the conservative fallbacks: `syncMaxSizeInMB` is 50, `customChunkSize` is 0, `chunkSplitterVersion` is `v3-rabin-karp`, and `E2EEAlgorithm` is `v2`. Data Compression, Eden, V1 dynamic iteration, the legacy IndexedDB adapter, Hidden File Sync, and automatic synchronisation remain disabled when their stored values are absent. Explicit stored values take precedence.
31
31
 
32
- Apply a remote-specific preferred object only while configuring that remote. Do not merge it into an existing stored configuration merely because the remote type can be inferred. The self-hosted CouchDB profile uses a chunk-size coefficient of 60, matching the Doctor check for Rabin–Karp databases; Cloudant retains its service-specific value of 0.
32
+ Apply a remote-specific preferred object only while configuring that remote. Do not merge it into an existing stored configuration merely because the remote type can be inferred. Cloudant, self-hosted CouchDB, and Journal Sync setup all use the common `customChunkSize` value of 0. Their unrelated request and bulk settings remain service-specific. The current Doctor regulation no longer recommends changing the value to 60 universally, and existing explicit values remain valid.
33
+
34
+ The new Doctor rule set inherits 1.0.33 and withdraws the Chunk-size rule with
35
+ `customChunkSize: undefined`. Historical regulations keep their original rules.
36
+ This removes the recommendation, rather than deleting a stored setting or
37
+ forcing it to 0. Its consultation version deliberately remains 1.0.33: removing
38
+ a recommendation does not require users to review unchanged recommendations
39
+ again. Existing dismissals remain effective; an explicit forced rescan still
40
+ checks the remaining rules.
33
41
 
34
42
  | Explicitly selected remote | `customChunkSize` | `concurrencyOfReadChunksOnline` | `minimumIntervalOfReadChunksOnline` |
35
43
  | --- | ---: | ---: | ---: |
36
44
  | Cloudant | 0 | 100 | 333 |
37
- | Self-hosted CouchDB | 60 | 30 | 25 |
38
- | Object Storage | 10 | 30 | 25 |
45
+ | Self-hosted CouchDB | 0 | 30 | 25 |
46
+ | Journal Sync (Object Storage) | 0 | 30 | 25 |
39
47
 
40
48
  These are setup-time recommendations, not upgrade migrations. Selecting or detecting a remote does not apply them to an existing configuration unless the person is actively configuring that remote.
41
49
 
@@ -60,6 +68,8 @@ if (prepared.requiresSyncReview) {
60
68
 
61
69
  Explicit stored values take precedence over every fallback. A migration therefore preserves synchronisation switches and other user choices unless a migration step documents a deliberate transformation.
62
70
 
71
+ `batchSaveInLiveSync` is a separate explicit choice for bounded local database batching in LiveSync mode. Both new and existing settings default to `false`; the legacy pair `liveSync: true, batchSave: true` does not enable it. The public setting property is optional for existing host settings shapes, and a missing value remains disabled. Other modes continue to use `batchSave`. Markdown settings and QR data preserve the explicit choice, while older imports without the property do not infer it from `batchSave`. Hosts should preserve both choices during ordinary mode changes and use the shared minimum and maximum delays.
72
+
63
73
  The sleep preferences are included in Setup URI encoding. When an older Setup URI or stored setting does not contain either key, `allowSleepDuringSynchronisation` defaults to `false`, while `allowSleepDuringSynchronisationOnDesktop` defaults to `true`. The maintained Obsidian host therefore retains best-effort screen-awake protection on mobile but allows normal automatic sleep on desktop unless the user selects otherwise.
64
74
 
65
75
  An existing settings store without an explicit `handleFilenameCaseSensitive` value is normalised to `false` and saved. This preserves the effective behaviour of earlier releases, where an absent value followed the case-insensitive branch. An explicit `true` or `false` value is preserved. New Vaults also select case-insensitive handling (`false`).
@@ -72,6 +82,30 @@ Two other values deliberately remain unset until their owning lifecycle supplies
72
82
 
73
83
  The standard Commonlib `SettingService` performs this preparation automatically. Hosts which use that service can inspect `getSettingsMigrationState()` after `loadSettings()` instead of calling the function themselves.
74
84
 
85
+ ## Host storage for device values and configuration
86
+
87
+ The host selects an asynchronous `ValueStore & ValueDeleter` for each application instance and injects it into the setting service as `deviceValues`. Commonlib does not discover `globalThis.localStorage`, create module-global storage, or fall back to another store. `setting.deviceValues` exposes the injected exact-key root. `setting.config` is an asynchronous view over the same root, scoped to the current Vault name and `additionalSuffixOfDatabaseName`; the Service Hub `config` property refers to that same view.
88
+
89
+ For an existing Web Storage layout, pass the selected storage object explicitly and use an empty adapter prefix so the service can address existing raw keys:
90
+
91
+ ```ts
92
+ import { createWebStorageValueStore } from "@vrtmrz/value-store/web-storage";
93
+
94
+ const deviceValues = createWebStorageValueStore(window.localStorage, { prefix: "" });
95
+ // Supply deviceValues as the setting service's required composition dependency.
96
+ ```
97
+
98
+ Use `deviceValues` for exact device-local keys, including existing keys such as `ls-setting-passphrase`. Use `config` for logical configuration keys; the service retains the established Vault and database-suffix key format. For example, the `checkpoint` key for Vault `notes` remains `notes--checkpoint` with an empty suffix and `notes-team-checkpoint` with suffix `team`. Keep the existing key names and await every read, write, and deletion:
99
+
100
+ ```ts
101
+ const passphrase = await setting.deviceValues.getValue("ls-setting-passphrase");
102
+ const checkpoint = await setting.config.getValue("checkpoint");
103
+ await setting.config.setValue("checkpoint", "revision-42");
104
+ await setting.config.delete("checkpoint");
105
+ ```
106
+
107
+ `getValue` returns `null` when a key is absent. Use `getObject` and `setObject` for values stored as JSON; Commonlib does not automatically reinterpret old strings or migrate entries between the exact-key root and the scoped view.
108
+
75
109
  ## Preparing settings for persistence and Markdown
76
110
 
77
111
  The existing `SettingService` provides `changeConfigurationEncryption(store, passphrase?)` for hosts which edit the configuration encryption mode or device-local custom key. Await it before marking those edits as saved. It prepares one wrapper key for connection credentials, the E2EE passphrase, remote profile URIs, and the independent ID key, and serialises the change with ordinary settings saves. It rejects empty keys and unreadable existing protected items. A failed write attempts to restore the previous device key and does not send a success event. The device key and settings document use separate host writes, so this is not a guarantee of rollback after process termination or an uncertain persistence error.
@@ -1,16 +1,18 @@
1
1
  ---
2
- date: 2026-09-05
3
- commonlib-version: "0.1.23"
4
- self-hosted-livesync-version: "1.0.25"
2
+ date: 2026-10-25
3
+ commonlib-version: "0.2.0"
4
+ self-hosted-livesync-version: "1.0.40"
5
+ status: unreleased
5
6
  ---
6
7
 
7
8
  # Storage events and database-to-storage reflection
8
9
 
9
10
  This note separates storage-to-database event handling from
10
- database-to-storage reflection. It describes the Commonlib 0.1.23 guard against
11
- a stale `DELETE` notification logically deleting Metadata while the same file
12
- is still present in storage. The version fields identify the reviewed source
13
- versions.
11
+ database-to-storage reflection. It documents the current guard against a stale
12
+ `DELETE` notification logically deleting Metadata while the same file is still
13
+ present in storage, and the accepted local completion contract for queued file
14
+ operations. The version fields identify the reviewed source versions; these
15
+ changes remain unreleased.
14
16
 
15
17
  ## Processing directions
16
18
 
@@ -46,6 +48,101 @@ and unsynchronised storage content retain their existing reflection rules.
46
48
  The watcher guard does not intercept this path or general explicit database
47
49
  deletion operations.
48
50
 
51
+ ## Pending operations and local completion
52
+
53
+ Once admitted, a file operation remains pending while buffered, waiting for its
54
+ batch boundary, or running in a handler. The manager includes all three states
55
+ in its existing event snapshot. It saves snapshots in order, so an older write
56
+ cannot replace a newer view of pending work. The manager acknowledges an
57
+ ordinary file operation only after its effect is durable locally, current state proves the
58
+ effect is already represented or safely covered by a successful successor, or
59
+ the handler returns a skip explicitly allowed by policy. A handler
60
+ failure or exception leaves the saved operation pending. A failed initial
61
+ snapshot prevents the handler from starting; a later snapshot failure is
62
+ reported and cannot be treated as completion. Completion of an older operation
63
+ cannot remove a newer operation for the same path.
64
+
65
+ The event snapshot records operation intent, not authoritative current file
66
+ content. Admission does not read a file body merely to wait. When a `CREATE` or
67
+ `CHANGED` operation becomes eligible, the handler rechecks the current path and
68
+ obtains its current content for processing. Restored events are revalidated
69
+ against current storage and selection before they can affect the database. The
70
+ saved stub and any optional cache are not used as a historical file version.
71
+ The current-storage deletion checks and rename-exclusion rules below continue to
72
+ apply.
73
+
74
+ A restored deletion whose database entry is already absent or deleted completes
75
+ without another revision. A restored rename whose source deletion is already
76
+ represented reconciles the current target through the ordinary non-forced
77
+ handler. The same applies to a case-only rename when Metadata already records
78
+ the target spelling. Replaying either completed operation preserves later local
79
+ edits. If the source is still live and a target already exists, the existing
80
+ collision refusal remains in force: the snapshot does not prove that this
81
+ rename created that target, so the unresolved operation remains pending.
82
+
83
+ `commitPendingFileEvents()` requests immediate processing of the finite set of
84
+ operations accepted before the call, including work which is buffered,
85
+ waiting, or already in a handler. It waits for each actual handler result and
86
+ for the ordered snapshot update which records that result; releasing a timer is
87
+ not completion. Later events do not extend that captured boundary. A newer
88
+ coalesced operation satisfies an earlier one only when its successful result
89
+ covers the earlier intent. A failed handler or snapshot update is reported to
90
+ the caller and leaves unresolved work in the snapshot. This boundary covers
91
+ local completion under the selected handler contract only; it does not prove replication or receipt by another
92
+ Vault.
93
+
94
+ A `DELETE` or `RENAME` ordering sentinel waits for its predecessors only. It
95
+ must not wait for itself or events which follow it in the dispatch queue. Batch
96
+ timers make work eligible to run; they do not perform it. The quiet period
97
+ follows the latest eligible content update, while the maximum intentional delay
98
+ is measured from the first uncommitted update, starting when the queue call is
99
+ accepted and before asynchronous filtering or the touched-file check. Waiting
100
+ timers do not occupy a processing slot, and time spent waiting for a slot does
101
+ not restart the maximum delay. Once due, the operation acquires a slot and reads
102
+ current content for processing.
103
+
104
+ ## Optional file handlers
105
+
106
+ `INTERNAL` events and files marked `isInternal` use their owning feature's
107
+ contract. Hosts can register `processOptionalFileEventWithResult` handlers with
108
+ `firstHandled`. `HandlerOutcome<TValue = void, TVetoReason = void>` carries a
109
+ success value and a veto reason independently; a non-void payload is required
110
+ in its branch. Dispatch follows priority and registration order:
111
+
112
+ | Outcome | Dispatch and completion |
113
+ | --- | --- |
114
+ | `not-applicable` | Try the next provider. If none applies, complete the policy-approved skip. |
115
+ | `completed` | Stop and complete the event after the provider's actual save or verified no-op. |
116
+ | `veto` | Stop and retain the event with a reported refusal reason. |
117
+ | Exception or rejection | Propagate the original error and retain the event. |
118
+
119
+ A completed result stops dispatch immediately; a later provider cannot veto
120
+ it. Empty registration returns `not-applicable`. False or zero payloads remain
121
+ valid values. Providers must resolve their promise only after the operation
122
+ finishes. Snapshot acknowledgement still follows the handler result, and a
123
+ snapshot failure keeps the event recoverable.
124
+
125
+ The optional `whenFlushRequested` promise resolves when a finite completion
126
+ boundary includes this event. A provider which debounces saves uses it to
127
+ release that event's captured delay. It must wait for the corresponding save,
128
+ settle all notifications coalesced into that save, and keep later groups outside
129
+ the captured boundary. This signal is runtime state; it does not change the
130
+ persisted snapshot format.
131
+
132
+ Migrate all optional providers in a host together. Once the first typed provider
133
+ is registered, the manager uses the typed chain exclusively, even when every
134
+ provider returns `not-applicable`. A host without a registered typed provider
135
+ retains `processOptionalFileEvent` as a legacy notification contract. Its
136
+ boolean does not confirm persistence: a resolved notification completes only
137
+ that dispatch, while a rejection which reaches the manager is still retained.
138
+ The existing boolean aggregators and ordinary file contract are unchanged.
139
+
140
+ During start-up replay and settings or unload flushes, providers use actual
141
+ database readiness and initialise their required local state before saving.
142
+ They must not wait for an application-ready or resume event which occurs only
143
+ after that same flush. An unavailable prerequisite returns a veto; the manager
144
+ does not introduce an automatic retry loop for a failed operation.
145
+
49
146
  ## Event intent and current storage state
50
147
 
51
148
  | Event intent | Current storage observation | Admitted operation |
@@ -92,9 +189,11 @@ incoming logical deletion can still remove unchanged storage content.
92
189
 
93
190
  These tests do not establish the native watcher event sequence reported in
94
191
  Issue 1168. Real-runtime verification belongs to the host and must distinguish
95
- observed events from inferred causes. This change does not promise path-case
96
- convergence or full folder-rename support. It does not rewrite the queue,
97
- introduce a retry subsystem, or change the database schema.
192
+ observed events from inferred causes. The watcher guard does not promise
193
+ path-case convergence or full folder-rename support. Focused manager tests cover
194
+ finite completion boundaries, ordered snapshots, retained failures, replay, and
195
+ batch timing. This work adds no second journal, unbounded retry loop, or
196
+ database-schema change.
98
197
 
99
198
  The maintained host's real-runtime comparison is recorded in
100
199
  [Proven in maintained hosts](proven-in-use.md#obsidian-plug-in), separately