@mnstry/atelier 0.2.0-alpha.11 → 0.2.0-alpha.12
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.
- package/CHANGELOG.md +344 -0
- package/README.md +10 -6
- package/contracts/atelier-obsidian-generation-manifest.v2.schema.json +372 -0
- package/contracts/compat-baseline.json +1 -1
- package/docs/assurance-controls.md +3 -2
- package/docs/install.md +3 -3
- package/docs/local-services.md +62 -11
- package/docs/obsidian-contract.md +669 -35
- package/docs/obsidian-plugin.md +585 -0
- package/docs/obsidian.md +233 -50
- package/docs/release-engineering.md +7 -1
- package/docs/upgrade.md +23 -3
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/case-only-collision.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/flat-note-path.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/hidden-folder.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/identity-in-the-body.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/link-breaking-name.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/missing-identity-region.v2.json +156 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/missing-layout-version.v2.json +159 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/note-named-like-a-folder.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/unknown-region-kind.v2.json +160 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/v1-contract-version.v2.json +161 -0
- package/fixtures/obsidian/contracts/generation-manifest-v2/valid/three-notes.v2.json +160 -0
- package/fixtures/obsidian/edits/workspace.json +1 -1
- package/fixtures/obsidian/materialization/expected-full.json +21 -21
- package/fixtures/obsidian/materialization/expected-full.layout-1.json +99 -0
- package/fixtures/obsidian/materialization/expected-scoped.json +7 -7
- package/fixtures/obsidian/materialization/expected-scoped.layout-1.json +39 -0
- package/package.json +7 -3
- package/plugins/obsidian/main.js +444 -0
- package/plugins/obsidian/manifest.json +9 -0
- package/plugins/obsidian/styles.css +22 -0
- package/src/commands/obsidian.mjs +89 -10
- package/src/contracts/corpus.mjs +6 -0
- package/src/egress/forbidden-egress.mjs +2 -0
- package/src/project/private-state.mjs +10 -1
- package/src/projection/obsidian/contracts.mjs +224 -18
- package/src/projection/obsidian/edits/apply.mjs +12 -7
- package/src/projection/obsidian/edits/regions.mjs +137 -42
- package/src/projection/obsidian/materialize/index.mjs +6 -3
- package/src/projection/obsidian/materialize/path-registry.mjs +443 -57
- package/src/projection/obsidian/materialize/prepare-view.mjs +382 -131
- package/src/projection/obsidian/materialize/redaction.mjs +327 -0
- package/src/projection/obsidian/materialize/settings.mjs +113 -34
- package/src/projection/obsidian/plugin-bridge/bundle.mjs +50 -0
- package/src/projection/obsidian/plugin-bridge/channel.mjs +174 -0
- package/src/projection/obsidian/plugin-bridge/index.mjs +10 -0
- package/src/projection/obsidian/proposals/observation.mjs +10 -5
- package/src/projection/obsidian/publication/bridge-script.mjs +11 -5
- package/src/projection/obsidian/publication/index.mjs +2 -1
- package/src/projection/obsidian/publication/publisher.mjs +173 -26
- package/src/projection/obsidian/publication/transport.mjs +63 -8
- package/src/projection/obsidian/publication/vault-list.mjs +199 -0
- package/src/projection/obsidian/recovery/restart.mjs +3 -1
- package/src/projection/obsidian/recovery/store.mjs +11 -4
- package/src/projection/obsidian/selection-ui/focus.mjs +14 -7
- package/src/projection/obsidian/selection-ui/selection-state.mjs +2 -2
- package/src/projection/obsidian/selection-ui/selection.mjs +8 -3
- package/src/runtime/obsidian/app-capability.mjs +104 -12
- package/src/runtime/obsidian/app-production-seams.mjs +126 -39
- package/src/runtime/obsidian/app-registration.mjs +166 -0
- package/src/runtime/obsidian/engine.mjs +127 -11
- package/src/runtime/obsidian/extension-points.mjs +1 -1
- package/src/runtime/obsidian/index.mjs +5 -1
- package/src/runtime/obsidian/launch-plan.mjs +88 -0
- package/src/runtime/obsidian/lifecycle.mjs +105 -14
- package/src/runtime/obsidian/opening.mjs +227 -55
- package/src/runtime/obsidian/pending-edits.mjs +10 -0
- package/src/runtime/obsidian/plugin-channel.mjs +281 -0
- package/src/runtime/obsidian/plugin-choice.mjs +170 -0
- package/src/runtime/obsidian/plugin-drift.mjs +76 -0
- package/src/runtime/obsidian/plugin-presence.mjs +48 -0
- package/src/runtime/obsidian/service-main.mjs +18 -5
- package/src/runtime/obsidian/service-record.mjs +36 -0
- package/src/runtime/obsidian/service-server.mjs +50 -12
- package/src/runtime/obsidian/service.mjs +100 -6
- package/src/runtime/obsidian/state-store.mjs +13 -1
- package/src/server/local-sidecar.mjs +24 -5
- package/src/server/server.mjs +18 -5
- package/src/upgrade/upgrade.mjs +1 -1
- package/templates/external-project-workspace/pack-lifecycle.json +1 -1
package/docs/obsidian.md
CHANGED
|
@@ -72,13 +72,18 @@ A focus keeps the full vault on disk and filters the native Graph view. The
|
|
|
72
72
|
query is
|
|
73
73
|
|
|
74
74
|
```
|
|
75
|
-
path
|
|
75
|
+
path:/^<note path>$/ OR path:/^<note path>$/ ...
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
in the order of the selected set (canonical-id order).
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
in the order of the selected set (canonical-id order). Each term is a regular
|
|
79
|
+
expression anchored at both ends, so it matches exactly one note: a plain
|
|
80
|
+
`path:` term matches any path that contains it, and with titles as names one
|
|
81
|
+
note's path can lie inside another's. In the term every regular-expression
|
|
82
|
+
character and the `/` delimiter is escaped with a backslash, and a space and
|
|
83
|
+
a double quote are written as `\x20` and `\x22`, so a path that itself
|
|
84
|
+
contains search operators (`tag:`, `-`, `OR`, parentheses) stays text. Paths
|
|
85
|
+
are NFC-normalized first. A persisted focus of the earlier query version
|
|
86
|
+
(`obsidian-graph-search-paths/v1`, quoted substring terms) is still read. A
|
|
82
87
|
path that carries a control character, a C1 control, or the U+2028 or U+2029
|
|
83
88
|
line separator cannot be a search term and refuses with
|
|
84
89
|
`focus-path-unrepresentable`; so does an absolute path, a backslash path or a
|
|
@@ -256,7 +261,7 @@ nothing under `scripts/obsidian/` ships.
|
|
|
256
261
|
| --- | --- |
|
|
257
262
|
| `@mnstry/atelier/obsidian` | `src/runtime/obsidian/index.mjs`: enablement, machine settings, engine, service lifecycle, production seams, app qualification |
|
|
258
263
|
| `@mnstry/atelier/obsidian/contracts` | `src/projection/obsidian/contracts.mjs`: contract validation, `selectScope`, note paths |
|
|
259
|
-
| `@mnstry/atelier/obsidian/materialize` | `prepareView`, settings, path registry |
|
|
264
|
+
| `@mnstry/atelier/obsidian/materialize` | `prepareView`, settings (including the plugin's files and entry), path registry |
|
|
260
265
|
| `@mnstry/atelier/obsidian/publication` | `publishView`, editor adapters, the exchange |
|
|
261
266
|
| `@mnstry/atelier/obsidian/recovery` | recovery store, journals, late-writer recheck |
|
|
262
267
|
| `@mnstry/atelier/obsidian/edits` | edit observation, arbitration, apply policy, source apply |
|
|
@@ -272,9 +277,10 @@ What the tarball must and must not carry, and the package proof, are in
|
|
|
272
277
|
|
|
273
278
|
## First vault: what to expect
|
|
274
279
|
|
|
275
|
-
This is the path an adopter walks on one machine
|
|
276
|
-
|
|
277
|
-
|
|
280
|
+
This is the path an adopter walks on one machine. Every operation and flag
|
|
281
|
+
below is the shipped `atelier obsidian` usage text; `atelier obsidian --help`
|
|
282
|
+
prints it. There are three steps, and none of them is done by hand in
|
|
283
|
+
Obsidian.
|
|
278
284
|
|
|
279
285
|
1. Enable the projection in the project configuration. The member
|
|
280
286
|
`ext["mnstry.atelier.obsidian"]` is an `atelier-obsidian-ext-settings/v1`
|
|
@@ -295,19 +301,54 @@ usage text; `atelier obsidian --help` prints it.
|
|
|
295
301
|
has to carry; `policy install FILE` installs it and leaves the mode
|
|
296
302
|
unchanged; `policy revoke` returns the mode to `manual` and stops every
|
|
297
303
|
later apply.
|
|
298
|
-
3.
|
|
299
|
-
`atelier obsidian
|
|
300
|
-
Reaching the installed app is never a default, so
|
|
301
|
-
is required, and the
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
304
|
+
3. Open a view:
|
|
305
|
+
`atelier obsidian open --scope ID --consent-actor ID --adapter=obsidian-cli`.
|
|
306
|
+
That's it. Reaching the installed app is never a default, so
|
|
307
|
+
`--adapter=obsidian-cli` is required, and the first open of a workspace
|
|
308
|
+
records who allowed the maintenance service to run (`--consent-actor`;
|
|
309
|
+
later opens reconnect to it and need no consent). `open` starts or
|
|
310
|
+
reconnects maintenance and asks it for a tick, makes Obsidian (version
|
|
311
|
+
1.13.7 or later) know the view's vault as one of its vaults, opens it,
|
|
312
|
+
publishes the view, verifies the vault by reading it back, and waits until
|
|
313
|
+
the app answers for exactly that vault. It does this in whatever state
|
|
314
|
+
Obsidian is in; see
|
|
315
|
+
[What `open` does in each state of Obsidian](#what-open-does-in-each-state-of-obsidian).
|
|
316
|
+
|
|
317
|
+
The maintenance service can also be managed on its own:
|
|
318
|
+
`atelier obsidian service start --consent-actor ID --adapter=obsidian-cli`,
|
|
319
|
+
`service status` and `service stop`; `service unit --print
|
|
320
|
+
--adapter=obsidian-cli` prints a startup unit and installs nothing.
|
|
321
|
+
|
|
322
|
+
After an upgrade of Atelier, a maintenance service started earlier still runs
|
|
323
|
+
the earlier release. `open` replaces it: when the service of this workspace
|
|
324
|
+
proves itself Atelier's own but runs an earlier release than the installed one
|
|
325
|
+
(its record names the release it runs: the package version and a digest of
|
|
326
|
+
its modules; an earlier version, the same version with other modules, or no
|
|
327
|
+
release named), or refuses a tick that names a view as releases up to
|
|
328
|
+
0.2.0-alpha.11 do,
|
|
329
|
+
`open` stops it as `service stop` would and starts the installed release under
|
|
330
|
+
the consent already recorded, and says `service: restarted (outdated)`. A
|
|
331
|
+
service of a later release is never replaced by an earlier one, so two
|
|
332
|
+
installations used on one workspace (a global and a project-local one, say)
|
|
333
|
+
do not replace each other's service on every open: `open` answers
|
|
334
|
+
`service-unavailable` / `service-other-release`; run `atelier obsidian service
|
|
335
|
+
stop`, then open again, or open with the later release. A service in a long
|
|
336
|
+
tick is not stopped (`open` answers `busy`), and a listener that does not
|
|
337
|
+
prove itself this workspace's service is never touched.
|
|
338
|
+
|
|
339
|
+
The first time Obsidian opens a view's vault it asks "Do you trust the author
|
|
340
|
+
of this vault?", because every vault Atelier publishes carries Atelier's own
|
|
341
|
+
plugin. "Trust author and enable plugins" turns it on: a status bar item then
|
|
342
|
+
says whether the view is current, updating, held for an edit you made, stale,
|
|
343
|
+
or whether the maintenance service does not answer, and "Atelier: show status"
|
|
344
|
+
says why. "Browse vault in Restricted Mode" keeps every community plugin off
|
|
345
|
+
in that vault, and nothing else changes: maintenance, `open` and `status` work
|
|
346
|
+
without the plugin, as they always did. Obsidian keeps the answer per vault in
|
|
347
|
+
its own storage and asks again the next time until the vault is trusted or
|
|
348
|
+
restricted mode is switched on for good in its settings. Turning the plugin
|
|
349
|
+
off in a vault, or uninstalling it there, is respected from then on;
|
|
350
|
+
`atelier obsidian plugin on --scope ID` brings it back. What the plugin does
|
|
351
|
+
and never does is in [obsidian-plugin.md](obsidian-plugin.md).
|
|
311
352
|
|
|
312
353
|
`open` and `status` answer with a freshness state, not a promise. `current`
|
|
313
354
|
means the vault is the present generation, verified by read-back, and the app
|
|
@@ -319,7 +360,9 @@ and the view is not republished over it. The remaining outcomes (`indexing`,
|
|
|
319
360
|
`publisher-conflict`, `app-missing`, `app-version-unsupported`,
|
|
320
361
|
`app-cli-unavailable`, `launch-failed`, `service-unavailable`, `busy`,
|
|
321
362
|
`disabled`) each name what to do next, and exit code 3 says the answer is not
|
|
322
|
-
success.
|
|
363
|
+
success. Both also report Atelier's plugin for each view, under `plugin`:
|
|
364
|
+
present while it holds the vault open in the app, with the app and plugin
|
|
365
|
+
versions, or not present with the reason.
|
|
323
366
|
|
|
324
367
|
Edits made in the vault are preserved before anything is republished. A body
|
|
325
368
|
replacement is held as a pending edit; under manual mode it reaches its source
|
|
@@ -342,34 +385,120 @@ absolute directory. Private state inside an enrolled repository is refused.
|
|
|
342
385
|
Publication is proven on macOS arm64 only and is refused on Windows; see
|
|
343
386
|
[Known limits](#known-limits).
|
|
344
387
|
|
|
345
|
-
###
|
|
346
|
-
|
|
347
|
-
|
|
388
|
+
### What `open` does in each state of Obsidian
|
|
389
|
+
|
|
390
|
+
Obsidian opens a vault from a link only when the folder is in its own vault
|
|
391
|
+
list, the `vaults` of `obsidian.json` in its user-data directory. `open` puts
|
|
392
|
+
the view's vault there first, and never asks a person to open a folder by
|
|
393
|
+
hand. It then names the vault by its id (`obsidian://open?vault=<id>`), never
|
|
394
|
+
by path where the id reaches it first: a path is matched against the list by
|
|
395
|
+
string prefix, so a link for a note under `/A/strategy-lab` can open the
|
|
396
|
+
listed vault `/A/strategy`. When another vault's folder is named like the id,
|
|
397
|
+
or the id is unusual, the vault's own path is used, which the app matches
|
|
398
|
+
exactly because the folder is listed.
|
|
399
|
+
|
|
400
|
+
- **Obsidian runs and answers its command line (any vault is open).** `open`
|
|
401
|
+
reads the app's vault list through its command line. A vault the app does
|
|
402
|
+
not list is added and opened in a new window through the app itself (it
|
|
403
|
+
writes its own settings), and read back from its list. `open`
|
|
404
|
+
then waits, bounded, until the app answers for exactly this vault, and asks
|
|
405
|
+
maintenance for the view again: the publication runs through the app, which
|
|
406
|
+
now holds the vault, with every editor check of the publication protocol.
|
|
407
|
+
Obsidian does not have to be quit.
|
|
408
|
+
- **Obsidian is not running.** The view is published on the path with no app,
|
|
409
|
+
as before. `open` then adds the vault to the app's settings file (see
|
|
410
|
+
[Obsidian's vault list](obsidian-contract.md#obsidians-vault-list) for
|
|
411
|
+
exactly when and how that one file is written, marked to reopen) and starts
|
|
412
|
+
Obsidian plainly, with no link: an Obsidian started with a link opens only
|
|
413
|
+
that vault and drops the reopen flag of every other one, so your other
|
|
414
|
+
vaults would not come back the next time you start it. Started plainly, it
|
|
415
|
+
reopens every vault it had open, this one included, and once its command
|
|
416
|
+
line answers (the app itself, not the tool's "unable to find Obsidian")
|
|
417
|
+
`open` hands it the vault's link through that tool, never through the
|
|
418
|
+
operating system. On Linux a plain start is not
|
|
419
|
+
qualified yet, and Obsidian is started with the link (the other vaults'
|
|
420
|
+
reopen marks are lost there; a known limit).
|
|
421
|
+
An Obsidian that never ran on this account has no settings file yet, and
|
|
422
|
+
`open` does not create one: start Obsidian once, then open again.
|
|
423
|
+
- **Obsidian runs with no vault open.** Its command line then answers every
|
|
424
|
+
command with "Vault not found.", so nothing can be asked of it, and its
|
|
425
|
+
settings file belongs to the running app, so it is only read. A vault the
|
|
426
|
+
app already lists is opened by path and published through the app as
|
|
427
|
+
above. A vault it does not list yet is answered as outcome
|
|
428
|
+
`app-version-unsupported` with reason `no-vault-open`: open any vault in
|
|
429
|
+
Obsidian, or quit it, and open again. This is the one state of a running
|
|
430
|
+
Obsidian that `open` cannot get through alone.
|
|
431
|
+
- **Obsidian's command line is turned off** (the default of a new
|
|
432
|
+
installation). It then answers every command with "Command line interface
|
|
433
|
+
is not enabled", and only a link reaches it. With Obsidian running, `open`
|
|
434
|
+
adds and launches nothing. With Obsidian quit, `open` adds the vault to its
|
|
435
|
+
settings and starts it, and Obsidian opens the vault. Either way `open` then
|
|
436
|
+
answers `app-cli-unavailable` with reason `cli-turned-off` and says where to
|
|
437
|
+
turn it on: Settings > General > Advanced > Command line interface.
|
|
438
|
+
Publishing into a vault Obsidian holds needs it until Atelier's plugin takes
|
|
439
|
+
over that part.
|
|
440
|
+
|
|
441
|
+
Only `open` adds a vault to Obsidian: the maintenance service never does, so
|
|
442
|
+
it never opens a window nobody asked for. A declared view that was never
|
|
443
|
+
opened is published while Obsidian is quit, and `open --scope ID` adds it.
|
|
444
|
+
|
|
445
|
+
In no state does `open` add a view's vault inside a folder Obsidian already
|
|
446
|
+
lists as a vault (your home folder, say): that vault would show the view's
|
|
447
|
+
notes too, and a command-line call run in the view's folder would reach it.
|
|
448
|
+
`open` answers `launch-failed` with reason `vault-inside-another-vault`:
|
|
449
|
+
remove that vault from Obsidian's vault list, or keep Atelier's data root
|
|
450
|
+
outside that folder. A view's vault that Obsidian lists already, below such a
|
|
451
|
+
vault, is reached by its id instead (see "Which window answers" in
|
|
452
|
+
[Known limits](#known-limits)).
|
|
453
|
+
|
|
454
|
+
Obsidian can list one folder more than once, under another letter case or
|
|
455
|
+
through a link, and then open it in one window per entry. Each of those
|
|
456
|
+
windows holds the view's vault, and a publication coordinates with one window
|
|
457
|
+
only, so none is made: the view reports `publisher-conflict` with reason
|
|
458
|
+
`vault-open-in-several-windows`, and `open` answers the same, names the
|
|
459
|
+
entries (`open in Obsidian as: …`; `duplicates` in JSON) and launches nothing.
|
|
460
|
+
Remove the extra entries from Obsidian's vault list and open again. Closing
|
|
461
|
+
the extra windows is not enough: Obsidian keeps the last window it closed
|
|
462
|
+
marked open, so two entries can stay marked open with one window showing, and
|
|
463
|
+
the view stays refused until the list names the folder once.
|
|
464
|
+
While one entry of the folder has a window, `open` reaches only that one and
|
|
465
|
+
never opens another entry of the same folder beside it.
|
|
466
|
+
|
|
467
|
+
The publisher still writes into a vault only when it can coordinate with every
|
|
348
468
|
Obsidian that may hold it, or when the process table shows, positively, that
|
|
349
469
|
none runs. Otherwise it stops, and the view reports `publisher-conflict` with
|
|
350
470
|
reason `editor-uncoordinated`. That reason has several causes, and the report
|
|
351
471
|
does not say which: Obsidian runs without this vault open, or with it open but
|
|
352
472
|
its command line did not answer; the process table could not be read, or on
|
|
353
473
|
Linux shows an app that runs on a system Electron, which may be Obsidian; the
|
|
354
|
-
app's version was not checked when the publication began
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
version
|
|
474
|
+
app's version was not checked when the publication began; or an app started
|
|
475
|
+
while the view was being published with the app closed. `status` gives
|
|
476
|
+
`atelier obsidian open` as the next step (it adds the vault to Obsidian and
|
|
477
|
+
publishes through it), or quitting Obsidian (on Linux, also any app that runs
|
|
478
|
+
on a system Electron). When `open` itself already did that and the
|
|
479
|
+
publication still stopped, it says to quit Obsidian and open again.
|
|
480
|
+
|
|
481
|
+
The tick `open` asks for prepares and publishes its view once more, whatever
|
|
482
|
+
state the view is in. A view whose last publication did not settle (refused,
|
|
483
|
+
stale or still updating) is also tried again without waiting for a change: as
|
|
484
|
+
soon as the app looks different (it quit or started, or opened or closed a
|
|
485
|
+
vault in its list), and otherwise after a delay that starts at 30 seconds and
|
|
486
|
+
doubles per attempt, up to the full reconciliation every five minutes. What
|
|
487
|
+
the app looks like is read from the process table and the app's vault list
|
|
488
|
+
alone: maintenance runs nothing in Obsidian to find out, and asks it for its
|
|
489
|
+
version only when a view is about to be published, without blocking.
|
|
490
|
+
|
|
491
|
+
While Atelier's plugin holds the view's vault open in the app and the command
|
|
492
|
+
line gives no version (as it answers while a vault is still loading), the
|
|
493
|
+
version the plugin reports stands in (reason `plugin-reported`); a version the
|
|
494
|
+
command line gives always decides, and while the process table shows no app
|
|
495
|
+
running, a plugin's report counts for nothing. `open` then asks the command
|
|
496
|
+
line nothing: a vault the app's settings file lists is opened by path, and one
|
|
497
|
+
it does not show is answered as `app-cli-unavailable` with reason
|
|
498
|
+
`vault-open-cli-silent` (the app has the vault open; make sure its
|
|
499
|
+
command-line interface is turned on). So is a listed vault the command line
|
|
500
|
+
does not answer for once it is opened, while only the plugin reports the
|
|
501
|
+
version: `open` says so at once instead of waiting for the app.
|
|
373
502
|
|
|
374
503
|
## Known limits
|
|
375
504
|
|
|
@@ -402,8 +531,10 @@ test reported as a pass.
|
|
|
402
531
|
built on it does not coordinate with any app it then finds running, and
|
|
403
532
|
asks it nothing. That publication stops as `publisher-conflict` with reason
|
|
404
533
|
`editor-uncoordinated`, and the next publication asks again and reads the
|
|
405
|
-
version. A view that stopped is tried again on
|
|
406
|
-
|
|
534
|
+
version. A view that stopped is tried again on the next tick `open` asks
|
|
535
|
+
for, as soon as the app looks different, and otherwise after a delay that
|
|
536
|
+
starts at 30 seconds and doubles, up to the full reconciliation every five
|
|
537
|
+
minutes. A tick `open` asks for never reuses a remembered app answer.
|
|
407
538
|
- Install location (macOS): the app is found only at
|
|
408
539
|
`/Applications/Obsidian.app`, and its command-line tool only inside it. An
|
|
409
540
|
Obsidian installed elsewhere, or a renamed bundle, is `app-missing`.
|
|
@@ -417,9 +548,46 @@ test reported as a pass.
|
|
|
417
548
|
published through the app. `status` (under `service.app`) and `open` say what
|
|
418
549
|
to do: open any vault in Obsidian, or quit Obsidian. With the app quit,
|
|
419
550
|
maintenance publishes on its own path again; with a vault open, it reads
|
|
420
|
-
the version again on a later tick.
|
|
421
|
-
|
|
422
|
-
|
|
551
|
+
the version again on a later tick. While the app runs with no vault open,
|
|
552
|
+
`open` has it open a vault only when its list already knows that vault,
|
|
553
|
+
by path; one it does not know cannot be added then (see below). After its
|
|
554
|
+
own launch `open` waits, bounded, while the app is still opening the vault;
|
|
555
|
+
an app whose vault window is still loading answers a command with `Error:
|
|
556
|
+
Command "…" not found`, which is read as not up yet, never as a version.
|
|
557
|
+
- Obsidian's settings file: `open` adds a view's vault to the app's own list,
|
|
558
|
+
`obsidian.json` in its user-data directory: `~/Library/Application
|
|
559
|
+
Support/obsidian` on macOS and `$XDG_CONFIG_HOME/obsidian` (else
|
|
560
|
+
`~/.config/obsidian`) on Linux. It is written only while the process table
|
|
561
|
+
shows, positively, that no Obsidian runs, only when Obsidian created it
|
|
562
|
+
before, and with a backup beside it; see
|
|
563
|
+
[Obsidian's vault list](obsidian-contract.md#obsidians-vault-list). A
|
|
564
|
+
Flatpak or snap build keeps its list inside its sandbox and never reads that
|
|
565
|
+
file. Which build is in use is read from which vault list was written last
|
|
566
|
+
(every build rewrites its own when a vault window opens or closes), and only
|
|
567
|
+
when no build wrote one from its installation; for a Flatpak or snap build
|
|
568
|
+
the file is then neither read nor written: `open` answers
|
|
569
|
+
`obsidian-sandboxed` and says to start Obsidian with any vault open, then
|
|
570
|
+
open again, and adds the vault through the app. On Windows no location is
|
|
571
|
+
known, and `open` adds the vault through the app in the same way.
|
|
572
|
+
- Recent items: adding a vault through the running app is Obsidian's own
|
|
573
|
+
"open folder as vault", which also puts the vault's folder in the operating
|
|
574
|
+
system's recently used documents (Recent Items on macOS).
|
|
575
|
+
- Which window answers: Obsidian answers a command-line call that names a
|
|
576
|
+
vault (`vault=<id>` first) in that vault's window; any other call in the
|
|
577
|
+
window of the first vault in its list whose folder is the tool's working
|
|
578
|
+
directory or contains it, and otherwise in the vault window that had focus
|
|
579
|
+
last. A vault that takes a call is opened when it is closed. Calls about a
|
|
580
|
+
vault therefore name its id; only when that id would name another vault
|
|
581
|
+
first (one whose folder has the id as its name) do they run inside its
|
|
582
|
+
folder instead, and not at all when a vault listed before it at a folder
|
|
583
|
+
above it (your home folder, say) would take them there. The vault root is
|
|
584
|
+
taken in the spelling the file system stores, so a data root given in
|
|
585
|
+
another letter case on macOS changes nothing. Publication calls do this
|
|
586
|
+
only while the app's list shows the view's vault open, and run in a
|
|
587
|
+
directory that is no vault otherwise. So maintenance never reopens a vault
|
|
588
|
+
window you closed while Obsidian keeps running, and never reaches another
|
|
589
|
+
vault: publication then stops as `publisher-conflict` until `atelier
|
|
590
|
+
obsidian open` opens the vault again or Obsidian quits.
|
|
423
591
|
- Detecting the app: the process table is read with `ps -A -o comm=`
|
|
424
592
|
(`pid=,comm=` on Linux), and the app is recognised by the executable a
|
|
425
593
|
process runs, never by its arguments, so a path argument that contains an
|
|
@@ -469,6 +637,15 @@ test reported as a pass.
|
|
|
469
637
|
- An edit the byte lens cannot turn into source bytes (a new or changed link
|
|
470
638
|
to another note of the vault, an edited front matter) becomes a copy-only
|
|
471
639
|
proposal. No operation applies one.
|
|
640
|
+
- Atelier's plugin (phase 1) reports presence and status and decides
|
|
641
|
+
nothing: publication still coordinates with the app through its
|
|
642
|
+
command-line tool, which must be enabled, and the plugin only supplies the
|
|
643
|
+
app version where that tool gives none. A person who turns the plugin off
|
|
644
|
+
in a vault, or uninstalls it there, is followed: its entry is not added
|
|
645
|
+
back and a deleted folder is not made again until the person turns it on
|
|
646
|
+
in Obsidian's settings or runs
|
|
647
|
+
`atelier obsidian plugin on --scope ID`. Plugin files are never removed from
|
|
648
|
+
a vault. See [obsidian-plugin.md](obsidian-plugin.md).
|
|
472
649
|
- Acceptance: a schema-valid receipt closes no gate, the package proof closes
|
|
473
650
|
no gate, and no adopter acceptance is recorded in this repository.
|
|
474
651
|
|
|
@@ -482,3 +659,9 @@ The test suite proves each oracle can fail:
|
|
|
482
659
|
- `createReceiptValidatorForOracleTests` accepts a rule table with one rule
|
|
483
660
|
switched off; every negative case that depends on that rule then passes,
|
|
484
661
|
and even that closes nothing.
|
|
662
|
+
- `openScopeForOracleTests` with an `OPENING_PRIMITIVES.keptByApp` that
|
|
663
|
+
never holds: an open with Obsidian running without the vault reports the
|
|
664
|
+
publisher conflict it could have cleared, and the first-open oracle fails.
|
|
665
|
+
- `createMaintenanceEngineForOracleTests` with an
|
|
666
|
+
`ENGINE_PRIMITIVES.isRetryDue` that never holds: a refused view waits for
|
|
667
|
+
the full reconciliation, and the retry oracle fails.
|
|
@@ -63,7 +63,13 @@ Shipped, and required by the audit:
|
|
|
63
63
|
- Every `contracts/atelier-obsidian-<name>.v<n>.schema.json` present in the
|
|
64
64
|
source tree (lowercase name, versioned). Each must be packed and exported
|
|
65
65
|
under its own path, and the audit fails if it finds none.
|
|
66
|
-
- `docs/obsidian.md` and `docs/obsidian-
|
|
66
|
+
- `docs/obsidian.md`, `docs/obsidian-contract.md` and `docs/obsidian-plugin.md`.
|
|
67
|
+
- Atelier's own Obsidian plugin, which every managed vault receives:
|
|
68
|
+
`plugins/obsidian/manifest.json`, `main.js` and `styles.css`, the only files
|
|
69
|
+
admitted under `plugins/`, with its channel
|
|
70
|
+
(`src/projection/obsidian/plugin-bridge/index.mjs`) and the service's half
|
|
71
|
+
(`src/runtime/obsidian/plugin-channel.mjs`). `main.js` is scanned for egress
|
|
72
|
+
like every packed script.
|
|
67
73
|
- `fixtures/obsidian/**`: small synthetic text only. Every packed fixture, in
|
|
68
74
|
any subtree and spelling under `fixtures/`, is at most 262,144 bytes. The
|
|
69
75
|
acceptance receipts under `fixtures/obsidian/acceptance/receipts/` are
|
package/docs/upgrade.md
CHANGED
|
@@ -9,6 +9,26 @@ workspace between Atelier package releases. The flow is local-only: it does not
|
|
|
9
9
|
provision repositories, contact a Git host, mutate the MNSTRY runtime, or write
|
|
10
10
|
through a browser view.
|
|
11
11
|
|
|
12
|
+
## Upgrading to 0.2.0-alpha.12
|
|
13
|
+
|
|
14
|
+
This release makes the first open of an Obsidian view automatic and ships
|
|
15
|
+
Atelier's own plugin. `open` adds the view's vault to Obsidian itself, whether
|
|
16
|
+
Obsidian is running or not, and publishes without quitting it; a closed
|
|
17
|
+
Obsidian is started so your other vaults reopen. Vaults are laid out
|
|
18
|
+
readably: folders mirror your repository and file names are note titles,
|
|
19
|
+
with identity kept in a properties block; a vault published by an earlier
|
|
20
|
+
release is laid out again once, keeping held edits. Each managed vault gets
|
|
21
|
+
the Atelier plugin (one trust prompt per vault) with a status bar. A
|
|
22
|
+
maintenance service of an earlier release is replaced by `open` under the
|
|
23
|
+
consent already recorded. No project or machine setting needs to change.
|
|
24
|
+
Machine settings stay v1; generation manifests move to v2 (below).
|
|
25
|
+
|
|
26
|
+
Node.js 24 (24.13.1 or later) is now supported alongside Node.js 22, and
|
|
27
|
+
both write byte-identical graph files, so people on either can share a
|
|
28
|
+
repository. `atelier dev` on a port that is already taken now says
|
|
29
|
+
`port-in-use` and names `--port=<free port>`; before, it printed an internal
|
|
30
|
+
error. A script that read that failure's exit code sees 2 instead of 1.
|
|
31
|
+
|
|
12
32
|
## Upgrading to 0.2.0-alpha.11
|
|
13
33
|
|
|
14
34
|
This release tightens the Obsidian projection's app checks after independent
|
|
@@ -163,7 +183,7 @@ See `docs/contract-stability.md`.
|
|
|
163
183
|
## Before You Upgrade
|
|
164
184
|
|
|
165
185
|
- Start from a clean Git status in the copied workspace.
|
|
166
|
-
- Confirm Node.js matches the package range: `>=22.18.0 <23`.
|
|
186
|
+
- Confirm Node.js matches the package range: `>=22.18.0 <23 || >=24.13.1 <25`.
|
|
167
187
|
- Keep this package root and copied starter workspaces separate.
|
|
168
188
|
- Keep upgrade diffs generic: placeholders only, no project-specific private
|
|
169
189
|
material, transcripts, support bundles, local absolute paths, local agent
|
|
@@ -185,7 +205,7 @@ For registry installs, pin the exact version and record the resolved version
|
|
|
185
205
|
in the lockfile:
|
|
186
206
|
|
|
187
207
|
```bash
|
|
188
|
-
npm install --save-dev @mnstry/atelier@0.2.0-alpha.
|
|
208
|
+
npm install --save-dev @mnstry/atelier@0.2.0-alpha.12
|
|
189
209
|
npx mnstry-atelier lock write --project ./atelier.project.json
|
|
190
210
|
```
|
|
191
211
|
|
|
@@ -193,7 +213,7 @@ For Git installs, pin the release tag rather than a branch, so the lock file
|
|
|
193
213
|
records exactly what was reviewed:
|
|
194
214
|
|
|
195
215
|
```bash
|
|
196
|
-
npm install --save-dev "git+https://github.com/MNSTRY/atelier.git#v0.2.0-alpha.
|
|
216
|
+
npm install --save-dev "git+https://github.com/MNSTRY/atelier.git#v0.2.0-alpha.12"
|
|
197
217
|
npx mnstry-atelier lock write --project ./atelier.project.json
|
|
198
218
|
```
|
|
199
219
|
|
package/fixtures/obsidian/contracts/generation-manifest-v2/invalid/case-only-collision.v2.json
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "atelier-obsidian-generation-manifest/v2",
|
|
3
|
+
"layoutVersion": 2,
|
|
4
|
+
"generationId": "gen-0001",
|
|
5
|
+
"scopeId": "scope-topic",
|
|
6
|
+
"snapshotId": "snap-0001",
|
|
7
|
+
"notes": [
|
|
8
|
+
{
|
|
9
|
+
"repoId": "north",
|
|
10
|
+
"nodeId": "a",
|
|
11
|
+
"title": "Shared concept",
|
|
12
|
+
"path": "north/topics/Shared concept.md",
|
|
13
|
+
"noteDigest": "sha256:5555555555555555555555555555555555555555555555555555555555555555",
|
|
14
|
+
"regions": {
|
|
15
|
+
"frontmatter": {
|
|
16
|
+
"start": 0,
|
|
17
|
+
"end": 120
|
|
18
|
+
},
|
|
19
|
+
"identity": {
|
|
20
|
+
"start": 40,
|
|
21
|
+
"end": 116
|
|
22
|
+
},
|
|
23
|
+
"body": {
|
|
24
|
+
"start": 120,
|
|
25
|
+
"end": 280
|
|
26
|
+
},
|
|
27
|
+
"generated": [
|
|
28
|
+
{
|
|
29
|
+
"kind": "relations",
|
|
30
|
+
"range": {
|
|
31
|
+
"start": 280,
|
|
32
|
+
"end": 340
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"repoId": "north",
|
|
40
|
+
"nodeId": "d",
|
|
41
|
+
"title": "Shared concept",
|
|
42
|
+
"path": "north/topics/shared CONCEPT.md",
|
|
43
|
+
"noteDigest": "sha256:6666666666666666666666666666666666666666666666666666666666666666",
|
|
44
|
+
"regions": {
|
|
45
|
+
"frontmatter": {
|
|
46
|
+
"start": 0,
|
|
47
|
+
"end": 40
|
|
48
|
+
},
|
|
49
|
+
"body": {
|
|
50
|
+
"start": 40,
|
|
51
|
+
"end": 200
|
|
52
|
+
},
|
|
53
|
+
"generated": [
|
|
54
|
+
{
|
|
55
|
+
"kind": "relations",
|
|
56
|
+
"range": {
|
|
57
|
+
"start": 200,
|
|
58
|
+
"end": 260
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"kind": "identity",
|
|
63
|
+
"range": {
|
|
64
|
+
"start": 260,
|
|
65
|
+
"end": 340
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"identity": {
|
|
70
|
+
"start": 260,
|
|
71
|
+
"end": 340
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"repoId": "north",
|
|
77
|
+
"nodeId": "e",
|
|
78
|
+
"title": "Diagram",
|
|
79
|
+
"path": "north/figures/diagram.svg.md",
|
|
80
|
+
"noteDigest": "sha256:8888888888888888888888888888888888888888888888888888888888888888",
|
|
81
|
+
"regions": {
|
|
82
|
+
"frontmatter": {
|
|
83
|
+
"start": 0,
|
|
84
|
+
"end": 80
|
|
85
|
+
},
|
|
86
|
+
"identity": {
|
|
87
|
+
"start": 0,
|
|
88
|
+
"end": 80
|
|
89
|
+
},
|
|
90
|
+
"body": {
|
|
91
|
+
"start": 240,
|
|
92
|
+
"end": 240
|
|
93
|
+
},
|
|
94
|
+
"generated": [
|
|
95
|
+
{
|
|
96
|
+
"kind": "representation",
|
|
97
|
+
"range": {
|
|
98
|
+
"start": 80,
|
|
99
|
+
"end": 240
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
"links": [
|
|
107
|
+
{
|
|
108
|
+
"edgeId": "e4",
|
|
109
|
+
"sourceNodeId": "d",
|
|
110
|
+
"targetNodeId": "a",
|
|
111
|
+
"type": "links_to",
|
|
112
|
+
"origin": "markdown",
|
|
113
|
+
"targetState": "in-scope",
|
|
114
|
+
"inversions": [
|
|
115
|
+
{
|
|
116
|
+
"source": {
|
|
117
|
+
"start": 12,
|
|
118
|
+
"end": 30
|
|
119
|
+
},
|
|
120
|
+
"note": {
|
|
121
|
+
"start": 52,
|
|
122
|
+
"end": 96
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"edgeId": "e5",
|
|
129
|
+
"sourceNodeId": "a",
|
|
130
|
+
"targetNodeId": "d",
|
|
131
|
+
"type": "supersedes",
|
|
132
|
+
"origin": "declared",
|
|
133
|
+
"targetState": "in-scope"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"edgeId": "e1",
|
|
137
|
+
"sourceNodeId": "a",
|
|
138
|
+
"targetNodeId": "b",
|
|
139
|
+
"type": "related",
|
|
140
|
+
"origin": "declared",
|
|
141
|
+
"targetState": "outside-selection"
|
|
142
|
+
}
|
|
143
|
+
],
|
|
144
|
+
"attachments": [
|
|
145
|
+
{
|
|
146
|
+
"path": "north/figures/diagram.svg",
|
|
147
|
+
"digest": "sha256:7777777777777777777777777777777777777777777777777777777777777777",
|
|
148
|
+
"byteLength": 900
|
|
149
|
+
}
|
|
150
|
+
],
|
|
151
|
+
"completeness": {
|
|
152
|
+
"status": "complete",
|
|
153
|
+
"expectedNotes": 3,
|
|
154
|
+
"writtenNotes": 3
|
|
155
|
+
},
|
|
156
|
+
"freshness": {
|
|
157
|
+
"status": "current",
|
|
158
|
+
"checkedAt": "2026-01-05T10:00:05Z"
|
|
159
|
+
}
|
|
160
|
+
}
|