@mnstry/atelier 0.2.0-alpha.13 → 0.2.0-alpha.14
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 +233 -0
- package/README.md +22 -5
- package/docs/blocks/capability-stewardship.md +1 -1
- package/docs/blocks/learning-harnesses.md +1 -1
- package/docs/capability-stewardship.md +2 -4
- package/docs/ingestion.md +3 -3
- package/docs/inquiry-harnesses.md +2 -3
- package/docs/install.md +3 -3
- package/docs/learning-harnesses.md +2 -3
- package/docs/local-services.md +42 -3
- package/docs/obsidian-contract.md +23 -0
- package/docs/obsidian-plugin.md +214 -3
- package/docs/obsidian.md +271 -11
- package/docs/personal-workspace.md +13 -4
- package/docs/release-engineering.md +2 -1
- package/docs/repo-boundary-guard.md +26 -3
- package/docs/semantic-operation.md +4 -3
- package/docs/upgrade.md +174 -2
- package/fixtures/templates/local-library.v1.json +2 -1
- package/package.json +2 -1
- package/plugins/obsidian/main.js +57 -8
- package/plugins/obsidian/manifest.json +1 -1
- package/src/boundary/content-rules.mjs +532 -67
- package/src/boundary/policy.mjs +135 -56
- package/src/cli/run.mjs +3 -2
- package/src/commands/obsidian.mjs +23 -7
- package/src/graph/graph.mjs +13 -5
- package/src/graph/knowledge-graph.mjs +19 -1
- package/src/ingestion/optional/graphify/graphify_supplier.py +5 -0
- package/src/ingestion/optional/graphify/native-closure-pins.json +275 -0
- package/src/ingestion/optional/graphify/native-pins.json +306 -0
- package/src/knowledge-health/rule-identity.mjs +14 -9
- package/src/personal-workspace/index.mjs +1 -1
- package/src/personal-workspace/selection.mjs +17 -1
- package/src/project/path-match.mjs +2 -1
- package/src/projection/obsidian/materialize/prepare-view.mjs +87 -5
- package/src/projection/obsidian/personal-workspace.mjs +87 -4
- package/src/projection/obsidian/proposals/adapter.mjs +46 -23
- package/src/projection/obsidian/proposals/observation.mjs +40 -2
- package/src/projection/obsidian/publication/publisher.mjs +72 -5
- package/src/projection/project.mjs +3 -2
- package/src/runtime/obsidian/engine.mjs +293 -55
- package/src/runtime/obsidian/lifecycle.mjs +17 -12
- package/src/runtime/obsidian/login-item.mjs +4 -1
- package/src/runtime/obsidian/observation.mjs +36 -4
- package/src/runtime/obsidian/opening.mjs +3 -2
- package/src/runtime/obsidian/personal-selection.mjs +125 -0
- package/src/runtime/obsidian/pipeline.mjs +44 -3
- package/src/runtime/obsidian/private-lock.mjs +39 -4
- package/src/runtime/obsidian/service-main.mjs +58 -12
- package/src/runtime/obsidian/service.mjs +66 -6
- package/src/runtime/obsidian/view-permission.mjs +334 -0
- package/src/ui/control-system.mjs +201 -0
- package/src/ui/html-primitives.mjs +3 -0
- package/src/upgrade/closure-diagnostics.mjs +135 -0
- package/src/upgrade/closure.mjs +649 -0
- package/src/upgrade/upgrade.mjs +19 -7
- package/templates/external-project-workspace/pack-lifecycle.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,238 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0-alpha.14
|
|
4
|
+
|
|
5
|
+
- The Obsidian maintenance service's log no longer gets a `status-project-loaded` line for every failure and recovery, or every slow load, of the plugin's status read while its loads come less than a minute apart and fail, or are slow, every other load or every third. Before, any load that answered ended a row of failures, and any load in time a row of slow loads. Now a row of failures ends only at a load that answers a minute or more after its last failure, with a line that names how many failures it held, and a row of slow loads only at a load in time a minute or more after the last slow one. Such a loader writes one row, less and less often. While a project is kept, it is loaded again every two and a half minutes, so there a row of failures ends at the next reload that answers. A loader that answers at least one reload between two failures still writes each failure and the load that ends its row, and one that fails at every reload and answers only at its retry keeps one row. Each slow load is still written there. A load at every request is now written too, when each load names the files it started over. That happens while a file that decides the views vouches for nothing, or while loads never agree, and before it showed only as a growing count. A load after which nothing is kept, though it named the files it started over, is written from the second in a row, on the same doubling row (`notKept`, with `vouched`); a load that named other files, as after a new confirmed selection, neither counts nor ends that row. `statusLoads` in the status document counts these loads as `notKept`, one at a start and one after each change on a healthy service.
|
|
6
|
+
- An Obsidian maintenance service started by a login item asks its project loader once (under `--personal-home`, its locator), and now hands its first answer on faithfully. A thenable that is no promise is adopted once as a native promise that settles as it does, so its `then` is called once in all. Before, it was called twice: once where the process let the answer go, and again at the service's own start. A promise whose `constructor` reads as this realm's `Promise` is let go without calling a `then` of its own, which may throw; any other, as a rule another realm's promise or a subclass's, is adopted as any other thenable is. A service started any other way that is handed a promise with a `then` of its own that throws now refuses it typed as well. Before, that start ended untyped. Anything thrown, `undefined` included, and a project whose `then` cannot be read, ends the start as a failure of the loader does. Before, `undefined` was taken as an answer of nothing: the start went on to resolve a workspace from it, and for a locator it was refused as `service-locator-invalid`. An unreadable `then` ended the process with an error the service never logged. The service also logs what was thrown when it has no prototype, or a `code` or `message` that cannot be read, where before that logging itself threw. A start ended by an error without a code is still a crash that the service manager restarts.
|
|
7
|
+
- A view the Obsidian maintenance service refuses for a file-system error is now named in its private log. The view's freshness names only the refusal's code, and such a tick is no failure, so nothing recorded the error before. Examples are `vault-location-unusable` for a vault folder that could not be allocated or placed, `mixed-read` for a configuration file that could not be read again during the tick or a source removed while it was read, and `staging-failed` for candidates that could not be staged. The service now writes a `view-refused` line for each such view whose refusal carries a file-system error's code, at most 64 a tick. The line names the view, the refusal's code and the error's code (`EACCES`, `EIO`, `ENOENT`, `ENOSPC`), and `cleanupCause` when a cleanup after it failed too. Only codes shaped as a file-system error's are written, never a message. A view the next tick names for the same reason is not written again, so a vault folder that stays unwritable, tried at every tick, is written once. The engine's tick report carries the same entries as `refusalCauses`, only when there are any.
|
|
8
|
+
- A failure while the publisher cleans up the candidates of a staging that did not complete no longer replaces what ended the staging. Before, a removal or a read that failed (`EBUSY`, `EPERM`, `EIO`, a volume gone) was thrown in place of the refusal or stop, and the tick failed without a typed reason. Now each candidate is cleaned up on its own, so one that cannot be removed leaves the others removed. What ended the staging is handed on unchanged: a caller's stop, a refusal such as `vault-root-moved`, or `staging-failed`. The code of the first such failure is added to a copy of its detail as `cleanupCause`. A candidate that could not be removed stays where it was, as before. That is in staging, or at its exchange path once the publication's journal is written, where the restart recovery of the next publication removes it.
|
|
9
|
+
- A selected source removed while the Obsidian maintenance service prepares or publishes a view, after the tick observed it and before the preparation, or the look again at the sources before the commit, read it, now refuses that view as `mixed-read`, as a source removed during the graph build does: no generation is committed for the view, the tick goes on to its other views, and the next tick reads everything again and publishes without the source. A generation already committed and prepared again is not reported current. Before, the tick failed without a typed reason: the view read `publisher-error`, the views after it were not prepared in that tick, and the service read degraded and recorded the error until the next tick. The rule is the build's: only a source the tick observed and that is gone when it is read (or its folder) is refused this way, and a file that cannot be read for another reason, or a path still there, still stops the tick. The engine primitive that decides it is now named `sourceRemovedSinceObserved`.
|
|
10
|
+
- A running Obsidian maintenance service whose loaded project is pinned to a selection record below the highest record any part of the service has read the selection history ending at loads the project again at its next tick, with no read of its own to trigger it. A confirmation read by another part of the service (the plugin's status read) and removed before the engine looked therefore refuses `selection-history-shortened` at that tick. Before, the engine went on publishing under its record until its next recheck, at the latest the next full reconciliation. Views that a change stops one after another have the history's end read once, not once a view, and it is read also when a file that decides the views could not be read.
|
|
11
|
+
- A source removed while the Obsidian maintenance service builds the canonical graph, after the build listed it and before it read it, now refuses the views of that tick as `mixed-read`, as a source changed during the build is: nothing is prepared from the build, and the next tick reads everything again and publishes without it. Before, the tick failed without a typed reason, and the service read degraded and recorded the error until the next tick. Only a source this tick observed, gone when the build reads it (or its folder), is refused this way. Anything else still stops the tick: a file that cannot be read for another reason, a path still there, one the walk over the sources listed and could not read, or one it never observed, so a path that can never be read fails every tick as before.
|
|
12
|
+
- AP-03 cleanup joins owned child handles even when the runtime status record is missing or malformed, and retains disposable roots when cleanup cannot be proven.
|
|
13
|
+
- The Obsidian maintenance service goes on answering health and status while a tick observes and routes pending edits. Observation gives the event loop a turn between two edits, and proposal routing gives it one before each repository and between two operations. Each edit and each operation is still handled in one stretch, and what is observed, recorded and proposed is unchanged. Before, a tick with a burst of edits held the listener for the whole observation. An observation asked for while another is under way now waits for it.
|
|
14
|
+
- The event-loop benchmark (`scripts/obsidian/benchmark-event-loop.mjs`) waits for the health request in flight at the end of the first tick, and of a transition, before it judges the window. A stretch at the end that holds the listener is then measured by that request's latency, under the same five-second rule as any other, instead of being read as a gap in the sampling; the 250 ms edge rule is about whether the sampling reached the edge, as it already was at the start of a tick. A request in flight at the end that is never answered now fails the run, where before it was left out. The rules a request is judged by are unchanged. The result's `health` and `transitionHealth` say whether such a request was waited for (`awaitedProbeInFlightAtEnd`; `false` also when it was answered at once). `--timeout-ms` may now be up to four hours, so a first publication of ten thousand notes in six views can be run; the window is judged without a limit on how many requests it holds.
|
|
15
|
+
- A running Obsidian maintenance service no longer follows a selection history back when it is cut short. It notes the highest record number at every read of the history's end, and a history that ends earlier refuses `selection-history-shortened`: nothing is loaded from it, a project already loaded is refused at its next recheck, the views say why and the vault keeps what it last published, until the removed records are back or the history is as long again. Before, the service loaded whatever record the history then ended with, so a history cut after a restore nobody confirmed published that restore. The number is kept in the process only: a service started after the cut, by the person or at login, after an upgrade or a failure, reads the history as it then stands and follows what it ends with, as each command run once does. Keeping it across a start is not part of this change.
|
|
16
|
+
- The Obsidian maintenance service goes on answering health and status while a published view is read back. The read-back after a commit gives the event loop a turn between notes, as preparation and publication do. It ran through before: for one view of 10,000 notes it held the listener for 1.9 s on a quiet host and 16.8 s on a loaded one, long enough for three health requests in a row to time out. Each note of the committed generation is still read once, and a view is reported current only after its read-back. `reconcileCooperatively` beside `reconcile` in `src/runtime/obsidian/observation.mjs` is the walk that waits.
|
|
17
|
+
- The event-loop benchmark (`scripts/obsidian/benchmark-event-loop.mjs`) has two more scenarios, which ask the service for one more tick after the first publication, with nothing changed, and judge that tick: by what it did, by how long it held the event loop, and by the health requests across it. `--scenario=next-tick` keeps the watcher a service runs with, so that tick is told of the notes the publication wrote, as the tick after a publication is; when the publication took five minutes or more it is also a full reconciliation, as it is in a service. `--scenario=full-reconciliation` makes it a full reconciliation whatever the publication took. The result records the tick, whether it was full, its longest event-loop delay, and what the watcher had reported before it. Until now no stage ran a tick that a watcher had told of anything, and the one full reconciliation a stage ran (in `observe-16`) was over the small corpus that stage is run with.
|
|
18
|
+
- The Obsidian maintenance service goes on answering health and status while a tick looks at the vault and at the sources. Before a view is prepared, the walk over every view's notes and the walk over the sources give the event loop a turn between files, as the read-back does. They ran through before: on one view of 10,000 notes the tick after a publication (told by the watcher of every note it wrote, and a full reconciliation as well after a long publication) held the listener for 5.5 s on a loaded host, and a health request timed out; at every full reconciliation, every five minutes, each walk reads every file. The pending edits are still read, compared and written in one stretch, after the walk over the vault. A file changed after its turn in a walk is seen by the next tick, by its stat hint or the watcher, or at the latest by the next full reconciliation. The tick reports two more phases to a caller that asks for them, `vault-observation` and `late-writer-recheck`. In the event-loop benchmark, a second-tick stage's ending is one tested function, and its delay test samples the stretch it means to leave out.
|
|
19
|
+
- A note removed from a vault is no longer lost when the tick that saw it fails without a typed reason (a source or a note it cannot read, a full disk while an edit is kept). The removal is a change once, and such a tick had recorded it without acting on it, so the view stayed `current` with the note gone, through every later full reconciliation. The tick after a failure without a typed reason now starts as the first tick of a process does: it loads the project again and looks at every view, and the view reads `vault-note-missing`. A source changed in such a tick, or a view added to the configuration in a tick whose project loader failed, is published by the next one for the same reason. The phase `late-writer-recheck` is reported when the look again at displaced files fails too.
|
|
20
|
+
- An Obsidian maintenance service whose engine lock could not be given back at the end of a tick (its released ticket could not be written: a full disk, an I/O error) goes on maintaining once the write succeeds. Before, every later tick found the service's own unreleased ticket and answered `busy`, which the service counts as a success: health read healthy, the views stayed `current`, and nothing was maintained until the service was started again. The tick whose release failed still fails; the lock stays held, and the next tick gives it back before it takes it again. A release of a private generation lock that throws now settles nothing, so calling it again tries again.
|
|
21
|
+
- A private generation lock whose acquisition fails after its ticket was put in place (the directory sync or the read-back of the ticket, or the listing that confirms it) removes that ticket again, while it still holds its own bytes, before the failure is reported; a removal that fails too is made at that process's next acquisition in the same directory. Before, the ticket stayed on disk unreleased and nobody held it to release it: every later acquisition by the same process found it held by that process, so an Obsidian maintenance service answered `busy` at every tick until it was started again, with health reading healthy. The engine lock, the start lock of `service start` and the proposal adapter's lock all take this path.
|
|
22
|
+
- The Obsidian maintenance service goes on answering health and status while a tick builds the canonical graph. The census (each Markdown source read, hashed and parsed, or reused from the graph file cache) gives the event loop a turn between sources; a first tick reads every source, and on a heavily loaded host the build for one view of 10,000 invented notes held the listener for 8 s in one stretch. The settings that select the views are checked at each wait, as during a preparation. `buildKnowledgeGraphSteps` and `buildCanonicalGraphSteps` are the builds as steps; `buildKnowledgeGraph` and `buildCanonicalGraph` run them through, unchanged for their callers. A project bound to a personal workspace is built by its own route in one stretch, as before, and the other sources (HTML files, the sidecars of PDF and Word documents) are read without a turn between them. Walking the directories, resolving links and assembling the graph still run through. `createProductionSeams()` has one more member, `buildGraphCooperatively`, which the engine uses for every project not bound to a personal workspace; a caller that replaces `buildGraph` and leaves `buildGraphCooperatively` as it ships builds every graph through its own `buildGraph`, as a replaced `prepareView` already prepares every view. Knowledge Health pins the bytes of the two graph files, so its rule identity is regenerated for them: the `check` each assessment carries names a new `version`, `sourceTree` and `graphBlob`, and the rule it applies is unchanged.
|
|
23
|
+
- Pages that still called shipped commands unreleased now say they shipped in 0.2.0-alpha.13: `atelier knowledge semantic`, `atelier ingest` (with the `./ingestion` export), `atelier inquiry`, `atelier harness` and `atelier capability`. Each was checked against the published 0.2.0-alpha.12 and 0.2.0-alpha.13 packages.
|
|
24
|
+
- A staged or pushed deletion of a tracked binary file no longer blocks the boundary check. A deleted path has nothing to read at the revision being scanned, yet the check tried to read it and reported `content-scan-incomplete` ("binary content could not be read within the 8388608-byte per-file limit"), which refused the commit or the push. A binary added or changed in the same change is still read and scanned, and only the extended header of the diff can say a path was deleted, so a content line cannot skip a file. A binary rename that git shows as a deletion and an addition is unblocked the same way.
|
|
25
|
+
- The optional Graphify supplier loads again. `graphify_supplier.py` reads `native-pins.json` and `native-closure-pins.json` beside it when it is imported, and neither file was in the package, so importing it (and the JSON bridge, query and composition receivers that import it) failed with a missing file in 0.2.0-alpha.13. Both pin files ship now, admitted by the release tarball audit, pinning Graphify 0.9.72 at upstream commit `1cd9a36c`: 30 operations and 67 closure modules. Of the 30, `extract-semantic` is pinned but cannot run, because its `graphify.llm` module is outside the admitted closure, so 29 are usable. `extract-structure` now refuses `parallel: true` with `NATIVE_WORKERS_UNATTESTED`: parallel extraction starts worker processes that import Graphify outside the pinned source loader, so only in-process extraction is admitted.
|
|
26
|
+
- `atelier upgrade closure` says what to do in two more cases and refuses one more flag. A refused linked `--npm-root` prints the directory's `realPath`, and the docs say why macOS `/tmp` and `$TMPDIR` are refused until it is given. A tarball URL with no readable host no longer suggests `--registry-host`, which cannot clear it. The `prefix-mismatch` message no longer claims npm would use another root, since the plan refuses any parent that declares workspaces. `--__proto__`, which the shared argument parser dropped, is refused as an unsupported flag. The upgrade guide now matches the code on an empty `--npm-root=` (`npm-root-required`), on `http` and `https` (counted separately in `registrySchemes`) and on when Atelier's own `package.json` is read.
|
|
27
|
+
- The practice-consumer reconsideration tool (`scripts/practice-consumer/reconsider.mjs measure`) can keep the full context of one measurement with `--capture NEWFILE`: the records and definition the evaluator was given, each evaluation's input and outcome as received, the citations that were not evaluated and why, and the decisions with no citation. It is written once, to a file that must not exist yet and must not be the measurements file under another name (checked by real path before measuring and by the file itself after writing), only when the measurement line can be appended, and before that line, so a refused capture appends nothing and the run removes the capture it wrote; only a run interrupted between writing the capture and appending its line can leave one standing. Without `--capture` nothing changes.
|
|
28
|
+
- The boundary content scan reads every changed file under its own name. Its diffs now pin their format (`--no-ext-diff --no-textconv --no-color --src-prefix=a/ --dst-prefix=b/ --no-relative --inter-hunk-context=0 --submodule=short --full-index`), so `diff.noprefix`, `diff.mnemonicPrefix`, changed prefixes, forced colour, an external diff or a textconv driver no longer leave a staged or pushed change unscanned, and fused hunks or submodule summaries do not stop clean work. Quoted paths (non-ASCII, quotes, tabs) and paths containing ` b/` are read correctly instead of being skipped or read as another file; a name holding a backslash is reported as it is, never inherits a sibling path's exception, and is in scope of every path-scoped rule. Changed binaries are read by object id, so a name shaped like an index stage (`0:name`) is read as itself. Added lines are taken from each hunk by its own line counts, so an added line that itself begins with `+`, such as a Markdown bullet, is scanned. Text that the repository's attributes declare binary (UTF-8 without a NUL byte in its first 8 KiB) was not scanned at all; it is now judged by its added lines like any other text, with renames and copies paired as in the main diff, and other content declared binary is scanned as a binary, as before. A changed file the scan cannot read from the diff is reported as `content-scan-incomplete` and blocks, where before it was skipped; a Git setting not pinned here can therefore stop a commit but cannot hide content.
|
|
29
|
+
- The boundary pre-commit check judges what is being committed. `git commit -a` and `git commit <paths>` hand the hook a temporary index, and a commit from a linked worktree its own index; the check dropped it with the rest of the Git environment, read the configured checkout's index and saw nothing staged. It now reads the index and work tree Git names, for the managed repository being committed, found by Git even from a hook chained into a subdirectory, and blocks when no single declared repository is being committed (`pre-commit-repo-unresolved`), as the push check does. Staged paths are listed NUL-separated, so a quoted name (non-ASCII, quotes) is matched against forbidden-path patterns, and a repository whose staged paths cannot be listed blocks. The semantic-field review reads the staged diff with the same pinned format and parser as the content scan, reads a document Git would call binary as text, blocks on a diff it cannot read, and takes only an added review marker as approval: a removed marker, or one in a file name, approves nothing; `atelier upgrade` applies the same marker rule. A forbidden-path pattern's `**` now also matches across a newline in a name.
|
|
30
|
+
- In the boundary content scan, a file that takes the name of a directory, or moves into a directory of its own name, in text that Git reports as binary, is judged as the main diff pairs it instead of blocking the commit. When such text is still unreadable the message says Git reports it as binary, which also covers text replacing binary content.
|
|
31
|
+
- The boundary push check judges the commits a push carries that the remote's tracking refs do not hold, each against its parent, binaries included, so a key added in one commit and removed in the next is no longer published unscanned; such a finding names its commit, and a key that reaches the pushed tip is reported once, listing the commits that carry it. A merge is judged only for what is new against all its parents, so a pull merge into the branch you push is not refused for what the remote already holds. The pre-push hook now passes the remote Git names (a configured remote, or a URL matched to one); reinstall hooks (`atelier boundary install-hooks`) so history held only by another remote is judged on its way to a new one. A push carrying more than 1000 such commits or more than the evidence budget is refused with `content-scan-incomplete`; push it in smaller parts. The content scan's diffs omit a deleted file's removed lines, which it never judges (the semantic-field review still reads them), so deleting a large file no longer exhausts the output limit. A new branch in a SHA-256 repository is diffed against that format's empty tree instead of failing.
|
|
32
|
+
|
|
33
|
+
- `atelier upgrade closure --npm-root ABSOLUTE_NPM_ROOT` plans a check of a staged
|
|
34
|
+
candidate's own dependency closure, and `planConsumerClosure` is exported from
|
|
35
|
+
`@mnstry/atelier/upgrade`. The plan reads the candidate's `package.json` and
|
|
36
|
+
`package-lock.json`, the installed manifest of every lockfile copy of the
|
|
37
|
+
selected package, `package.json` in each directory above the npm root (to
|
|
38
|
+
refuse a parent that declares workspaces) and, for the default package name,
|
|
39
|
+
Atelier's own `package.json`. It runs no npm command, uses no network, reads
|
|
40
|
+
no npm configuration and writes nothing. It reports the lockfile's sources,
|
|
41
|
+
registry hosts and schemes, and platform exclusions. It checks the selected
|
|
42
|
+
package's exact `overrides` against every installed copy, only after binding
|
|
43
|
+
each installed manifest to the name and version of its lockfile entry, and
|
|
44
|
+
lists selector, nested and non-exact overrides, and overrides for packages a
|
|
45
|
+
link provides, as unchecked. The npm root must be the directory's real path.
|
|
46
|
+
Refusals reuse `usage`, `consumer-closure-incomplete` and
|
|
47
|
+
`override-not-inherited`, each with one bounded reason. Every run prints one
|
|
48
|
+
JSON document, including a run whose arguments are refused. A completed plan
|
|
49
|
+
exits 3 with `"proof": "not-run"` and a refusal exits 2; the command never
|
|
50
|
+
exits 0 and does not prove an offline reinstall. The consumer-closure
|
|
51
|
+
classifiers moved to `src/upgrade/closure-diagnostics.mjs`, and
|
|
52
|
+
`scripts/consumer-closure-diagnostics.mjs` re-exports them. Two of them
|
|
53
|
+
changed: a project folder entry in a lockfile is never counted as an
|
|
54
|
+
installed copy, and a lockfile entry that is not an object is reported as
|
|
55
|
+
missing by `capturedClosure` instead of raising a `TypeError`.
|
|
56
|
+
|
|
57
|
+
- Add a tenant-neutral HTML control system at `@mnstry/atelier/ui`. The shared
|
|
58
|
+
geometry, variants, focus treatment, motion, action-group layouts, and safe
|
|
59
|
+
renderers are used by Atelier's generated project UI and are available to
|
|
60
|
+
downstream publication kits without importing publication semantics into
|
|
61
|
+
Atelier.
|
|
62
|
+
- The Obsidian service test suites reserve the loopback ports they choose, so
|
|
63
|
+
another test cannot take a port between its selection and its use.
|
|
64
|
+
- The Obsidian plugin (1.1.2) keeps the last authenticated status in view
|
|
65
|
+
through a transport interruption. After a connection refusal or timeout on
|
|
66
|
+
the same channel, the status window shows the last observed state, reason,
|
|
67
|
+
generation and check under "Last observed" labels. Those rows are historical
|
|
68
|
+
and never establish current freshness, service health or permission. The
|
|
69
|
+
history stays in memory and is cleared on a channel change, an
|
|
70
|
+
authentication refusal, an untrusted or malformed answer, a report for
|
|
71
|
+
another scope, or unload. A republish that names a different channel resets
|
|
72
|
+
the display to `connecting` without repeating an unchanged notice, and the
|
|
73
|
+
plugin opens no channel before startup admits it or after unload.
|
|
74
|
+
- The Obsidian maintenance service tells a plugin a view's stored freshness
|
|
75
|
+
and pending-edit count only while the project allows that view at the moment
|
|
76
|
+
of the request: its Obsidian settings are enabled and declare the view, and
|
|
77
|
+
its workspace pointer still names the workspace the service runs for.
|
|
78
|
+
Before, a view that was turned off or taken out of the settings was still
|
|
79
|
+
told what the service had stored, until the next tick and, for its
|
|
80
|
+
generations and counts, after it. The answer is now sealed as before with
|
|
81
|
+
`view` and `pendingEdits` null, so the plugin shows `Atelier: stale`, keeps
|
|
82
|
+
its session and asks again; anything that cannot be established (settings
|
|
83
|
+
that refuse, a project that does not load) is answered the same way. The
|
|
84
|
+
project is loaded again only when a file that decides it changed, compared
|
|
85
|
+
by content at every request. A loader that answers a promise is never
|
|
86
|
+
waited for: its view is told nothing until the load has answered.
|
|
87
|
+
- The real-app acceptance run of the small-fixture desktop procedure
|
|
88
|
+
(`scripts/obsidian/desktop-receipts.mjs`, maintainer tooling that is not
|
|
89
|
+
shipped) ends the processes it started, removes the temporary directories it
|
|
90
|
+
created, and bounds the receipt and console output it keeps. It signals only
|
|
91
|
+
a process it spawned or a recorded child of one, and removes a directory only
|
|
92
|
+
while the path still names the one it created; otherwise it keeps the
|
|
93
|
+
directories and names them.
|
|
94
|
+
- A project configuration file that is a link (one kept in a dotfiles
|
|
95
|
+
repository, say) no longer makes the Obsidian maintenance service load the
|
|
96
|
+
project, and run Git, at every status request a plugin makes. The service
|
|
97
|
+
compares what the loader reads through the link: the file it leads to and
|
|
98
|
+
that file's bytes, so a link pointed elsewhere is a change. A folder in a
|
|
99
|
+
file's place, or a link that leads nowhere or to a folder, still vouches for
|
|
100
|
+
nothing. The project the service keeps for those requests is taken only from
|
|
101
|
+
two loads in a row that agree over the same bytes, is loaded again every two
|
|
102
|
+
and a half minutes and never used past five. A loader that answers a promise
|
|
103
|
+
is given up after two and a half minutes, and a load that fails is asked for
|
|
104
|
+
again after two seconds, then four, up to thirty, unless a file changed.
|
|
105
|
+
- The Obsidian maintenance service refuses to start on a project loader that
|
|
106
|
+
answers a promise (or any other thenable), with
|
|
107
|
+
`service-loader-not-synchronous`, before it reads anything from it, listens
|
|
108
|
+
or records anything. Before, it handed the promise to its workspace
|
|
109
|
+
resolution, which then read the workspace pointer of the working directory
|
|
110
|
+
and protected no repository. For the status read of a plugin: a load whose
|
|
111
|
+
answer comes after its deadline is given up even when no request saw the
|
|
112
|
+
deadline pass; a link is resolved as the system resolves it, a `..` after a
|
|
113
|
+
link included, and a link to a folder above a configuration file pointed
|
|
114
|
+
elsewhere is a change; two loads agree when what the decision reads of the
|
|
115
|
+
project is the same, so a field that differs between loads (a time, a
|
|
116
|
+
binding under a symbol key) no longer stops a project from being kept; and
|
|
117
|
+
a load that fails, is given up or holds the event loop is logged with how
|
|
118
|
+
long it took.
|
|
119
|
+
- The Obsidian maintenance service stays responsive while it prepares and
|
|
120
|
+
publishes a view. The engine prepares each view cooperatively and the
|
|
121
|
+
publisher yields between complete units, so the loopback listener answers
|
|
122
|
+
health and status while notes are emitted, staged and published, instead of
|
|
123
|
+
being `busy` for the length of a large first publication. `open` and
|
|
124
|
+
`service stop` therefore reach a service in the middle of a publication:
|
|
125
|
+
`open` waits for a tick that starts after the one in flight, and `service
|
|
126
|
+
stop` ends the service after the tick in flight or its shutdown grace,
|
|
127
|
+
leaving a publication cut short to the publisher's restart recovery.
|
|
128
|
+
Walking the directories, resolving links and assembling the canonical
|
|
129
|
+
graph, capturing the source snapshot, the
|
|
130
|
+
publisher's restart recovery, validating a prepared view and planning its
|
|
131
|
+
units, the exchange probe, writing and syncing the journal before the
|
|
132
|
+
candidates move and the look again at displaced files after the units
|
|
133
|
+
still run without yielding. So did reading the sources into the canonical
|
|
134
|
+
graph, observing the source and vault files and the read-back after a
|
|
135
|
+
commit, until later changes made them wait between files (for the graph,
|
|
136
|
+
between Markdown sources, outside a personal workspace).
|
|
137
|
+
`prepareView` stays synchronous
|
|
138
|
+
and returns the same bytes. Across each wait of preparation and publication
|
|
139
|
+
the configuration files, the machine settings and the eligibility revision
|
|
140
|
+
are checked again, and after
|
|
141
|
+
a view is prepared and before its generation commits every selected source
|
|
142
|
+
is checked as observation checks it: by its stat hint, and by digest where
|
|
143
|
+
the hint moved. A change during the tick refuses the generation as
|
|
144
|
+
`mixed-read`; neither that tick nor a later restart recovery commits it,
|
|
145
|
+
and the next tick prepares the view again. A source that drifts under an
|
|
146
|
+
unchanged stat hint is still seen by the next full reconciliation, and a
|
|
147
|
+
note reused from the preparation cache is still not read. A cancelled
|
|
148
|
+
preparation leaves the preparation cache as it was, and publication keeps
|
|
149
|
+
its locks, journal and conditional writes.
|
|
150
|
+
- Started by a login item, the Obsidian maintenance service asks its project
|
|
151
|
+
loader once, and a loader that answers a promise there is refused and
|
|
152
|
+
recorded as at any other start, ending with 0. Before, the promise reached
|
|
153
|
+
the workspace resolution, and a refused one ended the process with an error
|
|
154
|
+
that the service manager answered by starting it again. The loads behind a
|
|
155
|
+
plugin's status requests are counted in the service's status document
|
|
156
|
+
(`statusLoads`), and the service log gets a `status-project-loaded` line
|
|
157
|
+
only for a load that failed or was given up (the first in a row, then when
|
|
158
|
+
the row is 2, 4, 8 and so on long), the first that answered after a failure,
|
|
159
|
+
and one that held the event loop for a second or more. A regular
|
|
160
|
+
configuration file the system resolves no path for (some Windows volumes)
|
|
161
|
+
is compared by its own path and bytes, instead of the project being loaded
|
|
162
|
+
at every request.
|
|
163
|
+
- A publication of the Obsidian maintenance service that is stopped or refused
|
|
164
|
+
on the way is recorded and cleaned up as what it was. A caller's stop after
|
|
165
|
+
the journal was opened (a setting that changed during the tick,
|
|
166
|
+
`mixed-read`, or a cancellation) is recorded in the journal's last entry
|
|
167
|
+
under that stop's own code instead of `publisher-error`; restart recovery
|
|
168
|
+
reads such a journal as before. A configuration file that cannot be read
|
|
169
|
+
again at a wait (no permission, an I/O error, no descriptor left) refuses
|
|
170
|
+
that view as `mixed-read` and has the next tick read everything again;
|
|
171
|
+
before, the raw error ended the tick, and the views after that one were not
|
|
172
|
+
prepared. A refusal raised while candidates are being staged (the vault's
|
|
173
|
+
folder replaced or gone) is still reported under its own code, now after
|
|
174
|
+
the candidates it staged are removed; before, those staged ahead of the
|
|
175
|
+
journal stayed in private staging, where restart recovery never looks. And
|
|
176
|
+
the service reads an explicit `prepareViewCooperatively: null` among its
|
|
177
|
+
engine seams as the engine does: there is no cooperative preparation, so a
|
|
178
|
+
replaced `prepareView` prepares the view.
|
|
179
|
+
- The desktop acceptance procedures (`scripts/obsidian/desktop-receipts.mjs`,
|
|
180
|
+
maintainer tooling that is not shipped) no longer signal the isolated app
|
|
181
|
+
by its process number outside the small-fixture run: quitting signals it
|
|
182
|
+
through the handle spawn returned and waits on that handle, a launch that
|
|
183
|
+
gives up signals the app's process group only while that handle still
|
|
184
|
+
holds the app, and a launch under the user's own HOME is refused before the
|
|
185
|
+
stale command-line socket there is removed. In the small-fixture run, a
|
|
186
|
+
process first recorded from a process table passes lineage on only from the
|
|
187
|
+
next read; a process the table shows, unrecorded, under a parent that
|
|
188
|
+
exited or changed during the read is remembered until it is seen gone, and
|
|
189
|
+
the directories are kept while it is there; and the app log copy's credit
|
|
190
|
+
is charged back when a directory is kept after all, with the excess over
|
|
191
|
+
the output bound's allowance reported.
|
|
192
|
+
- `atelier obsidian`, the Obsidian maintenance service and its login item take
|
|
193
|
+
`--personal-home DIR` instead of `--project`, and then follow the generation
|
|
194
|
+
last confirmed in that private home of a personal workspace. The head of the
|
|
195
|
+
selection history is read, that generation is loaded (composed in a worker
|
|
196
|
+
for the service), and the head is read again; a head that moved refuses
|
|
197
|
+
`personal-selection-changed`. Nothing confirmed publishes nothing, each with
|
|
198
|
+
its own code (`nothing-selected`, `stale-generation`,
|
|
199
|
+
`selection-history-corrupt`, `personal-home-unavailable`, or the module's own
|
|
200
|
+
code for a home that is gone), and nothing falls back to another project.
|
|
201
|
+
The service locates its workspace from the home alone through a new
|
|
202
|
+
`locateProject` option, so it starts whatever the selection; a service given
|
|
203
|
+
only a loader that answers a promise is still refused at start. The engine
|
|
204
|
+
observes the selection's record and the next one, so a confirmation or a
|
|
205
|
+
history cut short is loaded at the next tick, and reads the history again at
|
|
206
|
+
every full reconciliation. `status` reports the confirmed generation, and
|
|
207
|
+
nothing here confirms one. Who may see a bound vault is not decided: it stays
|
|
208
|
+
the machine's audience setting, none by default. `readPersonalSelectionHead`
|
|
209
|
+
is added to `@mnstry/atelier/personal-workspace`, and the selection history is
|
|
210
|
+
read without waiting on a record that is not a regular file. Without
|
|
211
|
+
`--personal-home` nothing changes.
|
|
212
|
+
- The desktop acceptance procedures (`scripts/obsidian/desktop-receipts.mjs`,
|
|
213
|
+
maintainer tooling that is not shipped) interrupt AP-03's service only
|
|
214
|
+
after a healthy status, and only through the handle of the service process
|
|
215
|
+
the run started; any other number is refused and the step fails. A launch
|
|
216
|
+
is refused unless the instance's HOME lies inside a layout root the run
|
|
217
|
+
made and is not the user's own home, however it is reached. In the
|
|
218
|
+
small-fixture run, cleanup reads the process table until a read records
|
|
219
|
+
nothing new before its first signal, and the run records how many
|
|
220
|
+
processes one command-line call becomes. The event-loop benchmark
|
|
221
|
+
(`scripts/obsidian/benchmark-event-loop.mjs`) signals its child's process
|
|
222
|
+
group only while it still holds the child.
|
|
223
|
+
|
|
224
|
+
- AP-03 reports a refused interruption without losing its failure evidence.
|
|
225
|
+
Separately, small-fixture cleanup remembers a child observed under a run-owned
|
|
226
|
+
process that exited or changed during the process-table read, then remembers
|
|
227
|
+
descendants of those children. It retains its temporary roots while those
|
|
228
|
+
remembered processes remain. It also reserves estimated time for SIGTERM
|
|
229
|
+
grace after reading the process tree.
|
|
230
|
+
- AP-03 test cleanup runs before temporary-workspace removal. It joins children
|
|
231
|
+
through retained handles even when the service record is missing or malformed
|
|
232
|
+
or graceful stop fails. Unheld launcher services use the existing forced-stop
|
|
233
|
+
fallback, which rechecks healthy runtime identity before signalling the PID.
|
|
234
|
+
A held child that does not exit within the cleanup bound fails the test.
|
|
235
|
+
|
|
3
236
|
## 0.2.0-alpha.13
|
|
4
237
|
|
|
5
238
|
- Add the public Knowledge Health workshop on the existing local Knowledge
|
package/README.md
CHANGED
|
@@ -94,7 +94,7 @@ governed projections
|
|
|
94
94
|
You can see the complete loop in a disposable sample workspace:
|
|
95
95
|
|
|
96
96
|
```bash
|
|
97
|
-
npm install --save-dev @mnstry/atelier@0.2.0-alpha.
|
|
97
|
+
npm install --save-dev @mnstry/atelier@0.2.0-alpha.14
|
|
98
98
|
npx mnstry-atelier init --fixture=sample-workspace --target ./sample
|
|
99
99
|
npx mnstry-atelier graph --project ./sample/atelier.project.json
|
|
100
100
|
npx mnstry-atelier project --project ./sample/atelier.project.json
|
|
@@ -276,6 +276,23 @@ if (graph.errors.length > 0) throw new Error(graph.errors.join('\n'))
|
|
|
276
276
|
console.log(graph.nodes)
|
|
277
277
|
```
|
|
278
278
|
|
|
279
|
+
Interfaces that need the same control grammar as Atelier's generated tooling
|
|
280
|
+
can import the additive UI subpath. The stylesheet contains no brand copy or
|
|
281
|
+
publication semantics; map its `--system-control-*` properties to the
|
|
282
|
+
consumer's own theme:
|
|
283
|
+
|
|
284
|
+
```js
|
|
285
|
+
import {
|
|
286
|
+
atelierControlStyles,
|
|
287
|
+
renderActionGroup,
|
|
288
|
+
} from '@mnstry/atelier/ui'
|
|
289
|
+
|
|
290
|
+
const navigation = renderActionGroup([
|
|
291
|
+
{ label: 'Library', href: '/library/' },
|
|
292
|
+
{ label: 'Concepts', href: '/concepts/' },
|
|
293
|
+
], { label: 'Browse the workspace' })
|
|
294
|
+
```
|
|
295
|
+
|
|
279
296
|
The package exports change over the alpha series, so pin the exact prerelease
|
|
280
297
|
version and treat the package export map and shipped source modules as the
|
|
281
298
|
executable API reference. [Distribution contracts](./docs/distributions.md) explain
|
|
@@ -307,7 +324,7 @@ Installation, host discovery and successful exercise are separate evidence.
|
|
|
307
324
|
Content-free observations can guide improvement, and draft graph records keep
|
|
308
325
|
provenance connected to adoption and outcomes. Publisher authentication, live
|
|
309
326
|
host evaluation and human acceptance require their own evidence. This source
|
|
310
|
-
capability
|
|
327
|
+
capability has shipped as the `capability` command since 0.2.0-alpha.13.
|
|
311
328
|
<!-- atelier:block:capability-stewardship:end -->
|
|
312
329
|
|
|
313
330
|
See [capability stewardship](docs/capability-stewardship.md) for the operating guide.
|
|
@@ -481,7 +498,7 @@ Node.js 22 (22.18.0 or later) or Node.js 24 (24.13.1 or later) is required.
|
|
|
481
498
|
Pin the prerelease while the package remains in alpha:
|
|
482
499
|
|
|
483
500
|
```bash
|
|
484
|
-
npm install --save-dev @mnstry/atelier@0.2.0-alpha.
|
|
501
|
+
npm install --save-dev @mnstry/atelier@0.2.0-alpha.14
|
|
485
502
|
```
|
|
486
503
|
|
|
487
504
|
Then choose the path that matches what you are building:
|
|
@@ -500,7 +517,7 @@ Then choose the path that matches what you are building:
|
|
|
500
517
|
|
|
501
518
|
## Status and command reference
|
|
502
519
|
|
|
503
|
-
Current package: `@mnstry/atelier@0.2.0-alpha.
|
|
520
|
+
Current package: `@mnstry/atelier@0.2.0-alpha.14`.
|
|
504
521
|
|
|
505
522
|
The alpha package is usable and contract-tested, but its library API may still
|
|
506
523
|
change before a stable release. Pin the exact version in production toolchains.
|
|
@@ -578,7 +595,7 @@ attempts and distinct verification gates. Its coordination proposal can be read
|
|
|
578
595
|
by Fabric or another adapter. The shared harness contract connects these workflows
|
|
579
596
|
to Discovery, Research and Skill Steward without replacing their domain records.
|
|
580
597
|
|
|
581
|
-
These are local, provider-neutral
|
|
598
|
+
These are local, provider-neutral capabilities, shipped since 0.2.0-alpha.13.
|
|
582
599
|
Handoffs preserve exact pins, and explicit source snapshots reveal corrections
|
|
583
600
|
that require downstream reconsideration. Graph outputs remain draft proposals.
|
|
584
601
|
Recorded acceptance is not authenticated authority, and local checks do not prove
|
|
@@ -14,4 +14,4 @@ Installation, host discovery and successful exercise are separate evidence.
|
|
|
14
14
|
Content-free observations can guide improvement, and draft graph records keep
|
|
15
15
|
provenance connected to adoption and outcomes. Publisher authentication, live
|
|
16
16
|
host evaluation and human acceptance require their own evidence. This source
|
|
17
|
-
capability
|
|
17
|
+
capability has shipped as the `capability` command since 0.2.0-alpha.13.
|
|
@@ -10,7 +10,7 @@ attempts and distinct verification gates. Its coordination proposal can be read
|
|
|
10
10
|
by Fabric or another adapter. The shared harness contract connects these workflows
|
|
11
11
|
to Discovery, Research and Skill Steward without replacing their domain records.
|
|
12
12
|
|
|
13
|
-
These are local, provider-neutral
|
|
13
|
+
These are local, provider-neutral capabilities, shipped since 0.2.0-alpha.13.
|
|
14
14
|
Handoffs preserve exact pins, and explicit source snapshots reveal corrections
|
|
15
15
|
that require downstream reconsideration. Graph outputs remain draft proposals.
|
|
16
16
|
Recorded acceptance is not authenticated authority, and local checks do not prove
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# Capability stewardship
|
|
2
2
|
|
|
3
|
-
Status:
|
|
4
|
-
|
|
5
|
-
`node bin/atelier.mjs capability ...`; the examples below use the installed
|
|
6
|
-
command spelling for a package built from this source.
|
|
3
|
+
Status: shipped since 0.2.0-alpha.13 as the `capability` command. The examples
|
|
4
|
+
below use the installed command spelling.
|
|
7
5
|
|
|
8
6
|
Atelier can package a useful workflow, introduce it alongside a person's
|
|
9
7
|
existing skills, and keep each repository's adoption explainable over time.
|
package/docs/ingestion.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Progressive local ingestion
|
|
2
2
|
|
|
3
|
-
Status: experimental
|
|
3
|
+
Status: experimental. Shipped since 0.2.0-alpha.13 as the `atelier ingest`
|
|
4
|
+
command and the `@mnstry/atelier/ingestion` export.
|
|
4
5
|
The local runner preserves explicitly selected files and makes bounded text
|
|
5
6
|
search useful before semantic interpretation. It composes the existing
|
|
6
7
|
[immutable intake](intake-and-guides.md) with a resumable plan. It never enrolls
|
|
@@ -16,8 +17,7 @@ traversal, symlinks and implicit external reads are refused. The local CLI is
|
|
|
16
17
|
for a trusted workspace owner. Its workspace identifier is a binding against
|
|
17
18
|
accidental cross-workspace reuse, not an authenticated identity or tenant ACL.
|
|
18
19
|
|
|
19
|
-
The following examples
|
|
20
|
-
version containing this command. Released alpha.7 does not contain it.
|
|
20
|
+
The following examples need 0.2.0-alpha.13 or later.
|
|
21
21
|
|
|
22
22
|
```sh
|
|
23
23
|
atelier ingest plan <<'JSON'
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Discovery Harness and Research Harness
|
|
2
2
|
|
|
3
|
-
Status:
|
|
4
|
-
|
|
5
|
-
source checkout, or `atelier inquiry help` from a package built from it.
|
|
3
|
+
Status: shipped since 0.2.0-alpha.13. Run `atelier inquiry help` for the
|
|
4
|
+
commands.
|
|
6
5
|
|
|
7
6
|
Discovery frames questions, compares explanations and prepares decisions.
|
|
8
7
|
Research gathers and checks evidence and returns a portable bundle. People set
|
package/docs/install.md
CHANGED
|
@@ -34,19 +34,19 @@ The registry is the distribution channel of record, as `docs/continuity.md`
|
|
|
34
34
|
commits:
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
npm install --save-dev @mnstry/atelier@0.2.0-alpha.
|
|
37
|
+
npm install --save-dev @mnstry/atelier@0.2.0-alpha.14
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
Installing from the matching Git tag resolves to the same reviewed commit:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
npm install --save-dev "git+https://github.com/MNSTRY/atelier.git#v0.2.0-alpha.
|
|
43
|
+
npm install --save-dev "git+https://github.com/MNSTRY/atelier.git#v0.2.0-alpha.14"
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
Or over SSH:
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
npm install --save-dev "git+ssh://git@github.com/MNSTRY/atelier.git#v0.2.0-alpha.
|
|
49
|
+
npm install --save-dev "git+ssh://git@github.com/MNSTRY/atelier.git#v0.2.0-alpha.14"
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
Keep the `@mnstry/` scope — see the command-form note above for why the
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# Knowledge development and the learning cycle
|
|
2
2
|
|
|
3
|
-
Status:
|
|
4
|
-
|
|
5
|
-
checkout, or `atelier harness help` from a package built from this candidate.
|
|
3
|
+
Status: shipped since 0.2.0-alpha.13. Run `atelier harness help` for the
|
|
4
|
+
commands.
|
|
6
5
|
|
|
7
6
|
Knowledge Harness helps people establish, develop, use and revise what they know.
|
|
8
7
|
Ingestion is one capability within that larger purpose. Sources can also originate
|
package/docs/local-services.md
CHANGED
|
@@ -203,8 +203,19 @@ the child it created. If that proof never arrives it stops only that child, by
|
|
|
203
203
|
its process handle, and reports the private operational log,
|
|
204
204
|
`state/service/service.log`.
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
Stretches of a tick are synchronous (walking the directories, reading the
|
|
207
|
+
HTML sources and the sidecars of PDF and Word documents, resolving links and
|
|
208
|
+
assembling the canonical graph, building the whole graph of a project bound to
|
|
209
|
+
a personal workspace, capturing the source snapshot, the
|
|
210
|
+
publisher's restart recovery, its exchange probe, writing the journal, the
|
|
211
|
+
look again at displaced files), so a healthy service in a long one may not
|
|
212
|
+
answer health within the deadline; observing the vault and source files before
|
|
213
|
+
a view is prepared, reading the Markdown sources into the canonical graph,
|
|
214
|
+
preparing
|
|
215
|
+
and publishing a view, and reading it back after a commit yield between files
|
|
216
|
+
and between publication units, and health is answered meanwhile. A service
|
|
217
|
+
that does not answer in time is `busy`, not
|
|
218
|
+
`occupied`: it is
|
|
208
219
|
never adopted, never stopped and never started over, and a `start` whose own
|
|
209
220
|
child went straight into a long first tick reports it as started and busy
|
|
210
221
|
instead of ending it. The command line of another process is read from
|
|
@@ -268,7 +279,35 @@ A tick that fails for a reason nobody typed (a full disk, a crash inside a
|
|
|
268
279
|
step) does not end the service. Its error code is kept in
|
|
269
280
|
`state/service/last-error.json`, its message only in the private log; the
|
|
270
281
|
delay before the next attempt doubles up to a ceiling, and the next tick
|
|
271
|
-
proceeds.
|
|
282
|
+
proceeds. It starts as the first tick of the service does, loading the project
|
|
283
|
+
again and looking at every view, so nothing the failed tick saw and did not
|
|
284
|
+
act on is lost. A tick whose engine lock could not be given back at its end
|
|
285
|
+
(its released ticket could not be written) fails too: its error is recorded
|
|
286
|
+
and the loop backs off, and its work was already recorded before the release.
|
|
287
|
+
The lock stays this service's, and the next tick gives it back before it takes
|
|
288
|
+
it again, instead of finding its own ticket and answering `busy` until the
|
|
289
|
+
service is started again.
|
|
290
|
+
|
|
291
|
+
A view refused for a file-system error does not fail the tick. Examples are
|
|
292
|
+
a vault folder that could not be allocated or placed (`vault-location-unusable`),
|
|
293
|
+
a configuration file that could not be read again during the tick, a source
|
|
294
|
+
removed while it was read (both `mixed-read`) and candidates that could not be
|
|
295
|
+
staged (`staging-failed`). The view's freshness names only the refusal, so the
|
|
296
|
+
service writes a `view-refused` line to its private log for each such view
|
|
297
|
+
whose refusal carries a file-system error's code, at most 64 a tick: the view,
|
|
298
|
+
the refusal's code and the error's code (`EACCES`, `EIO`, `ENOENT`, `ENOSPC`).
|
|
299
|
+
Only codes shaped as a file-system error's are written, never a message. A view
|
|
300
|
+
the next tick names for the same reason is not written again, so a folder that
|
|
301
|
+
stays unwritable (tried at every tick) is written once. It is written again when
|
|
302
|
+
its reason changes, or after a tick that failed or did not name it: a tick that
|
|
303
|
+
did not refuse it, refused it for no file-system error, had named 64 other
|
|
304
|
+
views first, or was refused as a whole. When removing the staged candidates
|
|
305
|
+
afterwards fails too, the line also names that error's code as `cleanupCause`,
|
|
306
|
+
and a line is written for that alone when the refusal names no error of its
|
|
307
|
+
own. That failure does not replace the refusal. What could not be removed
|
|
308
|
+
stays where it was: in `staging/`, or at its exchange path under `recovery/`
|
|
309
|
+
once the publication's journal is written. The restart recovery of the next
|
|
310
|
+
publication removes the latter.
|
|
272
311
|
|
|
273
312
|
### Cases that need a person
|
|
274
313
|
|
|
@@ -665,6 +665,29 @@ again, so bytes that drift under an unchanged pinned digest are not seen by
|
|
|
665
665
|
describes, and the next observation by digest sees the drift. A note whose pin
|
|
666
666
|
changed is read and verified against the pin as always.
|
|
667
667
|
|
|
668
|
+
The maintenance engine prepares a view cooperatively (the
|
|
669
|
+
`prepareViewCooperatively` production seam): it waits between notes so that
|
|
670
|
+
the service keeps answering. The bound above holds there too. After a
|
|
671
|
+
cooperative preparation, and again while the publisher holds its locks,
|
|
672
|
+
before the journal entry that says the publication settled and before the
|
|
673
|
+
commit, the engine checks every selected source as observation does: a source
|
|
674
|
+
that still has the stat hint it was indexed with is taken as unchanged and is
|
|
675
|
+
not opened, and one whose hint moved is read and must still be the pinned
|
|
676
|
+
bytes. A source that changed during the tick, or was removed, refuses the
|
|
677
|
+
generation as `mixed-read`: its manifest is not committed, at that tick or by the restart
|
|
678
|
+
recovery of a later one, and the next tick observes in full and prepares the
|
|
679
|
+
view again. The same waits check that the configuration files, the machine
|
|
680
|
+
settings and the eligibility revision are still the ones the tick read. A
|
|
681
|
+
configuration file that cannot be read at such a check (no permission, an I/O
|
|
682
|
+
error) refuses the view as `mixed-read` too, and the tick goes on to its
|
|
683
|
+
other views.
|
|
684
|
+
|
|
685
|
+
Called on its own, a cooperative preparation reads every selected source
|
|
686
|
+
against its pin once more before it returns, a reused note's included, unless
|
|
687
|
+
its caller says it checks them itself (`scheduling.recheckSources: false`, as
|
|
688
|
+
the engine does). A preparation refused or cancelled on the way leaves the
|
|
689
|
+
preparation cache as it was.
|
|
690
|
+
|
|
668
691
|
The engine holds one graph file cache and one preparation cache per scope for
|
|
669
692
|
its lifetime and hands them through the `createGraphCache` and
|
|
670
693
|
`createPreparationCache` production seams; a test replaces either with
|