docs: add Dewey row 131 r1 packet (plug-in kind registry and loader)

Candidate packet for #1626: build.patch on 0bdd573ff, candidate manifest,
targeted gate (588/0 over plugins, business, bus, webui, cli) and 42/42
mutants killed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-15 11:23:33 -05:00
co-authored by Claude Opus 5.5
parent 6daf6ac74c
commit 09447eb677
58 changed files with 5511 additions and 0 deletions
+122
View File
@@ -0,0 +1,122 @@
# Row 131 / #1626: the plug-in layer, kind registry and loader (round 1)
Builder: Dewey. Brief: `docs/plans/2026-10-14_prd-1.2-build.md`, step 6b.
Requirements: REQ-PLUG-1 (the plug-in layer), REQ-LIMIT-4 (the first two kinds,
usage-source and limit-check). The `plugins` write goes through step 5's write
path (REQ-ROLE-3). Base: see `base.txt`. Source is in a detached worktree; nothing
here is pushed, wired into a host process or written to Jason's organization file.
Governing rulings: lead decision 143 (in-repo modules only until Jason rules on
Vikunja #286; no path from config) and sage-0301 (choices 1, 3, 4 and 5 as
proposed; choice 2 changed).
## What ships
| Path | Change |
| --- | --- |
| `packages/plugins/package.json` | new package `@mosaic/plugins` |
| `packages/plugins/src/kinds.mjs` | kind registry: usage-source and limit-check, interface version 1, each switched on by a list; `checkPlugins` for the organization file's `plugins` key |
| `packages/plugins/src/loader.mjs` | `createLoader({log})` → `{load, loaded, modules}` |
| `packages/plugins/README.md` | kinds, export contract, adding a module, the key, the loader and its limits |
| `packages/plugins/tests/{helpers,kinds.test,loader.test}.mjs` | 19 tests |
| `packages/business/src/business.mjs` | `plugins` in TOP_KEYS; checked with `checkPlugins`; returned frozen (`{}` when absent) |
| `packages/business/src/plugin-settings.mjs` | the host setter: the only writer of `plugins`, through `organizationWriter` |
| `packages/business/tests/plugin-settings.test.mjs` | 3 tests: schema, setter success and log, setter refusals |
| `packages/business/README.md` | the `plugins` key and its writer |
| `scripts/stack-copy.sh` | whole `packages/plugins` in COPY_PATHS (approved, sage-0301) |
| `packages/cli/tests/stack-copy.test.mjs` | the copied-path list includes the plug-in sources |
No module of either kind ships. No core module imports a plug-in module by
name: `business.mjs` and `plugin-settings.mjs` import only `kinds.mjs`, and
nothing imports `loader.mjs` outside its tests. `organization.mjs` is untouched.
No limit ceiling is in code (Q150).
## sage-0301 rulings, as built
1. **One writer.** `pluginSettings({dir, rolesDir}).set(id, {revision, plugins}, actor)`
is the only writer. No broker verb, mosaic command, Settings form or
chief-of-staff write was added.
2. **A refused write throws a code and logs nothing.** `set` checks the request
shape (`invalid-request`: anything but `revision` and `plugins`, or a revision
that isn't 64 hex) and the value (`invalid-plugins`, from `checkPlugins`)
before the writer runs. The writer validates the whole document before its
`prepared` entry, so nothing reaches `changes.jsonl`. The test checks the
file bytes are unchanged and the log is empty after eleven refusals. A
committed write logs actor, transaction and old and new names per kind, the
same way a limit change does (`plugins` with old null when the key was
absent; `plugins.<kind>` with whole old and new lists after that).
Load-time refusals go through the loader's `log` callback.
3. **Last good modules per kind.** A loader instance holds, per kind, the
modules from its last good load. Any refusal for a kind keeps them. A kind
refused on its first load has nothing on. The test loads `["meter","absent"]`
so a half-loaded list would show. `load()` and `loaded()` report what each
kind actually has on. Every refusal calls `log` once with
`{event: "plugin-refused", kind, name, code, message, kept}`; the first-load
test checks kind and name. The loader is not wired into any host process.
4. **Export contract.** Every module exports `interfaceVersion`; usage-source
exports `readTurn`; limit-check exports `mayLaunch` and `mustStop`; both kinds
switch on a list. The README carries the contract. These names bind row 124.
Argument and return shapes are left to the calling rows.
5. **What is checked.** `packages/plugins`, `<kind>`, `<name>` (each an lstat:
exists, a directory, not a symlink) and `index.mjs` (exists, a regular file,
not a symlink); not the ancestors of `packages/plugins`. Just before
`import`, `realpath(index.mjs)` must equal
`realpath(packages/plugins)/<kind>/<name>/index.mjs`, and the import uses that
expected path.
## The check-then-import gap
For Darkwing, in those words: there is a check-then-import gap. The lstat
checks and the realpath comparison run, then `import()` opens the file by path.
Someone who can write `packages/plugins` as the host's user can swap
`index.mjs` (or a directory above it) between the realpath check and the open
inside `import()`. Node has no import-from-descriptor, so the loader cannot
close the gap. The checks catch mistakes, stale links and a link planted
before load; they do not stop a writer racing the host as the same user, and
such a writer could edit any core file anyway. The test with the `checked`
barrier swaps `index.mjs` for a symlink after the lstat checks and shows the
realpath check refusing it; it cannot show the narrower window after realpath.
The README states the same limit. Two related limits: a module runs with the
host's authority once imported (the loader does not sandbox it), and Node
caches modules by URL, so a changed `index.mjs` is not re-read until restart.
The `packages/plugins` symlink case is only reachable when Node keeps symlinked
paths (`--preserve-symlinks`); otherwise `import.meta.url` is already the real
path. The test runs that case in a child `node --preserve-symlinks`.
## Acceptance, mapped
| Acceptance item | Where |
| --- | --- |
| a test for each refusal, old setting kept | `loader.test.mjs`: bad name, missing (absent dir, empty dir, dir as index), symlink (name dir, index, kind dir, packages/plugins, realpath swap), load-failed (syntax error, top-level throw), missing export (absent, non-function, half of limit-check, no interfaceVersion), interface version (2, string "1"); each through `refusesKeepingOld`, which checks the kept setting, the one refusal entry and the one log call |
| fixture module of each kind switched on from a test organization file, no core file changed | `loader.test.mjs` first test: a business document built with business test helpers, validated by `validateBusinessDocument`, its `plugins` passed to the loader; fixture modules written into a temp copy of the package |
| one mutant per check, each failing a test | `MUTANTS.md`, `mutants.py`, `mutants/results.json` |
| `plugins` key in the schema, written through step 5's write path and logged like a limit change | `plugin-settings.test.mjs` |
REQ ids are in each test name.
## Gate (decision 136, targeted)
Approved list (sage-0301): plugins, business, bus, webui, cli, each
`node --test tests/*.test.mjs` (the command land-work.sh runs), via `gate.sh`.
Run 2026-10-15T16:20:28Z to 16:21:48Z in the slot Sage gave in sage-0310, on the
candidate as shipped in `build.patch`. Logs: `gate/`, summary `gate-run.log`.
| Package | Tests | Pass | Fail |
| --- | --- | --- | --- |
| plugins | 19 | 19 | 0 |
| business | 91 | 91 | 0 |
| bus | 171 | 171 | 0 |
| webui (includes pinned-copy.test.mjs) | 112 | 112 | 0 |
| cli (includes stack-copy.test.mjs) | 195 | 195 | 0 |
Not run: the other packages and the shell suites; Sage's full gate at the
landing target covers them (decision 136). BASE..refactor HEAD (ea5e70fa8 at
packet time) touches none of the candidate's paths.
Mutants: 42 of 42 killed (`MUTANTS.md`).
## Overlap
Row 123 (Researcher) also edits `packages/business/README.md`. Sage orders the
landings; the second row rebases.
+59
View File
@@ -0,0 +1,59 @@
# Row 131 mutants (round 1)
One fault per check. `mutants.py` makes each edit to the candidate, runs only the
listed test files with `node --test` in its own process group (whole-group kill at
300 s), records the result and restores the file. `mutants.py --dry` checks that each
mutation site appears exactly once. Per-mutant output is in `mutants/<id>.log`;
`mutants/results.json` holds every result. Run: 2026-10-15T16:21:54Z to 16:22:09Z, in
the slot Sage gave in sage-0310.
Result: 42 of 42 KILLED, 0 SURVIVED, 0 TIMEOUT.
| Mutant | Check | Result | Test files | First failing test |
| --- | --- | --- | --- | --- |
| 01-name-pattern | kinds: names must match ^[a-z][a-z0-9-]*$ | KILLED (exit 1) | plugins/tests/kinds.test.mjs, plugins/tests/loader.test.mjs | REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates |
| 02-list-cardinality | kinds: a list kind refuses a non-list | KILLED (exit 1) | plugins/tests/kinds.test.mjs, plugins/tests/loader.test.mjs | REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything |
| 03-one-cardinality | kinds: a one-module kind refuses a non-string | KILLED (exit 1) | plugins/tests/kinds.test.mjs | REQ-PLUG-1 a kind that switches on one module takes one name, not a list |
| 04-no-duplicates | kinds: a name twice refuses | KILLED (exit 1) | plugins/tests/kinds.test.mjs, plugins/tests/loader.test.mjs | REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates |
| 05-known-kind | kinds: an unknown kind refuses | KILLED (exit 1) | plugins/tests/kinds.test.mjs | REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates |
| 06-plugins-object | kinds: plugins must be a non-array object | KILLED (exit 1) | plugins/tests/kinds.test.mjs | REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates |
| 07-registry-exports | registry: limit-check exports mayLaunch and mustStop | KILLED (exit 1) | plugins/tests/kinds.test.mjs, plugins/tests/loader.test.mjs | REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list |
| 08-registry-version | registry: usage-source is interface version 1 | KILLED (exit 1) | plugins/tests/kinds.test.mjs, plugins/tests/loader.test.mjs | REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list |
| 09-missing-directory | loader: an absent path refuses as missing | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting |
| 10-component-type | loader: a component of the wrong type refuses | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting |
| 11-symlink-test | loader: a symlinked component refuses | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting |
| 12-plugins-dir-checked | loader: packages/plugins is checked | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) |
| 13-kind-dir-checked | loader: <kind> is checked | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting |
| 14-name-dir-checked | loader: <name> is checked | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting |
| 15-index-checked | loader: index.mjs is checked | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting |
| 16-realpath-before-import | loader: realpath(index.mjs) equals the expected path just before import | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check |
| 17-load-failure | loader: a module that fails to load refuses | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a module that fails to load refuses and keeps the old setting |
| 18-export-present | loader: every export is present | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a missing export refuses and keeps the old setting |
| 19-export-function | loader: the kind's exports are functions | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a missing export refuses and keeps the old setting |
| 20-interface-version | loader: interfaceVersion must equal the kind's | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a different interface version refuses and keeps the old setting |
| 21-version-strict | loader: the version comparison is strict (string "1" refuses) | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a different interface version refuses and keeps the old setting |
| 22-keep-old-setting | loader: a refusal keeps the last good modules | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting |
| 23-first-load-nothing-on | loader: a kind refused on first load has nothing on | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name |
| 24-refusal-logged | loader: every refusal calls log | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting |
| 25-log-names-module | loader: the log names the refused module | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting |
| 26-log-kept | loader: the log says what stays on | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting |
| 27-whole-setting-object | loader: an array setting refuses every kind | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything |
| 28-unknown-kind-logged | loader: an unknown kind refuses and is logged | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything |
| 29-absent-key-off | loader: an absent key switches the kind off | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything |
| 30-loads-serialized | loader: loads run one at a time | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 loads run one at a time, so the last requested setting wins |
| 31-log-required | loader: createLoader needs a log callback | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 the loader needs a log callback |
| 32-schema-key | schema: plugins is an allowed top-level key | KILLED (exit 1) | business/tests/plugin-settings.test.mjs, plugins/tests/loader.test.mjs | REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names |
| 33-schema-check | schema: plugins is checked with checkPlugins | KILLED (exit 1) | business/tests/plugin-settings.test.mjs | REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names |
| 34-schema-returned | schema: plugins is returned | KILLED (exit 1) | business/tests/plugin-settings.test.mjs, plugins/tests/loader.test.mjs | REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names |
| 35-schema-copy | schema: the returned value is a frozen copy, the document stays mutable | KILLED (exit 1) | business/tests/plugin-settings.test.mjs | REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names |
| 36-setter-strict-keys | setter: only revision and plugins are accepted | KILLED (exit 1) | business/tests/plugin-settings.test.mjs | REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing |
| 37-setter-revision-format | setter: revision must be 64 hex | KILLED (exit 1) | business/tests/plugin-settings.test.mjs | REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing |
| 38-setter-checks-names | setter: plugins is checked before the writer runs | KILLED (exit 1) | business/tests/plugin-settings.test.mjs | REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing |
| 39-setter-actor-logged | setter: the actor is logged | KILLED (exit 1) | business/tests/plugin-settings.test.mjs | REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names |
| 40-setter-writes-plugins | setter: plugins is written | KILLED (exit 1) | business/tests/plugin-settings.test.mjs | REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names |
| 41-copy-ships-plugins | stack-copy: packages/plugins ships in the pinned copy | KILLED (exit 1) | cli/tests/stack-copy.test.mjs | shared copy: npm ci, pushed reachability, readonly/existing copy, webui delegation |
| 42-modules-known-kind | loader: modules() refuses an unknown kind | KILLED (exit 1) | plugins/tests/loader.test.mjs | REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything |
`42-modules-known-kind` was added after the slot request named 41 mutants, together
with its test, before the run. Mutant 17's log shows a SyntaxError: it is the
fixture module's deliberate syntax error, which the mutant rethrows instead of refusing.
+1
View File
@@ -0,0 +1 @@
0bdd573ffff11bd563113f0077e88e9075b5de58
+904
View File
@@ -0,0 +1,904 @@
diff --git a/packages/business/README.md b/packages/business/README.md
index 9a6baad3c..4b091dcc2 100644
--- a/packages/business/README.md
+++ b/packages/business/README.md
@@ -100,6 +100,13 @@ instance whose definition holds `role.launch` within-role; otherwise the
file refuses. Only that instance keeps `role.launch`. Every other instance
loses it, so for them it's gated.
+`plugins` (optional) switches on plug-in modules, one key per kind from
+`packages/plugins/src/kinds.mjs`, each a list of module names matching
+`^[a-z][a-z0-9-]*$` with no name twice. An unknown kind or a bad name
+refuses the file. Absent means nothing is switched on. Whether a named
+module exists and loads is the plug-in loader's check, at load time; see
+`packages/plugins/README.md`.
+
### Chief-of-staff and assistant instances
Two roles stand above the PM (PRD 1.2; row 119, #1613).
@@ -237,7 +244,10 @@ launches keep using the whole validated file on disk, without consulting receipt
confirm that its old writer is gone before clearing it; the host does not
race another recovering process by deleting locks automatically. Settings reads never repair evidence themselves.
`launch-settings.mjs` restricts human edits to limits, model table and instance
-model selections and validates effective selections before publication. Migration
+model selections and validates effective selections before publication. `plugin-settings.mjs`
+is the only writer of `plugins`: the host calls it, and no broker verb, `mosaic`
+command or Settings form reaches it. A refused `plugins` write throws
+`invalid-plugins` or `invalid-request` before the writer runs and logs nothing. Migration
uses none of this: it only prints a converted document (decision 145).
For inherited selections, print-only migration reads the existing system config
diff --git a/packages/business/src/business.mjs b/packages/business/src/business.mjs
index cbe04a59b..49ac1fa45 100644
--- a/packages/business/src/business.mjs
+++ b/packages/business/src/business.mjs
@@ -9,12 +9,13 @@ import { checkModels, checkLimits } from "./launch.mjs";
import { checkVars } from "./vars.mjs";
import { parseCredentialRef } from "./credentials.mjs";
import { loadRole } from "./role.mjs";
+import { checkPlugins } from "../../plugins/src/kinds.mjs";
import {
requireObject, rejectUnknownKeys, requireId, requirePositiveInt, requireString, requireDistinctList, readJsonFile, deepFreeze,
} from "./util.mjs";
const BOT_NAME = /^bot-[a-z0-9][a-z0-9._-]{0,60}$/;
-const TOP_KEYS = ["businessVersion", "id", "human", "arbiters", "projects", "vars", "tracker", "roles", "launch"];
+const TOP_KEYS = ["businessVersion", "id", "human", "arbiters", "projects", "vars", "tracker", "roles", "launch", "plugins"];
// The config directory: next to the system config, $MOSAIC_CONFIG or
// ~/.config/mosaic-dev/config.json, the same rule mosaic-config.mjs uses.
@@ -178,9 +179,13 @@ export function validateBusinessDocument(document, file, { rolesDir }) {
}
}
const launch = document.launch === undefined ? null : checkLaunch(document.launch, roles, definitions, file);
+ // REQ-PLUG-1: the module names switched on per kind. The names are checked
+ // here; whether each module loads is the loader's check, at load time.
+ const plugins = document.plugins === undefined ? {}
+ : structuredClone(checkPlugins(document.plugins, `${file} plugins`, { fail: (_code, message) => refuse(message) }));
return deepFreeze({
- businessVersion: 1, id: document.id, file, human, arbiters, projects, vars, tracker, roles, launch, definitions,
+ businessVersion: 1, id: document.id, file, human, arbiters, projects, vars, tracker, roles, launch, plugins, definitions,
});
}
diff --git a/packages/business/src/plugin-settings.mjs b/packages/business/src/plugin-settings.mjs
new file mode 100644
index 000000000..7827a7ec9
--- /dev/null
+++ b/packages/business/src/plugin-settings.mjs
@@ -0,0 +1,38 @@
+// Host setter for the organization file's `plugins` switch (REQ-PLUG-1,
+// REQ-ROLE-3). It is the only writer of that key: no broker verb, mosaic
+// command, Settings form or chief-of-staff write reaches it (sage-0301).
+// Writes go through the organization writer, which logs old and new names
+// like a limit change. A refused write throws a code and logs nothing.
+import { organizationWriter, OrganizationError } from "./organization.mjs";
+import { isPlainObject } from "./util.mjs";
+import { PluginError, checkPlugins } from "../../plugins/src/kinds.mjs";
+
+export function pluginSettings({ dir, rolesDir }) {
+ const writer = organizationWriter({ dir, rolesDir });
+
+ function read(id) {
+ const { document, revision } = writer.read(id);
+ return { business: id, revision, plugins: structuredClone(document.plugins ?? {}) };
+ }
+
+ function set(id, args, actor) {
+ if (!isPlainObject(args) || Object.keys(args).some(key => key !== "revision" && key !== "plugins")) {
+ throw new OrganizationError("invalid-request");
+ }
+ if (typeof args.revision !== "string" || !/^[a-f0-9]{64}$/.test(args.revision)) {
+ throw new OrganizationError("invalid-request");
+ }
+ try {
+ checkPlugins(args.plugins, "plugins");
+ } catch (e) {
+ if (e instanceof PluginError) throw new OrganizationError("invalid-plugins", e.message);
+ throw e;
+ }
+ const next = structuredClone(writer.read(id).document);
+ next.plugins = structuredClone(args.plugins);
+ const receipt = writer.write(id, next, { revision: args.revision, actor });
+ return { ...read(id), transaction: receipt.transaction };
+ }
+
+ return { writer, read, set };
+}
diff --git a/packages/business/tests/plugin-settings.test.mjs b/packages/business/tests/plugin-settings.test.mjs
new file mode 100644
index 000000000..ec3c55cbe
--- /dev/null
+++ b/packages/business/tests/plugin-settings.test.mjs
@@ -0,0 +1,86 @@
+// REQ-PLUG-1 / REQ-ROLE-3 (step 5 write path): the `plugins` key in the organization
+// file, written only through the host setter and logged like a limit change.
+import test, { after } from "node:test";
+import assert from "node:assert/strict";
+import { existsSync, readFileSync, rmSync } from "node:fs";
+import { join } from "node:path";
+import { businessDoc, rolesCopy, tmp as makeRoot, writeJson } from "./helpers.mjs";
+import { validateBusinessDocument } from "../src/business.mjs";
+import { pluginSettings } from "../src/plugin-settings.mjs";
+import { OrganizationError } from "../src/organization.mjs";
+import { BusinessError } from "../src/errors.mjs";
+
+const roots = [];
+after(() => roots.forEach(root => rmSync(root, { recursive: true, force: true })));
+
+function fixture() {
+ const root = makeRoot();
+ roots.push(root);
+ const rolesDir = rolesCopy(root);
+ const doc = businessDoc(root, "acme");
+ const file = writeJson(join(root, "businesses", "acme.json"), doc);
+ return { root, rolesDir, doc, file, settings: pluginSettings({ dir: root, rolesDir }) };
+}
+
+const events = f => existsSync(f.settings.writer.logFile("acme"))
+ ? readFileSync(f.settings.writer.logFile("acme"), "utf8").trim().split("\n").map(JSON.parse) : [];
+
+test("REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names", () => {
+ const f = fixture();
+ const check = d => validateBusinessDocument(d, f.file, { rolesDir: f.rolesDir });
+ assert.deepEqual(check(f.doc).plugins, {});
+ const on = { ...structuredClone(f.doc), plugins: { "usage-source": ["meter"], "limit-check": [] } };
+ const business = check(on);
+ assert.deepEqual(business.plugins, on.plugins);
+ assert.ok(Object.isFrozen(business.plugins));
+ assert.ok(!Object.isFrozen(on.plugins), "the document itself stays mutable");
+ for (const plugins of [{ "made-up": [] }, { "usage-source": "meter" }, { "usage-source": ["Meter"] }, { "usage-source": ["a", "a"] }, []]) {
+ assert.throws(() => check({ ...structuredClone(f.doc), plugins }), BusinessError, JSON.stringify(plugins));
+ }
+});
+
+test("REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names", () => {
+ const f = fixture();
+ const view = f.settings.read("acme");
+ assert.deepEqual(view.plugins, {});
+ const done = f.settings.set("acme", { revision: view.revision, plugins: { "usage-source": ["meter"] } }, "jason");
+ assert.deepEqual(JSON.parse(readFileSync(f.file, "utf8")).plugins, { "usage-source": ["meter"] });
+ assert.deepEqual(done.plugins, { "usage-source": ["meter"] });
+ assert.notEqual(done.revision, view.revision);
+ let last = events(f).at(-1);
+ assert.equal(last.state, "committed");
+ assert.equal(last.actor, "jason");
+ assert.equal(last.transaction, done.transaction);
+ assert.deepEqual(last.changes, [{ path: "plugins", old: null, new: { "usage-source": ["meter"] }, oldPresent: false, newPresent: true }]);
+ f.settings.set("acme", { revision: done.revision, plugins: { "usage-source": ["meter", "other"], "limit-check": ["cap"] } }, "jason");
+ last = events(f).at(-1);
+ assert.deepEqual(last.changes.map(c => [c.path, c.old, c.new]), [
+ ["plugins.usage-source", ["meter"], ["meter", "other"]],
+ ["plugins.limit-check", null, ["cap"]],
+ ]);
+});
+
+test("REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing", () => {
+ const f = fixture();
+ const { revision } = f.settings.read("acme");
+ const bytes = readFileSync(f.file, "utf8");
+ const refusals = [
+ [{ revision, plugins: { "made-up": ["x"] } }, "invalid-plugins"],
+ [{ revision, plugins: { "usage-source": ["Bad"] } }, "invalid-plugins"],
+ [{ revision, plugins: { "usage-source": "meter" } }, "invalid-plugins"],
+ [{ revision, plugins: { "limit-check": ["cap", "cap"] } }, "invalid-plugins"],
+ [{ revision }, "invalid-plugins"],
+ [{ revision, plugins: [] }, "invalid-plugins"],
+ [{ revision, plugins: {}, limits: {} }, "invalid-request"],
+ [{ revision: "nope", plugins: {} }, "invalid-request"],
+ [{ plugins: {} }, "invalid-request"],
+ [null, "invalid-request"],
+ [["x"], "invalid-request"],
+ ];
+ for (const [args, expected] of refusals) {
+ assert.throws(() => f.settings.set("acme", args, "jason"),
+ e => e instanceof OrganizationError && e.code === expected, JSON.stringify(args));
+ }
+ assert.equal(readFileSync(f.file, "utf8"), bytes);
+ assert.deepEqual(events(f), []);
+});
diff --git a/packages/cli/tests/stack-copy.test.mjs b/packages/cli/tests/stack-copy.test.mjs
index d98e771ac..d44ce5788 100644
--- a/packages/cli/tests/stack-copy.test.mjs
+++ b/packages/cli/tests/stack-copy.test.mjs
@@ -34,7 +34,7 @@ export function setup(t, realPin=false){
test('shared copy: npm ci, pushed reachability, readonly/existing copy, webui delegation',t=>{
const s=setup(t),r=s.run('stack-copy.sh',['copy',s.id,'--copies',s.copies]);assert.equal(r.status,0,r.stderr);
const copy=join(s.copies,s.id);assert.equal(statSync(copy).mode&0o777,0o555);
- for(const f of ['scripts/mosaic','adapters/pi/adapter.sh','packages/queue/src/cli.mjs','package.json','package-lock.json','packages/tasks/requirements.json'])assert.ok(existsSync(join(copy,f)),f);
+ for(const f of ['scripts/mosaic','adapters/pi/adapter.sh','packages/queue/src/cli.mjs','package.json','package-lock.json','packages/tasks/requirements.json','packages/plugins/src/kinds.mjs','packages/plugins/src/loader.mjs'])assert.ok(existsSync(join(copy,f)),f);
const npm=join(s.bin,'npm');writeFileSync(npm,'#!/bin/sh\nexit 99\n');chmodSync(npm,0o755);
const existing=s.run('webui-service.sh',['copy',s.id,'--copies',s.copies]);assert.equal(existing.status,0);assert.equal(existing.stdout,`exists: ${copy}\n`);unlinkSync(npm);
git(s.repo,'update-ref','refs/remotes/origin/refactor',git(s.repo,'hash-object','-w','package.json'));
diff --git a/packages/plugins/README.md b/packages/plugins/README.md
new file mode 100644
index 000000000..2960396c0
--- /dev/null
+++ b/packages/plugins/README.md
@@ -0,0 +1,108 @@
+# Plug-ins
+
+The plug-in layer (PRD REQ-PLUG-1, build step 6b, Gitea #1626). Every part
+the core calls out to is a plug-in kind. A module of a kind is a directory
+in this package, and the organization file says which modules are switched
+on. No core module imports a plug-in module by name; calls go through the
+loader.
+
+| Path | What it is |
+|---|---|
+| `src/kinds.mjs` | the kind registry and the check for the organization file's `plugins` key |
+| `src/loader.mjs` | loads the switched-on modules from this tree |
+| `<kind>/<name>/index.mjs` | one module of a kind |
+
+## Kinds
+
+| Kind | Interface | Switch | Exports |
+|---|---|---|---|
+| `usage-source` | 1 | list | `interfaceVersion`, `readTurn` |
+| `limit-check` | 1 | list | `interfaceVersion`, `mayLaunch`, `mustStop` |
+
+Every module exports `interfaceVersion`, equal to its kind's interface
+version, plus the kind's functions. PRD REQ-LIMIT-4 says what the two kinds
+do: a usage source reads what a session used after each turn, and a limit
+check answers whether a launch may start (`mayLaunch`) and whether a running
+session must stop at the end of its turn (`mustStop`). The arguments and
+return values of these functions are set by the rows that call them (row 124
+converts the first usage source). No module of either kind ships yet.
+
+Adding a kind, or changing a kind's exports, interface version or switch,
+is a core change: a reviewed edit to `src/kinds.mjs`, with the code that
+calls the kind.
+
+## Adding a module
+
+1. Pick a name matching `^[a-z][a-z0-9-]*$`.
+2. Create `packages/plugins/<kind>/<name>/index.mjs`. A real directory and
+ a real file: the loader refuses a symlink anywhere from
+ `packages/plugins` down, `index.mjs` included.
+3. Export `interfaceVersion` and every function the kind lists.
+4. Land it like any other change. A running stack sees it only after a
+ re-pin, because `scripts/stack-copy.sh` copies the whole
+ `packages/plugins` directory into the pinned copy.
+5. Switch it on in the organization file's `plugins` object, under its
+ kind. The host setter `packages/business/src/plugin-settings.mjs` is the
+ only writer of that key.
+
+Adding a module changes no core file.
+
+## The organization file's `plugins` key
+
+One key per kind. A list kind takes a list of names; a kind that switches
+on one module takes one name. An absent key switches the kind off.
+
+```json
+"plugins": { "usage-source": ["meter"], "limit-check": [] }
+```
+
+`validateBusinessDocument` checks the shape: known kinds, the name pattern,
+no name twice. It doesn't check that a module exists; the loader does that
+when it loads. The setter writes through the organization writer, which
+logs actor and old and new names in `changes.jsonl` like a limit change. A
+refused write throws `OrganizationError` (`invalid-plugins` or
+`invalid-request`) and logs nothing.
+
+## The loader
+
+```js
+import { createLoader } from "../packages/plugins/src/loader.mjs";
+
+const loader = createLoader({ log: entry => record(entry) });
+const { loaded, refused } = await loader.load(business.plugins);
+loader.modules("limit-check"); // the switched-on modules, in order
+loader.loaded(); // { kind: [names actually switched on] }
+```
+
+The loader finds this package from its own location and takes no path from
+config. Until Jason rules on Vikunja #286 it loads modules from this tree
+only (lead decision 143). For each name it checks, in order:
+
+1. the name matches `^[a-z][a-z0-9-]*$` (`bad-name`);
+2. `packages/plugins`, `<kind>`, `<name>` and `index.mjs` exist, are a
+ directory or a regular file as expected, and are not symlinks (`missing`,
+ `symlink`); ancestors of `packages/plugins` are not checked;
+3. just before import, `realpath(index.mjs)` is still the expected path
+ (`symlink`);
+4. the module imports (`load-failed`);
+5. it has every export, and the kind's functions are functions
+ (`missing-export`);
+6. `interfaceVersion` equals the kind's (`interface-version`).
+
+A refusal refuses the whole kind for that load: the kind keeps the modules
+it had, and the loader calls `log` with `{event: "plugin-refused", kind,
+name, code, message, kept}`. A kind refused on its first load has nothing
+switched on. An unknown kind refuses only itself; a `plugins` value that
+isn't an object refuses every kind. Loads run one at a time.
+
+Limits of the checks:
+
+- A module runs with the host's authority once imported. The checks keep
+ the loader inside this tree; they don't sandbox the module.
+- Someone who can write this tree can swap a file between the realpath
+ check and the import. The checks catch mistakes and stale links, not a
+ writer racing the host as the same user.
+- Node caches a module by URL, so a changed `index.mjs` is not re-read
+ until the process restarts.
+- The loader is not wired into any host process yet. The rows that call
+ the first two kinds (124, 125, 126) wire it.
diff --git a/packages/plugins/package.json b/packages/plugins/package.json
new file mode 100644
index 000000000..2cbd5f028
--- /dev/null
+++ b/packages/plugins/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "@mosaic/plugins",
+ "version": "0.1.0",
+ "private": true,
+ "description": "Plug-in kind registry and in-repo module loader (PRD 1.2 row 131).",
+ "license": "UNLICENSED",
+ "type": "module",
+ "engines": { "node": ">=24" },
+ "exports": { "./kinds": "./src/kinds.mjs", "./loader": "./src/loader.mjs" },
+ "scripts": { "test": "node --test tests/" }
+}
diff --git a/packages/plugins/src/kinds.mjs b/packages/plugins/src/kinds.mjs
new file mode 100644
index 000000000..42b320a51
--- /dev/null
+++ b/packages/plugins/src/kinds.mjs
@@ -0,0 +1,66 @@
+// The plug-in kind registry (REQ-PLUG-1; the first two kinds are
+// REQ-LIMIT-4's). A kind names the exports its modules must provide, the
+// interface version they implement, and whether the organization file
+// switches on one module or a list. Adding a kind, or changing one, is a
+// core change: a reviewed edit to this file.
+
+export const NAME = /^[a-z][a-z0-9-]*$/;
+
+export const KINDS = Object.freeze({
+ // Reads one turn's usage for the limit checks (row 124 converts the first).
+ "usage-source": Object.freeze({
+ kind: "usage-source", interfaceVersion: 1, exports: Object.freeze(["readTurn"]), switch: "list",
+ }),
+ // Decides whether a launch may start and whether a running session must stop.
+ "limit-check": Object.freeze({
+ kind: "limit-check", interfaceVersion: 1, exports: Object.freeze(["mayLaunch", "mustStop"]), switch: "list",
+ }),
+});
+
+export class PluginError extends Error {
+ constructor(code, message, name = null) {
+ super(message);
+ this.code = code;
+ this.name = "PluginError";
+ this.plugin = name;
+ }
+}
+
+const raise = (code, message, name) => {
+ throw new PluginError(code, message, name);
+};
+
+// The module names one kind's setting switches on, checked against the
+// kind's cardinality and the name pattern. A list may be empty.
+export function namesFor(spec, setting, where, fail = raise) {
+ if (spec.switch === "one") {
+ if (typeof setting !== "string") fail("bad-setting", `${where} must be one module name`);
+ } else if (!Array.isArray(setting)) {
+ fail("bad-setting", `${where} must be a list of module names`);
+ }
+ const names = spec.switch === "one" ? [setting] : setting;
+ const seen = new Set();
+ for (const name of names) {
+ if (typeof name !== "string" || !NAME.test(name)) {
+ fail("bad-name", `${where} names ${JSON.stringify(name)}, which doesn't match ${NAME}`,
+ typeof name === "string" ? name : null);
+ }
+ if (seen.has(name)) fail("bad-setting", `${where} names ${name} twice`, name);
+ seen.add(name);
+ }
+ return names;
+}
+
+// The organization file's `plugins` object: one key per kind, each a known kind.
+export function checkPlugins(value, where, { kinds = KINDS, fail = raise } = {}) {
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
+ fail("bad-setting", `${where} must be an object with one key per kind`);
+ }
+ for (const [kind, setting] of Object.entries(value)) {
+ if (!Object.hasOwn(kinds, kind)) {
+ fail("unknown-kind", `${where} names unknown kind ${JSON.stringify(kind)}; adding a kind is a core change`);
+ }
+ namesFor(kinds[kind], setting, `${where}.${kind}`, fail);
+ }
+ return value;
+}
diff --git a/packages/plugins/src/loader.mjs b/packages/plugins/src/loader.mjs
new file mode 100644
index 000000000..a74721e2e
--- /dev/null
+++ b/packages/plugins/src/loader.mjs
@@ -0,0 +1,144 @@
+// The plug-in loader (REQ-PLUG-1). It switches on the modules the
+// organization file's `plugins` object names, one key per kind, and loads
+// only packages/plugins/<kind>/<name>/index.mjs from the tree this file runs
+// from. It takes no path from config and loads nothing outside that tree
+// (lead decision 143). A loaded module runs with the host's authority.
+//
+// A refusal keeps the kind's last good modules and is logged. A kind refused
+// on its first load has nothing switched on. `loaded()` reports what each
+// kind actually has switched on.
+import { lstatSync, realpathSync } from "node:fs";
+import { dirname, join, resolve } from "node:path";
+import { fileURLToPath, pathToFileURL } from "node:url";
+import { KINDS, PluginError, namesFor } from "./kinds.mjs";
+
+const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
+
+const refuse = (code, message, name) => {
+ throw new PluginError(code, message, name);
+};
+
+// lstat one path component: it must exist, be the expected type, and not be a symlink.
+function component(path, directory) {
+ let st;
+ try {
+ st = lstatSync(path);
+ } catch (e) {
+ if (e.code === "ENOENT" || e.code === "ENOTDIR") refuse("missing", `${path} is missing`);
+ throw e;
+ }
+ if (st.isSymbolicLink()) refuse("symlink", `${path} is a symlink`);
+ if (directory ? !st.isDirectory() : !st.isFile()) {
+ refuse("missing", `${path} is not a ${directory ? "directory" : "regular file"}`);
+ }
+}
+
+async function loadModule(spec, name, barrier) {
+ const kindDir = join(ROOT, spec.kind);
+ const nameDir = join(kindDir, name);
+ const index = join(nameDir, "index.mjs");
+ component(ROOT, true);
+ component(kindDir, true);
+ component(nameDir, true);
+ component(index, false);
+ barrier("checked", { kind: spec.kind, name });
+ // Last check before import: the resolved file is still the one checked
+ // above. Ancestors of packages/plugins are not checked (sage-0301).
+ const expected = join(realpathSync(ROOT), spec.kind, name, "index.mjs");
+ let real;
+ try {
+ real = realpathSync(index);
+ } catch {
+ refuse("missing", `${index} is missing`);
+ }
+ if (real !== expected) refuse("symlink", `${index} resolves to ${real}`);
+ let module;
+ try {
+ module = await import(pathToFileURL(expected).href);
+ } catch (e) {
+ refuse("load-failed", `${spec.kind} ${name} failed to load: ${e.message}`);
+ }
+ for (const key of ["interfaceVersion", ...spec.exports]) {
+ if (!Object.hasOwn(module, key)) refuse("missing-export", `${spec.kind} ${name} doesn't export ${key}`);
+ }
+ for (const key of spec.exports) {
+ if (typeof module[key] !== "function") refuse("missing-export", `${spec.kind} ${name} export ${key} isn't a function`);
+ }
+ if (module.interfaceVersion !== spec.interfaceVersion) {
+ refuse("interface-version",
+ `${spec.kind} ${name} implements interface ${module.interfaceVersion}, not ${spec.interfaceVersion}`);
+ }
+ return Object.freeze({ name, module });
+}
+
+export function createLoader({ log, barrier = () => {} }) {
+ if (typeof log !== "function") throw new TypeError("createLoader needs a log callback");
+ const current = new Map(Object.keys(KINDS).map(kind => [kind, Object.freeze([])]));
+ let queue = Promise.resolve();
+
+ function refused(kind, name, e, report) {
+ if (!(e instanceof PluginError)) throw e;
+ // kept: what stays switched on, for the kind or (whole setting refused) for every kind.
+ const entry = {
+ event: "plugin-refused", kind, name: e.plugin ?? name, code: e.code, message: e.message,
+ kept: kind === null ? loaded() : current.has(kind) ? current.get(kind).map(m => m.name) : [],
+ };
+ log(entry);
+ report.push(entry);
+ }
+
+ async function apply(plugins) {
+ const report = [];
+ // A malformed whole setting refuses every kind; an unknown kind refuses only itself.
+ if (plugins === null || typeof plugins !== "object" || Array.isArray(plugins)) {
+ refused(null, null, new PluginError("bad-setting", "plugins must be an object with one key per kind"), report);
+ return { loaded: loaded(), refused: report };
+ }
+ for (const kind of Object.keys(plugins)) {
+ if (!Object.hasOwn(KINDS, kind)) {
+ refused(kind, null, new PluginError("unknown-kind", `plugins names unknown kind ${JSON.stringify(kind)}`), report);
+ }
+ }
+ for (const [kind, spec] of Object.entries(KINDS)) {
+ // An absent key switches the kind off.
+ let names = [];
+ try {
+ if (Object.hasOwn(plugins, kind)) names = namesFor(spec, plugins[kind], `plugins.${kind}`, refuse);
+ } catch (e) {
+ refused(kind, typeof plugins[kind] === "string" ? plugins[kind] : null, e, report);
+ continue;
+ }
+ const next = [];
+ let ok = true;
+ for (const name of names) {
+ try {
+ next.push(await loadModule(spec, name, barrier));
+ } catch (e) {
+ refused(kind, name, e, report);
+ ok = false;
+ break;
+ }
+ }
+ if (ok) current.set(kind, Object.freeze(next));
+ }
+ return { loaded: loaded(), refused: report };
+ }
+
+ // Loads run one at a time, so a kind's switch never interleaves.
+ function load(plugins) {
+ const run = queue.then(() => apply(plugins));
+ queue = run.catch(() => {});
+ return run;
+ }
+
+ function loaded() {
+ return Object.fromEntries([...current].map(([kind, modules]) => [kind, modules.map(m => m.name)]));
+ }
+
+ function modules(kind) {
+ if (!current.has(kind)) refuse("unknown-kind", `unknown kind ${JSON.stringify(kind)}`);
+ return current.get(kind).map(m => m.module);
+ }
+
+ return { load, loaded, modules };
+}
diff --git a/packages/plugins/tests/helpers.mjs b/packages/plugins/tests/helpers.mjs
new file mode 100644
index 000000000..e74e875ee
--- /dev/null
+++ b/packages/plugins/tests/helpers.mjs
@@ -0,0 +1,38 @@
+// A scratch tree per test: a copy of this package's src/ under
+// <root>/packages/plugins, plus fixture modules. The loader finds modules
+// from its own location, so each tree's copy loads only that tree's modules.
+import { cpSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
+import { tmpdir } from "node:os";
+import { dirname, join, resolve } from "node:path";
+import { fileURLToPath, pathToFileURL } from "node:url";
+
+export const SRC = resolve(dirname(fileURLToPath(import.meta.url)), "..", "src");
+
+export const USAGE = name => `export const interfaceVersion = 1;
+export function readTurn() { return ${JSON.stringify(name)}; }
+`;
+export const LIMIT = name => `export const interfaceVersion = 1;
+export function mayLaunch() { return ${JSON.stringify(name)}; }
+export function mustStop() { return false; }
+`;
+
+export function tree(t) {
+ const root = mkdtempSync(join(tmpdir(), "mosaic-plugins-"));
+ t.after(() => rmSync(root, { recursive: true, force: true }));
+ const pkg = join(root, "packages", "plugins");
+ cpSync(SRC, join(pkg, "src"), { recursive: true });
+ const write = (kind, name, body) => {
+ const dir = join(pkg, kind, name);
+ mkdirSync(dir, { recursive: true });
+ writeFileSync(join(dir, "index.mjs"), body);
+ return dir;
+ };
+ const loaderUrl = pathToFileURL(join(pkg, "src", "loader.mjs")).href;
+ async function loader(options = {}) {
+ const { createLoader } = await import(loaderUrl);
+ const logged = [];
+ const instance = createLoader({ log: entry => logged.push(entry), ...options });
+ return { ...instance, logged };
+ }
+ return { root, pkg, write, loader, loaderUrl };
+}
diff --git a/packages/plugins/tests/kinds.test.mjs b/packages/plugins/tests/kinds.test.mjs
new file mode 100644
index 000000000..15b641245
--- /dev/null
+++ b/packages/plugins/tests/kinds.test.mjs
@@ -0,0 +1,47 @@
+// REQ-PLUG-1: the kind registry and the organization file's `plugins` shape (row 131).
+import test from "node:test";
+import assert from "node:assert/strict";
+import { KINDS, NAME, PluginError, checkPlugins } from "../src/kinds.mjs";
+
+const code = expected => e => e instanceof PluginError && e.code === expected;
+
+test("REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list", () => {
+ assert.deepEqual(Object.keys(KINDS), ["usage-source", "limit-check"]);
+ assert.deepEqual({ ...KINDS["usage-source"], exports: [...KINDS["usage-source"].exports] },
+ { kind: "usage-source", interfaceVersion: 1, exports: ["readTurn"], switch: "list" });
+ assert.deepEqual({ ...KINDS["limit-check"], exports: [...KINDS["limit-check"].exports] },
+ { kind: "limit-check", interfaceVersion: 1, exports: ["mayLaunch", "mustStop"], switch: "list" });
+ assert.ok(Object.isFrozen(KINDS) && Object.isFrozen(KINDS["usage-source"]) && Object.isFrozen(KINDS["limit-check"].exports));
+ assert.equal(String(NAME), "/^[a-z][a-z0-9-]*$/");
+});
+
+test("REQ-PLUG-1 checkPlugins accepts one key per kind, each a list of names, empty included", () => {
+ for (const value of [{}, { "usage-source": [] }, { "usage-source": ["a", "b-2"], "limit-check": ["cap"] }]) {
+ assert.equal(checkPlugins(value, "plugins"), value);
+ }
+});
+
+test("REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates", () => {
+ for (const value of [null, [], "x", 3]) assert.throws(() => checkPlugins(value, "plugins"), code("bad-setting"));
+ assert.throws(() => checkPlugins({ "made-up": [] }, "plugins"), code("unknown-kind"));
+ assert.throws(() => checkPlugins({ toString: [] }, "plugins"), code("unknown-kind"));
+ assert.throws(() => checkPlugins({ "usage-source": "meter" }, "plugins"), code("bad-setting"));
+ for (const name of ["Meter", "1m", "m_x", "../x", "a/b", ".x", "", 7, null]) {
+ assert.throws(() => checkPlugins({ "usage-source": [name] }, "plugins"), code("bad-name"), String(name));
+ }
+ assert.throws(() => checkPlugins({ "limit-check": ["cap", "cap"] }, "plugins"), code("bad-setting"));
+});
+
+test("REQ-PLUG-1 a kind that switches on one module takes one name, not a list", () => {
+ const kinds = { single: { kind: "single", interfaceVersion: 1, exports: ["run"], switch: "one" } };
+ assert.ok(checkPlugins({ single: "only" }, "plugins", { kinds }));
+ assert.throws(() => checkPlugins({ single: ["only"] }, "plugins", { kinds }), code("bad-setting"));
+ assert.throws(() => checkPlugins({ single: "Only" }, "plugins", { kinds }), code("bad-name"));
+});
+
+test("REQ-PLUG-1 checkPlugins reports through the caller's fail callback", () => {
+ const seen = [];
+ const fail = (c, message) => { seen.push(c); throw new Error(message); };
+ assert.throws(() => checkPlugins({ "made-up": [] }, "x plugins", { fail }), /x plugins names unknown kind/);
+ assert.deepEqual(seen, ["unknown-kind"]);
+});
diff --git a/packages/plugins/tests/loader.test.mjs b/packages/plugins/tests/loader.test.mjs
new file mode 100644
index 000000000..e10e670af
--- /dev/null
+++ b/packages/plugins/tests/loader.test.mjs
@@ -0,0 +1,222 @@
+// REQ-PLUG-1: the loader switches on in-repo modules only, and
+// every refusal keeps the kind's old modules and is logged (row 131).
+import test from "node:test";
+import assert from "node:assert/strict";
+import { spawnSync } from "node:child_process";
+import { mkdirSync, mkdtempSync, renameSync, rmSync, symlinkSync, unlinkSync } from "node:fs";
+import { tmpdir } from "node:os";
+import { join } from "node:path";
+import { LIMIT, USAGE, tree } from "./helpers.mjs";
+import { businessDoc, rolesCopy, tmp as businessRoot, writeJson } from "../../business/tests/helpers.mjs";
+import { validateBusinessDocument } from "../../business/src/business.mjs";
+
+const GOOD = { "usage-source": ["meter"], "limit-check": ["cap"] };
+
+// A tree with one good module of each kind, already loaded.
+async function loadedTree(t, options) {
+ const s = tree(t);
+ s.write("usage-source", "meter", USAGE("meter"));
+ s.write("limit-check", "cap", LIMIT("cap"));
+ const loader = await s.loader(options);
+ const first = await loader.load(GOOD);
+ assert.deepEqual(first.refused, []);
+ assert.deepEqual(loader.loaded(), GOOD);
+ return { ...s, loader };
+}
+
+// Switch usage-source to `names`: the load must refuse with `code`, keep
+// ["meter"], log one entry naming the kind and `name`, and leave limit-check on.
+async function refusesKeepingOld(s, names, code, name, message) {
+ const before = s.loader.logged.length;
+ const result = await s.loader.load({ "usage-source": names, "limit-check": ["cap"] });
+ assert.deepEqual(s.loader.loaded(), GOOD, `${name}: old setting kept`);
+ assert.equal(result.refused.length, 1, name);
+ const [entry] = result.refused;
+ assert.equal(entry.code, code, `${name}: ${entry.message}`);
+ assert.equal(entry.kind, "usage-source");
+ assert.equal(entry.name, name);
+ assert.deepEqual(entry.kept, ["meter"]);
+ if (message) assert.match(entry.message, message);
+ assert.deepEqual(s.loader.logged.slice(before), [entry], `${name}: logged once`);
+ assert.equal(s.loader.modules("usage-source")[0].readTurn(), "meter");
+}
+
+test("REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed", async t => {
+ const s = tree(t);
+ s.write("usage-source", "fixture-usage", USAGE("fixture-usage"));
+ s.write("limit-check", "fixture-limit", LIMIT("fixture-limit"));
+ const root = businessRoot();
+ t.after(() => rmSync(root, { recursive: true, force: true }));
+ const doc = businessDoc(root, "acme");
+ doc.plugins = { "usage-source": ["fixture-usage"], "limit-check": ["fixture-limit"] };
+ const file = writeJson(join(root, "businesses", "acme.json"), doc);
+ const business = validateBusinessDocument(doc, file, { rolesDir: rolesCopy(root) });
+ const loader = await s.loader();
+ const result = await loader.load(business.plugins);
+ assert.deepEqual(result.refused, []);
+ assert.deepEqual(result.loaded, doc.plugins);
+ assert.equal(loader.modules("usage-source")[0].readTurn(), "fixture-usage");
+ assert.equal(loader.modules("limit-check")[0].mayLaunch(), "fixture-limit");
+ assert.equal(loader.modules("limit-check")[0].mustStop(), false);
+ assert.deepEqual(loader.logged, []);
+});
+
+test("REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting", async t => {
+ const s = await loadedTree(t);
+ for (const name of ["Meter", "1meter", "me_ter", "../cap", "meter/index", ".hidden", ""]) {
+ await refusesKeepingOld(s, ["meter", name], "bad-name", name);
+ }
+});
+
+test("REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting", async t => {
+ const s = await loadedTree(t);
+ await refusesKeepingOld(s, ["meter", "absent"], "missing", "absent", /absent is missing/);
+ mkdirSync(join(s.pkg, "usage-source", "empty"));
+ await refusesKeepingOld(s, ["empty"], "missing", "empty", /index\.mjs is missing/);
+ mkdirSync(join(s.pkg, "usage-source", "dir-index", "index.mjs"), { recursive: true });
+ await refusesKeepingOld(s, ["dir-index"], "missing", "dir-index", /not a regular file/);
+});
+
+test("REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting", async t => {
+ const s = await loadedTree(t);
+ const meter = join(s.pkg, "usage-source", "meter");
+ symlinkSync(meter, join(s.pkg, "usage-source", "linked"));
+ await refusesKeepingOld(s, ["linked"], "symlink", "linked", /usage-source\/linked is a symlink/);
+ s.write("usage-source", "link-index", USAGE("x"));
+ unlinkSync(join(s.pkg, "usage-source", "link-index", "index.mjs"));
+ symlinkSync(join(meter, "index.mjs"), join(s.pkg, "usage-source", "link-index", "index.mjs"));
+ await refusesKeepingOld(s, ["link-index"], "symlink", "link-index", /link-index\/index\.mjs is a symlink/);
+});
+
+test("REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting", async t => {
+ const s = await loadedTree(t);
+ const real = join(s.pkg, "usage-real");
+ renameSync(join(s.pkg, "usage-source"), real);
+ symlinkSync(real, join(s.pkg, "usage-source"));
+ await refusesKeepingOld(s, ["meter"], "symlink", "meter", /plugins\/usage-source is a symlink/);
+});
+
+test("REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it)", async t => {
+ const s = tree(t);
+ s.write("usage-source", "meter", USAGE("meter"));
+ const other = mkdtempSync(join(tmpdir(), "mosaic-plugins-link-"));
+ t.after(() => rmSync(other, { recursive: true, force: true }));
+ mkdirSync(join(other, "packages"));
+ symlinkSync(s.pkg, join(other, "packages", "plugins"));
+ const url = new URL(`file://${join(other, "packages", "plugins", "src", "loader.mjs")}`).href;
+ const script = `const { createLoader } = await import(${JSON.stringify(url)});
+const logged = []; const loader = createLoader({ log: e => logged.push(e) });
+const r = await loader.load({ "usage-source": ["meter"] });
+console.log(JSON.stringify({ loaded: loader.loaded(), logged }));`;
+ const run = spawnSync(process.execPath, ["--preserve-symlinks", "--input-type=module", "-e", script], { encoding: "utf8" });
+ assert.equal(run.status, 0, run.stderr);
+ const out = JSON.parse(run.stdout);
+ assert.deepEqual(out.loaded["usage-source"], []);
+ assert.equal(out.logged[0].code, "symlink");
+ assert.match(out.logged[0].message, /packages\/plugins is a symlink/);
+});
+
+test("REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check", async t => {
+ let swap = false;
+ const s = await loadedTree(t, {
+ barrier: (point, { name }) => {
+ if (!swap || point !== "checked" || name !== "swapped") return;
+ const index = join(s.pkg, "usage-source", "swapped", "index.mjs");
+ unlinkSync(index);
+ symlinkSync(join(s.pkg, "usage-source", "meter", "index.mjs"), index);
+ },
+ });
+ s.write("usage-source", "swapped", USAGE("swapped"));
+ swap = true;
+ await refusesKeepingOld(s, ["swapped"], "symlink", "swapped", /swapped\/index\.mjs resolves to .*meter\/index\.mjs/);
+});
+
+test("REQ-PLUG-1 a module that fails to load refuses and keeps the old setting", async t => {
+ const s = await loadedTree(t);
+ s.write("usage-source", "syntax", "export const interfaceVersion = 1;\nexport function readTurn( {\n");
+ await refusesKeepingOld(s, ["syntax"], "load-failed", "syntax");
+ s.write("usage-source", "throws", "throw new Error('boom');\n");
+ await refusesKeepingOld(s, ["throws"], "load-failed", "throws", /boom/);
+});
+
+test("REQ-PLUG-1 a missing export refuses and keeps the old setting", async t => {
+ const s = await loadedTree(t);
+ s.write("usage-source", "no-read", "export const interfaceVersion = 1;\n");
+ await refusesKeepingOld(s, ["no-read"], "missing-export", "no-read", /doesn't export readTurn/);
+ s.write("usage-source", "no-version", "export function readTurn() {}\n");
+ await refusesKeepingOld(s, ["no-version"], "missing-export", "no-version", /doesn't export interfaceVersion/);
+ s.write("usage-source", "not-fn", "export const interfaceVersion = 1;\nexport const readTurn = 1;\n");
+ await refusesKeepingOld(s, ["not-fn"], "missing-export", "not-fn", /readTurn isn't a function/);
+ // limit-check needs both of its exports.
+ s.write("limit-check", "half", "export const interfaceVersion = 1;\nexport function mayLaunch() {}\n");
+ const result = await s.loader.load({ "usage-source": ["meter"], "limit-check": ["cap", "half"] });
+ assert.deepEqual(result.refused.map(e => [e.kind, e.name, e.code]), [["limit-check", "half", "missing-export"]]);
+ assert.deepEqual(s.loader.loaded(), GOOD);
+});
+
+test("REQ-PLUG-1 a different interface version refuses and keeps the old setting", async t => {
+ const s = await loadedTree(t);
+ s.write("usage-source", "v2", "export const interfaceVersion = 2;\nexport function readTurn() {}\n");
+ await refusesKeepingOld(s, ["v2"], "interface-version", "v2", /implements interface 2, not 1/);
+ s.write("usage-source", "v-string", "export const interfaceVersion = '1';\nexport function readTurn() {}\n");
+ await refusesKeepingOld(s, ["v-string"], "interface-version", "v-string");
+});
+
+test("REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name", async t => {
+ const s = tree(t);
+ s.write("usage-source", "meter", USAGE("meter"));
+ s.write("limit-check", "cap", LIMIT("cap"));
+ const loader = await s.loader();
+ // meter loads, absent refuses: the whole kind stays off, not half on.
+ const result = await loader.load({ "usage-source": ["meter", "absent"], "limit-check": ["cap"] });
+ assert.deepEqual(result.loaded, { "usage-source": [], "limit-check": ["cap"] });
+ assert.deepEqual(loader.loaded(), result.loaded);
+ assert.deepEqual(loader.modules("usage-source"), []);
+ assert.equal(loader.logged.length, 1);
+ assert.equal(loader.logged[0].event, "plugin-refused");
+ assert.equal(loader.logged[0].kind, "usage-source");
+ assert.equal(loader.logged[0].name, "absent");
+ assert.deepEqual(loader.logged[0].kept, []);
+});
+
+test("REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything", async t => {
+ const s = await loadedTree(t);
+ let result = await s.loader.load({ ...GOOD, "made-up": ["x"] });
+ assert.deepEqual(result.refused.map(e => [e.kind, e.code]), [["made-up", "unknown-kind"]]);
+ assert.deepEqual(s.loader.loaded(), GOOD);
+ for (const kind of ["made-up", "toString"]) {
+ assert.throws(() => s.loader.modules(kind), e => e.code === "unknown-kind", kind);
+ }
+ for (const bad of [null, [], "meter"]) {
+ result = await s.loader.load(bad);
+ assert.deepEqual(result.refused.map(e => [e.kind, e.code]), [[null, "bad-setting"]]);
+ assert.deepEqual(result.refused[0].kept, GOOD);
+ assert.deepEqual(s.loader.loaded(), GOOD);
+ }
+ await refusesKeepingOld(s, "meter", "bad-setting", "meter");
+ await refusesKeepingOld(s, ["meter", "meter"], "bad-setting", "meter", /twice/);
+ // An absent key switches the kind off.
+ result = await s.loader.load({ "limit-check": ["cap"] });
+ assert.deepEqual(result.loaded, { "usage-source": [], "limit-check": ["cap"] });
+});
+
+test("REQ-PLUG-1 loads run one at a time, so the last requested setting wins", async t => {
+ const s = await loadedTree(t);
+ let release;
+ globalThis.__row131Gate = new Promise(resolve => { release = resolve; });
+ t.after(() => { delete globalThis.__row131Gate; });
+ s.write("usage-source", "slow", "await globalThis.__row131Gate;\nexport const interfaceVersion = 1;\nexport function readTurn() {}\n");
+ s.write("usage-source", "fast", USAGE("fast"));
+ const first = s.loader.load({ "usage-source": ["slow"] });
+ const second = s.loader.load({ "usage-source": ["fast"] });
+ await new Promise(resolve => setTimeout(resolve, 100));
+ release();
+ await Promise.all([first, second]);
+ assert.deepEqual(s.loader.loaded()["usage-source"], ["fast"]);
+});
+
+test("REQ-PLUG-1 the loader needs a log callback", async t => {
+ const s = tree(t);
+ const { createLoader } = await import(s.loaderUrl);
+ assert.throws(() => createLoader({}), /log callback/);
+});
diff --git a/scripts/stack-copy.sh b/scripts/stack-copy.sh
index f384a983d..769f91c5f 100755
--- a/scripts/stack-copy.sh
+++ b/scripts/stack-copy.sh
@@ -28,6 +28,8 @@ COPY_PATHS=(
packages/seat/src packages/tasks/src packages/tasks/fields.json packages/tasks/requirements.json
packages/webui/src packages/webui/README.md packages/cli/README.md
packages/queue/src
+ # Whole directory: every in-repo plug-in module ships with the copy (row 131).
+ packages/plugins
)
copy_commit() {
[ -d "$REPO/.git" ] || [ "$ALLOW_WORKTREE" = 1 ] || die "refusing: $REPO is not the canonical checkout (its .git is not a directory); run copy from the canonical checkout"
@@ -0,0 +1,13 @@
9187b2a38ba387b5750e94b10b16870518f032489d784fa52563622932718e5d packages/business/README.md
46f0e977d78e579362b57415996041670e36c04017136359ec11b69ecd18e112 packages/business/src/business.mjs
f3858b6f4a35ad6fe3f451496a8b464a67104e3aebc645542f1e00655bde9bbe packages/business/src/plugin-settings.mjs
445651b3d6ded8405fe6df2b62629bc37197b97173893a9db9cd6d7dbc80cc9b packages/business/tests/plugin-settings.test.mjs
924df6c3844708306f379bc88c47387a3ed648ef176b56fa38f9f2ae73c0f46a packages/cli/tests/stack-copy.test.mjs
90a9ac59a9ea2287e9a3d4b2f4286d025e727f2a6ce149464f8d0420fd9a8ef1 packages/plugins/package.json
fdf3a99223451d9608297d6e4fdb46568428424798d61f1e604fc32265d81d91 packages/plugins/README.md
ed9cba47f892554b78f4ae9803552e38005c2e11b608efebf9b9cf252b38e408 packages/plugins/src/kinds.mjs
7a9cd3aba526f1bfffb89acb0c06af8d4aef608ef46ab6bcf9d1d90d39b42e41 packages/plugins/src/loader.mjs
af18df7dacc775b28cc2c67c5755017e4850f5200b9b4eace6f23f8638109a7a packages/plugins/tests/helpers.mjs
da606e8948ad26238084605c8d77042a64cf8f8ea1ca88247aa3931894c69bce packages/plugins/tests/kinds.test.mjs
b9a9f84a89dc810203ef10f00b0f444e63d250c0f913e3ccc9f0aecb4c74199f packages/plugins/tests/loader.test.mjs
0b6becaa237500d69a4977cb79a2c0da67f728be4cd0b930dd506d445698f43a scripts/stack-copy.sh
@@ -0,0 +1,5 @@
pkg-plugins start=2026-10-15T16:20:28Z end=2026-10-15T16:20:28Z rc=0 tests=19 pass=19 fail=0
pkg-business start=2026-10-15T16:20:28Z end=2026-10-15T16:20:30Z rc=0 tests=91 pass=91 fail=0
pkg-bus start=2026-10-15T16:20:30Z end=2026-10-15T16:20:35Z rc=0 tests=171 pass=171 fail=0
pkg-webui start=2026-10-15T16:20:35Z end=2026-10-15T16:21:30Z rc=0 tests=112 pass=112 fail=0
pkg-cli start=2026-10-15T16:21:30Z end=2026-10-15T16:21:48Z rc=0 tests=195 pass=195 fail=0
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Row 131 targeted builder gate (decision 136; list approved in sage-0301).
# Same command land-work.sh runs per package. One log per package under gate/.
set -uo pipefail
WT=${1:-$HOME/dewey-scratch/r131/wt}
OUT=$(cd "$(dirname "$0")" && pwd)/gate
mkdir -p "$OUT"
export TMPDIR=$HOME/dewey-scratch/tmp
status=0
for p in plugins business bus webui cli; do
start=$(date -u +%FT%TZ)
(cd "$WT/packages/$p" && node --test tests/*.test.mjs) >"$OUT/pkg-$p.log" 2>&1
rc=$?
counts=$(grep -E '^ℹ (tests|pass|fail) ' "$OUT/pkg-$p.log" | awk '{printf "%s=%s ", $2, $3}')
echo "pkg-$p start=$start end=$(date -u +%FT%TZ) rc=$rc $counts"
[ "$rc" -eq 0 ] || status=1
done
exit $status
@@ -0,0 +1,179 @@
✔ agents: a held role with nothing on the bus is idle; a role session gets the claim rows unchanged (122.616889ms)
✔ agents: working counts what the holder received and has not read; routed alone is not working (213.322634ms)
✔ agents: input needed is the role's open decisions routed to the human, gated first, then oldest (319.47273ms)
✔ agents: working wins over waiting, and Input needed still shows (180.070221ms)
✔ agents: a new holder doesn't inherit what the last one left unread; decisions stay with the role (216.056315ms)
✔ agents: state reads only its own business, even for a run name both use (219.676207ms)
✔ agents: a message the holder released unread is stranded; the next holder gets nothing and cannot read it (232.256043ms)
✔ agents: a message the holder read is not stranded; one the current holder has not read is not listed (240.399496ms)
✔ agents: stranded is oldest first and capped at 50 for a role (967.234959ms)
✔ agents: stranded is per role and per business (353.720105ms)
✔ agents: a message routed to a held role and not yet received is not stranded (140.789563ms)
✔ launch identity is stamped, payload identity is refused and stale holder cannot send (163.12274ms)
✔ decision classes route from policy; gated resolution is human-only, choice and target must match (241.298353ms)
✔ claim exclusion, holder release, gated revoke and rerouting to a new holder are atomic (279.260983ms)
✔ launch events require a human CLI capability; generic emit cannot forge authority events (197.381125ms)
✔ within-role decisions close atomically and invalid options or blocking omissions refuse (180.923981ms)
✔ a recommendation that is not exactly one option key refuses invalid-recommendation after the options (306.922539ms)
✔ observer capabilities read human inbox but cannot mutate or forge launch identity (203.194913ms)
✔ task action subjects and linked decision trail are complete and ordered (217.36727ms)
✔ launch binding is durable and reconnecting requires the identical trusted record (132.009857ms)
✔ business isolation includes inherited object names and cross-business message references (218.70735ms)
✔ authority never transfers between action, run, target, unresolved or replaced role holder (308.115523ms)
✔ task projection uses schema current view, skipping earlier and equal-start polls (163.823648ms)
✔ revocation permanently bars the old run from reclaiming first, including after broker restart (238.962724ms)
✔ empty message references refuse before storage; refusal-evidence failure stays a typed error (162.41287ms)
✔ both arbiters require human resolution when their cross-role route is themselves (246.400338ms)
✔ the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role (181.420931ms)
✔ S1 adapter takes resolved limits and refs, rejects mismatched instance, never mutates input (3.926546ms)
✔ credentials gives human and reader sessions service, account, role, date and state, and nothing else (109.54619ms)
✔ credentials refuses a role session and any argument (173.351899ms)
✔ credentials reads the status on each request, and a broker without one lists none (204.203918ms)
✔ the broker process answers credentials without a token, path or variable in its output, log or store (208.488062ms)
✔ only validated broker references load; returned data and exceptions cannot expose a known token (4.703346ms)
✔ bad file modes, symlinks, repository/data paths, malformed tokens and missing dates refuse (4.07566ms)
✔ expiry refuses use and env references never become client data (0.903082ms)
✔ S1 parsed service refs work, service mismatch refuses, Gitea rotation due is a warning state (1.932794ms)
✔ opaque tokens shorter than 16 characters refuse before use (0.428552ms)
✔ a human resolve sends the raising role one message from the human, and its request is the answer (228.048552ms)
✔ a cross-role decision resolved by its arbiter comes from that role and run, with no request (227.639561ms)
✔ a human withdraw sends one message with the note (233.440741ms)
✔ a human seen before the close leaves the request on the close's answer, not the seen (314.49029ms)
✔ the raising run's withdraw, a supersede, a within-role raise and seen send nothing (323.747425ms)
✔ refused closes send nothing: invalid-choice, decision-closed, not-resolver (322.671224ms)
✔ a raising role nobody holds at the close gets the message on the next claim (291.71435ms)
✔ a human DECISION send keeps its own instruction as the request, not a close's answer (290.862568ms)
✔ roles lists open decisions routed to a role, and never the human inbox (196.773608ms)
✔ recent lists closed decisions of every route, newest close first, with how each closed (262.175146ms)
✔ recent gives the closing event as closed when the decision was seen first (304.625565ms)
✔ recent stops at RECENT rows (907.439159ms)
✔ every row is what trail already gives the same session for that id (307.726342ms)
✔ a role session can't list decisions, so it can't read another role's open decisions (448.063928ms)
✔ decisions is a read: one business, strict arguments, nothing written (437.720072ms)
✔ endLaunch writes session.ended, releases the run claim and kills its capabilities (134.273021ms)
✔ endLaunch refuses an unknown run, a second end and a rebind of the ended run (129.796527ms)
✔ a restarted broker refuses to rebind an ended run; a refused rebind leaves the run unbound (137.40638ms)
✔ endLaunch leaves a claim another run took alone (186.687008ms)
✔ refuse records action.refused against the caller with the code only (154.640293ms)
✔ launches off refuses role.launch with launch-revoked until launches on (209.140848ms)
✔ broker process: launch ops authorize role.launch, record refusals and end runs (312.370283ms)
✔ human proof binds CLI entry, process start and nonce; agents and incomplete ancestry refuse (2.266478ms)
✔ process reader gets own kernel identity without exposing environment values (0.831413ms)
✔ EACCES ancestor environments skip only markers; commands and registered launches still refuse (0.855373ms)
✔ real pid 1 remains inspectable when its environment is protected (0.374065ms)
✔ limits binds actor from human proof and rejects role, reader, tester and forged actor (192.145966ms)
✔ runtime wires verified human settings to the actual shared file writer and actor receipt (257.248018ms)
✔ uncertain organization receipts leave bus startup and reads alone but refuse writes until reconciled (411.039393ms)
✔ within-role sends cite an open gated launch decision without spending it or naming it in grants (195.225947ms)
✔ missing and foreign-business citations refuse and roll back message and grant (180.353594ms)
✔ cross-role sends still need a matching resolved decision and consume it once (242.935586ms)
✔ messages pages all human messages newest first without writes; read state is history (2742.215961ms)
✔ messages refuses role callers, invalid paging and cursors outside this human feed (276.715007ms)
✔ views messages passes only its paging arguments through (0.241582ms)
✔ messages isolates businesses even with a valid foreign cursor (171.770113ms)
✔ the assistant sends to the user and to the chief of staff only (280.724437ms)
✔ an assistant send to any other role, or to the tester, is refused and logged (323.614034ms)
✔ only the user and the chief of staff may send to the assistant; the tester is refused by default (363.865201ms)
✔ a sender-side list alone refuses a send to a role that has no list (345.058837ms)
✔ a target-side list alone refuses a send from a role that has no list (370.5877ms)
✔ the sender list is checked before the target list (296.679891ms)
✔ roles without the field behave exactly as before (450.160597ms)
✔ a business with no role definitions at all is unlimited (335.462907ms)
✔ a malformed messaging field refuses at boot (132.373533ms)
✔ the assistant's review.request to a reviewer is refused and logged, even with an approved decision (313.583501ms)
✔ a review.verdict to the assistant from a role outside its receiveFrom is refused and logged (383.755063ms)
✔ review requests and verdicts between roles without the field are unchanged (264.547848ms)
✔ broker process binds trusted launches, offers reader capabilities, refuses human mutation, closes cleanly (196.758558ms)
✔ startup token refusal returns safe code without value or partial listening broker (44.688342ms)
✔ loaded fixture token is absent from socket replies and SQLite, including refusal evidence (177.181753ms)
✔ killed broker leaves an explicit stale lock; another process cannot silently reclaim it (195.462577ms)
✔ trusted host registers later launches; socket clients never have a registration verb (193.85395ms)
✔ runtime excludes declared project roots even when host supplies no repoRoots (43.21371ms)
✔ a refused launch binding leaves the broker and existing capabilities alive; bad protocol stops it (197.016508ms)
✔ a reply is listed for human and reader sessions, and its delivery rows are unchanged (224.389962ms)
✔ the reply still reaches the human message.receive (mosaic talk) after the Console showed it (194.743371ms)
✔ a message with no reply gives an empty list (161.762012ms)
✔ replies come oldest first and stop at REPLIES (839.840337ms)
✔ an id that names no message in the business refuses message-not-found, and the refusal is recorded (341.202527ms)
✔ strict arguments; a role session gets human-required (429.39239ms)
✔ views passes replies through as one call with the id (0.239612ms)
✔ requirements returns the committed list to a holding role session, and no one else (211.192606ms)
✔ the PM's prd.approve with no task, when no id fits, reaches the human's Inbox (148.840372ms)
✔ the PM's non-blocking role.revoke with no task reaches the human's Inbox (180.66913ms)
✔ a push is recorded as the action.allowed for git.push.working on the task, and only the host records one (245.893054ms)
✔ review.request names the latest pushed commit and sends the reviewer a REVIEW-REQUEST (227.869838ms)
✔ review.request says which run holds the reviewer role, or null, and a null request waits for a claim (323.927687ms)
✔ review.request refuses a commit that isn't the latest push, a non-reviewer and a role without the action (417.532547ms)
✔ reviewFetch answers only the addressed reviewer, for the request it cites (266.687352ms)
✔ review.verdict answers one request once, to the role that asked (323.784035ms)
✔ task.close on a pushed task needs an approve verdict for the latest push (430.021389ms)
✔ a refusal the host decided is filed on the task it names, and only a task ref counts (277.781054ms)
✔ v3b prototype refusals, views and append-only mutations (1089.055879ms)
✔ gated approval authorizes once, survives store reopen, and fresh approval works (273.732055ms)
✔ another run cannot consume an approval; a failed check leaves it usable (248.143891ms)
✔ two scheduled callers have exactly one grant and one consumed refusal (187.453741ms)
✔ failed commit rolls consumption back; cross-role consumes and within-role stays reusable (372.877976ms)
✔ class drift gated to cross-role refuses before consumption (245.340255ms)
✔ class drift cross-role to gated refuses before consumption (285.714235ms)
✔ class drift gated to within-role refuses before consumption (250.205796ms)
✔ class drift cross-role to within-role refuses before consumption (261.233495ms)
✔ class drift within-role to gated refuses before consumption (224.065738ms)
✔ class drift within-role to cross-role refuses before consumption (202.06831ms)
✔ message.send consumes approval and prevents a later send or authorize (235.999112ms)
✔ role.revoke consumes approval and prevents a later revoke or authorize (288.490446ms)
✔ a business that never switched launches reads allowed with no time and no event (127.852385ms)
✔ launches revoked and then restored read allowed, with the restore time and event id (164.494709ms)
✔ a task closed on the tracker leaves the open list, and total counts the open ones (167.464985ms)
✔ a tombstone is not open (171.460579ms)
✔ the open list stops at the cap and says so; total still counts every open task (150.390206ms)
✔ a role and a reader session read state; arguments, and an unbound cap, refuse (164.569781ms)
✔ a human session reads the same state, with the same cap (160.011936ms)
✔ reading state writes nothing: every table keeps its row count (162.403458ms)
✔ creates private WAL store and excludes a second writer until explicit close (145.946444ms)
✔ rollback is atomic and schema metadata is checked against trusted DDL, not just itself (194.945101ms)
✔ existing empty database and symlink runtime directory refuse, never initialize over damage (208.637074ms)
✔ crash during a transaction recovers no partial event after explicit fixture-only lock removal (192.903866ms)
✔ writer refuses mixed at/read_at forms atomically, even through trusted SQL helpers (131.366643ms)
✔ async transactions refuse before invoking their function (114.295165ms)
✔ the session default fits under the reply cap and pages to the end with next (346.885718ms)
✔ the short form: no description, assignees by role, every listed key (282.993393ms)
✔ each filter, alone and together, and total counts every match (379.809636ms)
✔ task_ref reads one task in full, as the whole board has it (324.705959ms)
✔ no arguments is the read the Console and mosaic make, unchanged (276.377834ms)
✔ an unknown argument or a bad value refuses invalid-request (1093.291939ms)
✔ tasks rows carry priority and requirement for every session that reads tasks (344.656201ms)
✔ the read model rebuilds from the ledger: a reopened bus and each trail give the same values (376.331732ms)
✔ an empty business and one with snapshots but no task.created read cleanly (135.176447ms)
✔ recordTask keeps sync reads and a role write apart (191.698634ms)
✔ read_at must be one canonical UTC format, so the projection compares strings safely (126.454044ms)
✔ a bad entry refuses the whole record (117.660204ms)
✔ taskView reads the projection for one business (137.090263ms)
✔ requestTask hands only a holder and a task verb to the handler, and records refusals (272.236652ms)
✔ the server sends task verbs to the adapter with its own timeout; other verbs stay synchronous (441.522841ms)
✔ without an adapter the server refuses every task verb (248.771833ms)
✔ the runtime refuses an invalid adapter and closes a valid one (243.953359ms)
✔ the process loads the S3 adapter from plain-data trackers (370.858909ms)
✔ the tester binds only when the business sets bus.tester to exactly true (884.76292ms)
✔ a business may not name a role tester, so the tester address is never a role (269.643239ms)
✔ every verb outside the tester list refuses tester-refused and is recorded against the tester (587.258833ms)
✔ a tester message is from tester, starts [mos test], and writes no human.input or authority (235.395203ms)
✔ a role answers a tester only in reply to a tester message, and the answer never reaches the human (276.720558ms)
✔ the tester reads the human views a reader reads (167.727478ms)
✔ tester proof binds its own CLI, start time and nonce, refuses a managed run and allows an agent (1.207237ms)
✔ neither CLI's nonce proves the other: each proof is bound to its own CLI and nonce variable (0.354008ms)
✔ testerNonce reads only the tester's variable and gives null on any read error (0.222645ms)
✔ an envelope carries exactly one of cap, human and tester; no tester authenticator refuses (90.324556ms)
✔ tester-cli.mjs binds through the runtime when the switch is on, and refuses off or under a run marker (842.328174ms)
✔ socket capability stamps launch identity; shared views use wire, no SQL client (172.450558ms)
✔ two wire claims serialize; a lost reply never automatically retries (199.693125ms)
✔ malformed, oversized and identity-forging envelopes refuse without echoing input (132.42488ms)
✔ client preserves UTF-8 when a response divides a multibyte character (11.461167ms)
✔ committed mutation followed by dropped reply reports unknown and is never retried (148.637774ms)
ℹ tests 171
ℹ suites 0
ℹ pass 171
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 4813.315624
@@ -0,0 +1,99 @@
✔ the role file loads and resolves through loadBusiness (8.263863ms)
✔ the shipped file is exactly the approved list (1.279864ms)
✔ the assistant's authority is message.send within role and nothing else (13.994902ms)
✔ the assistant talks to the user and the chief of staff only, and no other shipped role is limited (0.933464ms)
✔ a business grant through limits does not widen the role (2.061154ms)
✔ a launch block whose by is the assistant is refused (2.791592ms)
✔ the instance takes no credential references and no tracker (3.236434ms)
✔ the contract passes work to the chief of staff and directs no one (0.652603ms)
✔ config directory and file path follow MOSAIC_CONFIG (1.95993ms)
✔ the fixture business validates and comes back frozen (6.118634ms)
✔ two instances may share a definition (13.223991ms)
✔ top-level refusals (6.087281ms)
✔ arbiters and projects (8.050895ms)
✔ role instances (5.750234ms)
✔ Vikunja bots (6.791914ms)
✔ a role without Vikunja takes no tracker block (2.192527ms)
✔ credential references match the definition's services (2.791163ms)
✔ launch (7.352658ms)
✔ loadBusiness: file checks (1.791991ms)
✔ loadBusiness: not a regular file (37.403137ms)
✔ loading writes nothing (1.0575ms)
✔ names that are Object.prototype properties don't count as declared (2.674819ms)
✔ the shipped example refuses as written and validates once filled in (0.92968ms)
✔ the role file loads and resolves through loadBusiness (8.923785ms)
✔ the shipped file is exactly the approved list (0.30206ms)
✔ the resolved record matches the approved list, so a widened file fails (2.337455ms)
✔ the chief of staff holds role.launch and message.send; every other action is gated (1.974957ms)
✔ the pm does not hold role.launch (10.231481ms)
✔ a business grant of a gated action through limits does not widen the role (4.793563ms)
✔ a limit that drops role.launch leaves the chief of staff with no launch block (1.435305ms)
✔ a launch block whose by is the pm instance is refused (3.195612ms)
✔ the contract names no duty to talk to the human and the pm contract no launch (0.451805ms)
✔ the instance takes no credential references and no tracker (3.265046ms)
✔ usage errors exit 4 (290.447019ms)
✔ validate: a good business exits 0 and prints instance digests (64.44128ms)
✔ validate: project files (307.578432ms)
✔ validate: missing files and a broken system config (216.842734ms)
✔ validate: credential reference problems exit 2 and name each one (60.302634ms)
✔ validate: a token file inside the repository is refused (68.405838ms)
✔ validate: role definitions come from MOSAIC_ROLES_DIR (196.699745ms)
✔ resolve: prints one instance's record (267.461697ms)
✔ resolve: refusals (417.951027ms)
✔ parse: exactly one of file or env, plus the service's date (3.378321ms)
✔ check: a good file has no problems (1.020996ms)
✔ check never opens the file: a write-only token passes (0.441022ms)
✔ check: file problems (1.135172ms)
✔ check: token files can't live in the repository or dataRoot, even through a linked directory (12.60785ms)
✔ check: dates and environment references (0.536922ms)
✔ REQ-LIMIT-1 schema: every scope unset or zero, adjustable above four, bad values and unknown models refuse (28.466941ms)
✔ REQ-LIMIT-1 counts each exact scope at/over limit, unlimited and old tuple after model reassignment (1.052466ms)
✔ REQ-ROLE-3 writer: atomic validated revision, immutable previous bytes and authenticated actor/old/new receipt (190.271907ms)
✔ REQ-ROLE-3 writer: pre-rename fault leaves old file, post-rename recovery records commit, conflicting recovery refuses (105.691647ms)
✔ REQ-LIMIT-1 / decision145 migration prints only, preserves values, rejects ambiguity, and is idempotent (2.865704ms)
✔ REQ-LIMIT-1 shared settings validates the complete proposed selection and logs exact actor (96.351501ms)
✔ REQ-LIMIT-1 provider is table data: another provider routes separately and aliases share exact counts (1.753236ms)
✔ REQ-ROLE-3 refuses live writer locks, symlink targets and uncertain recovery without changing bytes (63.202485ms)
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (11.960715ms)
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (179.554394ms)
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.653656ms)
✔ path and load (4.240359ms)
✔ refusals (1.354171ms)
✔ systemVars flattens the validated config (1.809772ms)
✔ precedence: system, business, project, project role, agent (12.89165ms)
✔ limits narrow the definition and never widen it (3.096124ms)
✔ role.launch stays within-role only for the instance the launch block names (8.476862ms)
✔ limits.authority without role.launch leaves the launcher with no launch block (4.344811ms)
✔ limits.authority narrows cross-role actions too (1.882502ms)
✔ classify (1.697006ms)
✔ the record carries what the broker and launcher need (1.211188ms)
✔ digest: key order doesn't matter, any value change does (8.601241ms)
✔ refusals (1.889844ms)
✔ the four shipped version 2 roles load (3.134369ms)
✔ shipped role scopes match addendum B section 2 and the SR runbook (0.903041ms)
✔ shipped authority follows the note's table (0.692882ms)
✔ every role file in roles/ loads through the validator, and a v2 contract exists (0.916231ms)
✔ version 1 files keep loading with no authority (1.111918ms)
✔ the conductor policy isn't a role (0.235431ms)
✔ a missing role file is exit 4, a symbolic link too (0.497439ms)
✔ version 2 refusals (2.728143ms)
✔ authority: closed vocabulary, no gated-only action, no overlap (2.393358ms)
✔ messaging: optional, role definitions and human, never the tester (2.100656ms)
✔ credentials: Gitea scopes (1.93914ms)
✔ credentials: Vikunja scopes are a group-to-verbs map from the grantable list (1.409779ms)
✔ credentials: services (0.769383ms)
✔ contract: a non-empty regular Markdown file beside the role file (0.864863ms)
✔ every key names known layers and a merge rule (0.736048ms)
✔ unknown keys and wrong layers refuse (0.731079ms)
✔ types (1.583684ms)
✔ merge: defaults, then the most specific layer wins (0.231294ms)
✔ merge: limits only narrow, and provenance lists each source (0.323263ms)
✔ merge doesn't change its inputs (0.120226ms)
ℹ tests 91
ℹ suites 0
ℹ pass 91
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1967.194799
@@ -0,0 +1,205 @@
✔ every screen and overlay fits 120x36, 80x24 and the narrow layout, row for row (1325.814042ms)
✔ width counts terminal cells: wide CJK and emoji 2, U+FE0F after a one-cell base 1, marks 0 (98.29343ms)
✔ broker text is shown, never interpreted: no raw control character or bidi override reaches the terminal (171.124876ms)
✔ NO_COLOR: no escape codes, except reverse video on the selected row (257.230442ms)
✔ 1-9 shows the decide flow's own lines and question; only y resolves (161.101458ms)
✔ the app shows the same decide text as mosaic decide (118.880288ms)
✔ any key but y or Y at the question answers No; nothing is sent (1486.059354ms)
✔ below 40x10 the question is hidden, so every key answers it No, y included (Filbert R3) (782.125396ms)
✔ under 40 columns a tall terminal shows the too-small screen even where the decide box would fit; y sends nothing (Filbert N10) (1574.500756ms)
✔ a hidden decide box names the way out, and the status line under it does not promise y (Filbert N12) (86.325308ms)
✔ a throw while answering the question goes to crash, sends nothing and leaves no job pending (107.973847ms)
✔ type-ahead never answers the question: a paste, keys typed before it is drawn, keys inside askMs (936.710222ms)
✔ quitting at the question answers No and sends nothing, armed or not (568.572638ms)
✔ a timer tick reads nothing under an overlay, never answers the question, and keeps a resolved status (189.698755ms)
✔ a long question is cut inside the question; the action, target, choice and prompt stay (Dewey B1) (108.430128ms)
✔ a decide box too big for the terminal says so, and every key answers it No, y included (Dewey B1, round 3) (972.201475ms)
✔ at every size the decide box shows each line y depends on, or says it is hidden (8242.441282ms)
✔ outcome unknown: the CLI's sentence, no retry, then the trail or a re-read (157.713082ms)
✔ refused and failed reads keep the last list, marked stale; a refused decide says refused (110.858466ms)
✔ help or a detail box cut to fit keeps its last line, Esc included (Dewey) (57.705873ms)
✔ keys: views, moving, trail of a selection, the command line, help and quit (230.854804ms)
✔ one option: the status line says Press 1, not 1-1 (96.981806ms)
✔ no business: the app says so and :business picks one (48.763766ms)
✔ mosaic with no argument: exit 3 in an agent run, exit 4 with the usage off a terminal, the app on one (1.862784ms)
✔ scripts/mosaic with no argument reaches the app entry; the seat usage it printed before has not drifted (172.263599ms)
✔ strings to keep exactly live once, in the decide flow and the transport; the app does not restate them (0.453708ms)
✔ decide with --note still asks; a No sends nothing (79.512519ms)
✔ the app's business comes from a live bus host only (1.127008ms)
✔ runApp leaves the terminal as it found it on q, end of input, SIGTERM, SIGINT, SIGHUP and an error, the first frame's included (6.238657ms)
✔ draw is pure: same state, same frame (0.740601ms)
✔ inbox lists only decisions routed to the human, with what approving authorizes and how to decide (134.36535ms)
✔ decide resolves by id prefix with --yes, and the trail shows it in broker order with the task to follow (134.130417ms)
✔ decide refuses without a terminal or --yes, on an unknown option and on a short reference (106.047051ms)
✔ decide prints a declining choice as declining (149.905161ms)
✔ an unknown outcome is reported once and never resent (112.153208ms)
✔ a decision closed before the answer arrives exits 2 and points at its trail (108.663386ms)
✔ a prefix that matches two open decisions exits 2 and resolves neither (84.912476ms)
✔ without --business a command uses the live host's business, and a stale host.json is not a host (138.883907ms)
✔ every human command refuses inside an agent run before it touches the bus (100.512353ms)
✔ usage errors exit 4; no business and no host is a usage error (92.416585ms)
✔ agents and tasks print through the broker (96.667961ms)
✔ notify.json: missing, loose, malformed and extra keys refuse; a binding or null passes (1.376871ms)
✔ bootConfig builds the broker's boot message for one business, with no trackers key when no project names one (68.788229ms)
✔ trackers come from the tracker.* variables of the one project that names a tracker project (84.439941ms)
✔ with two projects, the one that sets tracker.project supplies the tracker and the other is no conflict (41.726414ms)
✔ two projects that each name a tracker project refuse, since the boot shape holds one (35.702561ms)
✔ a business without tracker.baseUrl gets no trackers entry (38.600612ms)
✔ an unknown business and a broken system config refuse with exit 3 (65.509721ms)
✔ follow prints an unsolicited message after plain talk returned (183.563341ms)
✔ talk --follow continues after reply and prints a later unsolicited message (178.109003ms)
✔ inbox lists all unread human messages under decisions, does not deliver or read them (2389.837776ms)
✔ follow refuses agent context and bad arguments (95.827573ms)
✔ follow Ctrl-C wakes the poll sleep and removes its handler; transport errors also clean up (88.823977ms)
✔ plain talk leaves the process SIGINT handlers unchanged (258.679145ms)
✔ empty views say so (1.046784ms)
✔ the trail keeps the broker's order and names a decision's task without its rows (1.289911ms)
✔ tasks print the tracker fields the snapshot carries (0.225299ms)
✔ the review loop: the coder pushes and asks, the reviewer fetches and approves, and the task's trail holds each step (724.549762ms)
✔ refusals: no action, non-fast-forward, an unknown commit or request, and a push for another task moves only that task's ref (633.568902ms)
✔ the size cap refuses a push before any byte (349.985527ms)
✔ a fetch over the cap refuses before any byte reaches the reviewer (543.18068ms)
✔ the socket refuses a request with no capability, an unknown one, and a bad request line (371.094846ms)
✔ a rigged clone runs nothing: hooks, fsmonitor, a filter, a gitlink and a symlinked .git/config leave no marker (731.463074ms)
✔ an identity with no role, or of another business, refuses not-a-role and moves no ref (536.507635ms)
✔ a review of a project the business no longer has refuses unknown-project (707.200595ms)
✔ past maxActive connections the socket refuses git-busy, and a hung-up connection frees its slot (274.860539ms)
✔ close() answers host-closing to a connection still sending its request line (311.617676ms)
✔ a bundle that trickles in refuses body-timeout at its deadline and frees its slot (1071.622924ms)
✔ a reviewer gone before its bundle was sent is filed fetch-aborted, not as the host's fault (826.822435ms)
✔ a reviewer that hangs up the moment it has the whole bundle was served, not aborted (986.954315ms)
✔ send: a close after every byte left is served; one that fails a queued write is fetch-aborted (40.62895ms)
✔ a bundle with no bytes for bodyIdle refuses body-timeout long before its deadline (602.715104ms)
✔ send: a close once the loop is past its last chunk is served, and a failed write there is fetch-aborted (24.971668ms)
✔ letGo waits for an answer to leave for at most its cap, then destroys the socket (301.326213ms)
✔ a record that fails and can't be undone refuses rollback-failed, which the host logs (517.566439ms)
✔ the host boots the broker, binds a launch in process, and the notifier DMs a blocking decision exactly once (941.818429ms)
✔ a notifier that dies takes the host down with exit 1, so the unit restarts the pair (233.298195ms)
✔ a second host for the same data root refuses with exit 3 while the first runs (171.083542ms)
✔ two bus starts racing for one data root leave one host running; the other refuses that a host already runs (603.911775ms)
✔ a host takes over a host.lock its dead host left, and puts back a live claim that replaced it meanwhile (1.32365ms)
✔ a host.lock that goes between the check and the read is tried again; one that can't be parsed still refuses as unreadable (2.679643ms)
✔ a live host's lock stays, however old the file is (0.571354ms)
✔ a host.lock left by a host killed with SIGKILL doesn't stop the next bus start (812.434207ms)
✔ while a host boots, a reader sees no host: the human commands still ask for --business, stop and status find none (219.605827ms)
✔ a host started before host.lock existed, holding only host.json, still refuses a second start, which leaves no lock (27.984179ms)
✔ a broker fork that throws leaves no host.lock and no host.json (29.244812ms)
✔ a broker send that throws leaves no host.lock and no host.json (41.588296ms)
✔ a broker that misses its boot wait leaves no host.lock and no host.json (30.00364ms)
✔ a notifier fork that throws stops the broker and leaves no host.lock and no host.json (172.599828ms)
✔ a notifier send that throws stops the broker and leaves no host.lock and no host.json (286.439005ms)
✔ a boot reply with no reader for the business refuses with exit 3 naming it, before the notifier starts, and leaves no host.lock and no live broker (173.483994ms)
✔ a boot reply with no reader for the business refuses with exit 3 naming it, with no notifier, and leaves no host.lock and no live broker (185.685125ms)
✔ a host.json that can't be written stops both children and leaves no host.lock (467.002995ms)
✔ an error event on the broker before it replies fails the start at once, and leaves no host.lock and no live child (33.395138ms)
✔ an error event on the notifier before it replies fails the start at once, and leaves no host.lock and no live child (160.109335ms)
✔ an error event on the broker while the notifier starts fails the start once the notifier replies (277.639724ms)
✔ an error event on the broker once the host is up stops the host with exit 1, and leaves no host.lock and no host.json (247.362201ms)
✔ an error event on the notifier once the host is up stops the host with exit 1, and leaves no host.lock and no host.json (404.212007ms)
✔ a broker reply that misses the wait breaks the channel: the late reply answers nothing, later requests refuse, the host exits 1 (1321.817115ms)
✔ a broker reply with another request's id, or none, breaks the channel and the host exits 1 (681.245943ms)
✔ a broker reply with no request waiting breaks the channel and the host exits 1 (188.977399ms)
✔ a notifier that refuses stops the broker and the host refuses with exit 3 (212.512221ms)
✔ a notifier that refuses after the broker died still refuses with exit 3, without a send to the dead broker (213.001366ms)
✔ a notifier that dies before it replies, after the broker died, still refuses, without a send to the dead broker (140.833829ms)
✔ a close send that fails with EPIPE after the notifier refuses still gives the notifier's refusal, exit 3 (166.4697ms)
✔ a close send that fails with EPIPE after the notifier dies unanswered still gives the notifier's error (150.801288ms)
✔ close() whose stop and close sends fail with EPIPE still finishes, with exit 1 (192.741254ms)
✔ watchChildren reports a child that died before it was called, and one that dies later (22.577293ms)
✔ bus stop refuses to signal a live pid that is not a bus host (202.642101ms)
✔ bus start refuses with exit 3 and the code when the broker refuses to boot; bus status names the lock (243.470052ms)
✔ bus start refuses with exit 3 without a notifier config (109.862339ms)
✔ bus start runs until bus stop; status reports it while it runs (708.056946ms)
✔ bus-service.sh renders the unit and installs it into a given directory (54.758139ms)
✔ bus-service.sh pins the claude binary, refuses one the unit can't carry, and renders without one (61.432948ms)
✔ bus-service.sh refuses a directory pin and keeps a symlink default unresolved (12.725368ms)
✔ bus-service.sh refuses a default claude without the execute bit, or in a PATH dir with a space (8.350693ms)
✔ each launch rereads limits and model selections; lowered limits keep sessions and log exact hit (47.944019ms)
✔ bus start --pm: the launcher instance (the chief of staff) runs under its own PID namespace, registered, with a manifest (1413.317683ms)
✔ the chief of staff launches a coder through its launch tool; the coder answers; refusals name their code (786.221271ms)
✔ each launch makes sure the workspace has a clone of the project: cloned, fast-forwarded, or fetched and logged (2081.328369ms)
✔ a project clone that can't be made refuses checkout-failed, from --chief and from a session, with no run and no clone left (280.481576ms)
✔ a runner that stops at once ends its launch with the runner's reason (318.629574ms)
✔ a Claude Code launch with no session credential refuses before it starts (229.809162ms)
✔ a pi launch needs no session credential: pi logs in through its own ENV_ALLOW variables (515.814539ms)
✔ a runner killed from the host ends its launch as killed, not failed (350.704601ms)
✔ a missing or failing bwrap refuses sandbox-unavailable before anything starts; there is no unconfined fallback (197.021869ms)
✔ a real Claude Code coder's shell can't read a stand-in secret or anything else its sandbox doesn't hold (1628.217088ms)
✔ a host that died hard leaves its sessions to the next host, which kills them and ends their runs so the role can be launched again (837.500267ms)
✔ a host that died hard leaves its sessions to the next host, which finds one already exited (23) and ends their runs so the role can be launched again (3685.024053ms)
✔ a malformed sessions.json refuses the host start with exit 3 and stays as it was (150.503222ms)
✔ an over-long launch request is refused at once, not at the 10 s idle timeout (152.734437ms)
✔ a launch client that never closes its side doesn't hold the host's close (146.022097ms)
✔ a runner that ignores SIGTERM is killed when the host closes (1279.765002ms)
✔ claudeVersion asks MOSAIC_CLAUDE_BIN when set, else claude on PATH (4.58605ms)
✔ migration needs no bus/config, prints whole converted document, is idempotent and never writes (16.007363ms)
✔ limits view/set use the human bus transport and pass only the explicit patch (1.831072ms)
✔ post-119 example and Jason fixture migrate with checked inherited models; wrong or absent config refuses (223.945694ms)
✔ zoned uses the IANA zone across DST (26.487944ms)
✔ each open blocking decision is DM'd once, across polls and a restart; non-blocking ones are not (137.183772ms)
✔ two blocking decisions get two DMs with different nonces (129.671674ms)
✔ the digest nonce differs per business and per day and fits Discord's 25 characters (0.326597ms)
✔ a failed DM is journaled, backs off, and is retried until it lands (105.332101ms)
✔ five definite refusals stop a DM: one gave-up line, one log line, and a restart keeps the count (104.848987ms)
✔ 429s, 5xx-style unknowns and refusals without a status never count toward the limit (147.575782ms)
✔ a crash between the fifth refusal and its gave-up line: the next poll appends it and sends nothing (111.339632ms)
✔ polled every POLL_MS against a permanent 403, a DM is sent at 0, 30, 60, 90 and 120 min and gives up only then (165.095163ms)
✔ a restart after the second refusal does not send before that refusal's 30 min are up (167.164494ms)
✔ the digest goes at 08:00 Chicago once a day, with blocking ones marked as DM'd (117.518585ms)
✔ a late start with no digest for the day sends one at once; an empty inbox gets one line (93.187725ms)
✔ an inbox read failure is logged and the next poll retries (1.14894ms)
✔ no Discord id reaches the journal or the log (157.632903ms)
✔ the journal: a torn tail is copied out and truncated, so an append after it reopens cleanly (19.756979ms)
✔ the journal: a crash between the copy and the truncate leaves a tail the next open repairs (49.697135ms)
✔ the journal: a whole file that is one torn line truncates to empty (44.902695ms)
✔ the journal: a malformed complete line refuses and leaves the file and any torn tail alone (2.08755ms)
✔ the journal: a loose file mode, a loose directory or a symlinked journal refuses (0.718088ms)
✔ the journal: a line with a wrong type refuses with exit 3 and names the field (4.459054ms)
✔ the journal: a symlinked directory refuses and says it is a link (2.184752ms)
✔ the journal: a dangling directory link, a parent that is a file and a journal that is a directory each refuse with exit 3 (0.553131ms)
✔ the journal: an append after the file was swapped for a symlink refuses and writes nothing through it (0.429727ms)
✔ the journal: a directory it cannot write or create refuses with exit 3 and names the path (0.511181ms)
✔ digest content stays within Discord's 2000 characters (0.32266ms)
✔ runLoop never overlaps ticks and stops after the one in flight (110.541242ms)
✔ a human command refused with human-required prints the other-tree line and exits 3 (3.475838ms)
✔ a --tester command refused with tester-required prints the other-tree line and exits 3 (1.289049ms)
✔ other exit-3 refusals print no other-tree line (2.214808ms)
✔ the line names no path (0.151192ms)
✔ shared copy: npm ci, pushed reachability, readonly/existing copy, webui delegation (513.12094ms)
✔ wrapper preserves Q81 lease and marker, rejects foreign/symlink; check uses effective roots (414.665863ms)
✔ bus unit copy/pm pins, install refuses all busy states and failed reads, drop-in refusal (455.066561ms)
✔ missing package manifest refuses before npm; npm failure removes the unpublished temp copy (309.870049ms)
✔ real pinned install loads bus and Console; same-copy mosaic talk passes proof, other trees refuse (4007.647111ms)
✔ bus status reads effective copy/pm/drop-ins without writes; check refuses an overridden ExecStart (475.278675ms)
✔ drop-ins in either override field and malformed metadata refuse; uninstall also checks --no-reload (184.614484ms)
✔ install never shows a template name on systemd 261; template drop-ins still refuse (198.839373ms)
✔ unlisted instance drop-in is found in another unit lookup directory; path discovery errors refuse (218.595144ms)
✔ inherited mosaic-.service.d override refuses without changing the unit (216.930351ms)
✔ inherited service.d override refuses without changing the unit (196.476437ms)
✔ inherited drop-in unreadable directory or file refuses before writing (356.194491ms)
✔ talk --tester sends a [mos test] message as tester from inside an agent and prints the role's reply (153.274809ms)
✔ talk --tester refuses text without the [mos test] prefix before any call (99.834097ms)
✔ the read verbs take --tester inside an agent, and the trail shows the tester as the sender (104.89727ms)
✔ decide, stop and launches take no --tester (104.440367ms)
✔ the tester transport runs tester-cli.mjs, and the tester's refusals exit 3 (0.347595ms)
✔ with no stand-in transport, --tester runs tester-cli.mjs on the data root's socket (0.531391ms)
task.close {} answered: invalid-request; fake saw 18 requests, first GET /info 200, GET /projects/1 200, GET /projects/1/views 200
task.close on a missing task answered: task-not-found; it made GET /tasks/999 404
✔ bootConfig trackers reach the S3 adapter in the real broker child, which goes ready against a fake Vikunja (435.521382ms)
✔ the transport writes {business, verb, args} to the child and reads its JSON (51.584323ms)
✔ a bus code on stderr becomes the exit code; garbage and timeouts are outcome problems (2171.99681ms)
✔ busExit and refuseInsideAgent (0.702256ms)
✔ talk sends a REQUEST, prints and reads everything that arrives, and stops at the reply (217.521588ms)
✔ talk --wait 0 only sends; no reply within --wait exits 1 and says where it will show (1109.866931ms)
✔ a late reply the Console has listed still reaches the next talk (2155.106616ms)
✔ talk, stop and launches off/on refuse inside an agent run; bad arguments are usage errors (110.338774ms)
✔ launches off and on go to the broker and change the business's launch state (130.237987ms)
✔ launches list reads sessions.json and marks a stale entry; stop reports it and refuses a non-runner (317.382941ms)
ℹ tests 195
ℹ suites 0
ℹ pass 195
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 18306.618783
@@ -0,0 +1,27 @@
✔ REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list (0.984321ms)
✔ REQ-PLUG-1 checkPlugins accepts one key per kind, each a list of names, empty included (0.220052ms)
✔ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (4.127979ms)
✔ REQ-PLUG-1 a kind that switches on one module takes one name, not a list (0.166159ms)
✔ REQ-PLUG-1 checkPlugins reports through the caller's fail callback (0.144692ms)
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (7.527967ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.356774ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.194607ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.316974ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.004169ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (26.90026ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.621211ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.743246ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (6.771389ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.461101ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.732925ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.541202ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.690898ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.091345ms)
ℹ tests 19
ℹ suites 0
ℹ pass 19
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 221.966687
@@ -0,0 +1,122 @@
✔ the Board: held roles with no machine sessions, a claim with no address, the toggle kept across a reload, and Message addressed to the role (6303.10561ms)
✔ a redirect answer on a Board read, a bus read, a conversation read, a board POST, a module read and a send signs the page out: one line, nothing stale, no further reads (8076.148612ms)
✔ browser edge states: loading, empty, malformed, stale, hostile/long values, in-flight reply and appearance fallback (5392.68844ms)
Rendered contrast: {"failures":[],"count":330,"lowest":4.504658476260286}
✔ served Console browser: real board fixtures, keyboard, drafts, receipts, themes, 320px and failures (6274.115052ms)
✔ the README says a refused field table names the file or the key at fault, never a path (1.599393ms)
✔ S5 pages in a browser: real broker rows, inert text, Copy, routes, failures, layout and no writes (9036.292084ms)
✔ bus routes serve the four reads from the reader view, unescaped JSON for the page to escape (315.99851ms)
✔ decisions route serves the roles and recent lists from the reader view (802.021065ms)
✔ credentials route serves the broker credential status from the reader view (650.940435ms)
✔ replies route serves the replies to one message from the reader view (1279.242281ms)
✔ bus routes are GET-only, have no write verb, and keep the same-origin checks (76.02534ms)
✔ bus refusals map to statuses and never carry a path or a stack (27.788262ms)
✔ humanCall speaks the human transport protocol without blocking the server (1679.524669ms)
✔ busReader takes --business, else the live bus host, else refuses (390.796091ms)
✔ humanCall kills a transport that runs past its timeout (530.429503ms)
✔ serve refuses a --business value that is not a business id (608.11661ms)
✔ conversation view: full history, collapsed tools, hidden thinking, inert hostile content, malformed and reconcile markers (4253.30008ms)
✔ conversation view: a fork keeps the open branch, says so, and opens the new one on request (4349.654833ms)
✔ conversation view: a newer session with no readable history keeps the marker (2754.638777ms)
✔ conversation view: seats without history say so and offer no reply (2781.092367ms)
✔ Discord row through real board/WebUI: independent brake/liveness, no Reply, literal content (4175.723547ms)
✔ return flow through the conversation view: send, tool call, delayed result, peer message, exact long answers, relaunch (54728.527208ms)
✔ messages HTTP read pages and preserves metadata; bad arguments refuse before a read; other reads unchanged (370.364872ms)
✔ Console Messages lists late unsolicited messages on polling with sender class time read state and trail; nav count includes beyond the page; reads never deliver (11991.789674ms)
✔ Messages nav count polls on Tasks and Board and clears on a Board refusal; messages 403 refuses local pages (3406.312095ms)
✔ copy writes the Console paths at the full id, read-only, and leaves an existing copy alone (3568.755608ms)
✔ copy refuses a commit off refactor, an unknown one, a worktree and a bad copies directory (8864.728034ms)
✔ copy writes nothing when an archived path is missing at the commit (1988.76524ms)
✔ a copy that loses the rename race leaves no read-only temp directory behind (469.245198ms)
✔ a copy whose rename fails with no target refuses, writes no copy and leaves no temp directory (451.873184ms)
✔ a copy that meets a target holding files says exists: and moves nothing into it (450.58999ms)
✔ a copy that meets an empty target directory replaces it by rename, not by moving into it (388.163785ms)
✔ serve runs from a copy: /healthz 200, the config check, roles and RELEASE from the copy, the queue from --checkout (641.361453ms)
✔ serve refuses a --checkout that is not an absolute directory path (211.29901ms)
✔ consoleReads runs the queue read under queueRoot and reads RELEASE under root (48.362371ms)
✔ preview options are all three or none, and each is checked (10.963538ms)
✔ the CLI refuses a partial or invalid preview flag set at start (2426.423296ms)
✔ the CLI with all three flags serves the preview mode (270.471087ms)
✔ a request for the public name needs the header equal to the user id, else 401 and nothing read (17.604995ms)
✔ the header name matches without regard to case, and the value compare is exact (3.923416ms)
✔ the public name with the header reads the bus views, and the Host and Origin are checked first (30.263768ms)
✔ in preview mode every write answers 403 read-only, from the public name and loopback, and reaches nothing (11.894315ms)
✔ loopback requests are unchanged in preview mode, and the bus views work with the board down (4.775059ms)
✔ a proxy that rewrites Host to the loopback port gets 403 on its first request, whatever the user (16.895379ms)
✔ a repeated Host is 400 before any other check, in preview mode and without it (8.168507ms)
✔ without the preview flags the public name is refused and writes still reach the board (4.860659ms)
✔ redactor: config, dataRoot, any absolute path, Discord ids and the JSON parser quote (2.303622ms)
✔ credential state from the date alone, as the bus states it (0.28922ms)
✔ queue: rows, one row, ids and methods, notes; no path or id from a row (1723.356497ms)
✔ queue: a refused read fails closed with the store's text, redacted (1609.79683ms)
✔ queue: a store that fails to load, or a child that dies, gives a fixed message with no path or stack (4220.915199ms)
✔ business: names, vars on the allowlist, authority, credential metadata only (21.773093ms)
✔ business: missing or invalid file refuses with the module's text, redacted (47.954711ms)
✔ business without --business: the live bus host, else no-bus-host; no system config: not-configured (16.459548ms)
✔ settings: release, board, loopback address, notifier binding name only (1007.810372ms)
✔ task fields: the committed table with no reads configured, GET only; a bad table refuses with no path (150.170504ms)
✔ both presentations replace old activity with relaunch notice, label retained history, then resume after new activity (3928.868296ms)
✔ reported return flow and relative Age: reply sent from the inspector, then the new answer appears there without manual refresh (23164.214902ms)
✔ the send box: held roles, the draft and caret across a refresh, each receipt, one POST per Send, and the trail link; the board Reply on a 4xx and a 5xx (7408.256554ms)
✔ a sent receipt lists its replies as the page refreshes, oldest first and capped, and marks none delivered or read (13817.370495ms)
✔ startServer takes send only as a function and only with the preview mode (6.222994ms)
✔ the CLI refuses --send without the preview flags, and without a configured bus (480.774964ms)
✔ the CLI with --send and a configured bus sends through the human transport and logs one line (129.214383ms)
✔ without --send the route is read-only like every other write, and config says no send (11.411492ms)
✔ with --send every other write is still 403 read-only, and nothing reaches the board or bus (8.256153ms)
✔ config says send only on the public name with --send on (2.250413ms)
✔ a send without the signed-in user is 401 and is neither sent nor logged, loopback included (4.739116ms)
✔ each request check refuses before anything is sent, and logs the refusal without the body (23.25476ms)
✔ a send passes exactly to, body and in_reply_to, answers the id, and logs it (4.072221ms)
✔ a crafted role name cannot forge a log line (4.278053ms)
✔ a bus refusal is not sent, with its status; an outcome the bus could not confirm is unknown and never retried (13.20471ms)
✔ through the human transport the request is exactly the business, message.send and the keys (133.201576ms)
✔ against a real broker: the message is written once as a human send, and its refusals map (1560.542642ms)
✔ loopback host and board origin fail closed (6.133024ms)
✔ real board fixture passes through WebUI; assets and isolated seen/reply work (372.965949ms)
✔ proxy preserves exact request bytes, status and receipt, rejects forms and malformed JSON, never follows redirect (67.930758ms)
✔ unreachable board reports URL; CLI rejects unsupported options (619.048779ms)
✔ render prints the unit without the preview flags (10.732396ms)
✔ render carries every serve flag, the preview set included (6.755057ms)
✔ a partial preview set is refused (16.941438ms)
✔ a value the unit cannot carry is refused, for every flag (493.548487ms)
✔ the rendered unit names none of the human proof agent markers and sets no agent variable (33.532995ms)
✔ install into --dir with --no-reload writes the file whole, then reports unchanged (231.568575ms)
✔ install without --no-reload runs daemon-reload and nothing that starts the unit (118.062339ms)
✔ uninstall refuses while the unit is running, with or without --no-reload, and removes it once it is not (354.889142ms)
✔ status prints the unit state and GET /healthz on the installed port (395.642259ms)
✔ usage: an unknown verb or flag, and start or enable, exit 4 (319.763513ms)
✔ without --send, render is the template with its placeholders filled, byte for byte (112.146814ms)
✔ render --send with the preview set ends ExecStart with --send and changes nothing else (171.025541ms)
✔ --send without the whole preview set is refused and writes nothing (320.692025ms)
✔ install --send into --dir with --no-reload writes the sending unit, and status says whether it sends (420.426794ms)
✔ uninstall and status take no --send (82.628457ms)
✔ install refuses while the unit is running, in any state but inactive or failed, and changes nothing (781.667238ms)
✔ when the state read fails, install and uninstall refuse with one line and change nothing (110.551737ms)
✔ install needs --copy naming a copy that exists, and refuses outside the canonical checkout without --allow-worktree (117.277604ms)
✔ install refuses unless the bus unit runs from the same copy, naming both paths, and changes nothing (432.402023ms)
✔ render --copy runs the copy and reads the queue in the checkout; without --copy the unit is unchanged (29.522255ms)
✔ the uid header takes only an Authentik uid; the username header still takes a username (594.83823ms)
✔ a preview value cannot start with - or . (39.638245ms)
✔ status names the copy and whether refactor has moved past it, and says when the unit runs from no copy (643.425632ms)
✔ Settings form shows every scope and held role; gated save, exact payload, retained draft and no automatic retry (4131.322546ms)
2026-10-15T16:20:40.813Z settings user="jason" changed
✔ REQ-WEB-1 signed-in same-origin Settings write reaches exactly one human operation (31.461326ms)
✔ REQ-WEB-1 Settings writes require enabled send, named Host, identity, exact Origin and same-origin Fetch Metadata (12.087476ms)
✔ REQ-WEB-1 Settings validates JSON, size, revision and strict writable keys before broker call (8.730245ms)
✔ tokens.css and icons.svg are byte copies of docs/design (1.107613ms)
✔ JetBrains Mono files match the digests in their sources file, with the OFL text (2.027698ms)
✔ shipped strings are unchanged, and the web keeps its write boundary (2.198961ms)
✔ shell in a browser: System default, seeded states, 400px, focus and the Ctrl+K palette (7495.88541ms)
✔ five states on Board, Inbox, Tasks, Agents and Trail (3080.498888ms)
✔ Queue, Business and Settings in their five states, with no move button (5051.251662ms)
✔ seeded secrets, Discord ids and paths reach no response and no page; business refusals in the module's words (3675.843699ms)
✔ the server log names no config path when the system config refuses (316.769625ms)
ℹ tests 112
ℹ suites 0
ℹ pass 112
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 55175.021453
@@ -0,0 +1,44 @@
2026-10-15T16:21:54Z
01-name-pattern KILLED 1
02-list-cardinality KILLED 1
03-one-cardinality KILLED 1
04-no-duplicates KILLED 1
05-known-kind KILLED 1
06-plugins-object KILLED 1
07-registry-exports KILLED 1
08-registry-version KILLED 1
09-missing-directory KILLED 1
10-component-type KILLED 1
11-symlink-test KILLED 1
12-plugins-dir-checked KILLED 1
13-kind-dir-checked KILLED 1
14-name-dir-checked KILLED 1
15-index-checked KILLED 1
16-realpath-before-import KILLED 1
17-load-failure KILLED 1
18-export-present KILLED 1
19-export-function KILLED 1
20-interface-version KILLED 1
21-version-strict KILLED 1
22-keep-old-setting KILLED 1
23-first-load-nothing-on KILLED 1
24-refusal-logged KILLED 1
25-log-names-module KILLED 1
26-log-kept KILLED 1
27-whole-setting-object KILLED 1
28-unknown-kind-logged KILLED 1
29-absent-key-off KILLED 1
30-loads-serialized KILLED 1
31-log-required KILLED 1
42-modules-known-kind KILLED 1
32-schema-key KILLED 1
33-schema-check KILLED 1
34-schema-returned KILLED 1
35-schema-copy KILLED 1
36-setter-strict-keys KILLED 1
37-setter-revision-format KILLED 1
38-setter-checks-names KILLED 1
39-setter-actor-logged KILLED 1
40-setter-writes-plugins KILLED 1
41-copy-ships-plugins KILLED 1
2026-10-15T16:22:09Z
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
"""Row 131: one fault per check. Sequential process groups; whole-group timeout cleanup.
--dry checks every mutation site matches and changes source, and runs nothing."""
import json,os,pathlib,signal,subprocess,sys
args=[a for a in sys.argv[1:] if a!='--dry'];DRY='--dry' in sys.argv
ROOT=pathlib.Path(args[0] if args else os.path.expanduser('~/dewey-scratch/r131/wt'))
OUT=pathlib.Path(__file__).resolve().parent/'mutants'
LT='packages/plugins/tests/loader.test.mjs';KT='packages/plugins/tests/kinds.test.mjs';PT=[KT,LT]
ST='packages/business/tests/plugin-settings.test.mjs';CT='packages/cli/tests/stack-copy.test.mjs'
cases=[]
def add(id,path,old,new,test):cases.append(dict(id=id,path=path,old=old,new=new,test=test if isinstance(test,list) else [test]))
p='packages/plugins/src/kinds.mjs'
add('01-name-pattern',p,'if (typeof name !== "string" || !NAME.test(name)) {','if (typeof name !== "string") {',PT)
add('02-list-cardinality',p,'} else if (!Array.isArray(setting)) {','} else if (false) {',PT)
add('03-one-cardinality',p,'if (typeof setting !== "string") fail(','if (false) fail(',KT)
add('04-no-duplicates',p,'if (seen.has(name)) fail(','if (false) fail(',PT)
add('05-known-kind',p,'if (!Object.hasOwn(kinds, kind)) {','if (false) {',KT)
add('06-plugins-object',p,'if (value === null || typeof value !== "object" || Array.isArray(value)) {','if (value === null) {',KT)
add('07-registry-exports',p,'exports: Object.freeze(["mayLaunch", "mustStop"])','exports: Object.freeze(["mayLaunch"])',PT)
add('08-registry-version',p,'kind: "usage-source", interfaceVersion: 1,','kind: "usage-source", interfaceVersion: 2,',PT)
p='packages/plugins/src/loader.mjs'
add('09-missing-directory',p,'if (e.code === "ENOENT" || e.code === "ENOTDIR") refuse("missing", `${path} is missing`);','/* missing not mapped */',LT)
add('10-component-type',p,'if (directory ? !st.isDirectory() : !st.isFile()) {','if (false) {',LT)
add('11-symlink-test',p,'if (st.isSymbolicLink()) refuse("symlink", `${path} is a symlink`);','/* symlinks allowed */',LT)
add('12-plugins-dir-checked',p,' component(ROOT, true);\n','',LT)
add('13-kind-dir-checked',p,' component(kindDir, true);\n','',LT)
add('14-name-dir-checked',p,' component(nameDir, true);\n','',LT)
add('15-index-checked',p,' component(index, false);\n','',LT)
add('16-realpath-before-import',p,'if (real !== expected) refuse(','if (false) refuse(',LT)
add('17-load-failure',p,'refuse("load-failed", `${spec.kind} ${name} failed to load: ${e.message}`);','throw e;',LT)
add('18-export-present',p,'if (!Object.hasOwn(module, key)) refuse(','if (false) refuse(',LT)
add('19-export-function',p,'if (typeof module[key] !== "function") refuse(','if (false) refuse(',LT)
add('20-interface-version',p,'if (module.interfaceVersion !== spec.interfaceVersion) {','if (false) {',LT)
add('21-version-strict',p,'if (module.interfaceVersion !== spec.interfaceVersion) {','if (module.interfaceVersion != spec.interfaceVersion) {',LT)
add('22-keep-old-setting',p,'if (ok) current.set(kind, Object.freeze(next));','current.set(kind, Object.freeze(next));',LT)
add('23-first-load-nothing-on',p,'if (ok) current.set(kind, Object.freeze(next));','if (ok || current.get(kind).length === 0) current.set(kind, Object.freeze(next));',LT)
add('24-refusal-logged',p,' log(entry);\n','',LT)
add('25-log-names-module',p,'name: e.plugin ?? name, code','name, code',LT)
add('26-log-kept',p,'kept: kind === null ? loaded() : current.has(kind) ? current.get(kind).map(m => m.name) : [],','kept: [],',LT)
add('27-whole-setting-object',p,'if (plugins === null || typeof plugins !== "object" || Array.isArray(plugins)) {','if (plugins === null || typeof plugins !== "object") {',LT)
add('28-unknown-kind-logged',p,'refused(kind, null, new PluginError("unknown-kind"','void (kind, null, new PluginError("unknown-kind"',LT)
add('29-absent-key-off',p,'if (Object.hasOwn(plugins, kind)) names = namesFor(','if (!Object.hasOwn(plugins, kind)) continue; names = namesFor(',LT)
add('30-loads-serialized',p,'const run = queue.then(() => apply(plugins));','const run = apply(plugins);',LT)
add('31-log-required',p,'if (typeof log !== "function") throw','if (false) throw',LT)
add('42-modules-known-kind',p,'if (!current.has(kind)) refuse("unknown-kind"','if (false) refuse("unknown-kind"',LT)
p='packages/business/src/business.mjs'
add('32-schema-key',p,'"roles", "launch", "plugins"];','"roles", "launch"];',[ST,LT])
add('33-schema-check',p,'structuredClone(checkPlugins(document.plugins, `${file} plugins`, { fail: (_code, message) => refuse(message) }))','structuredClone(document.plugins)',ST)
add('34-schema-returned',p,'launch, plugins, definitions,','launch, definitions,',[ST,LT])
add('35-schema-copy',p,' : structuredClone(checkPlugins(',' : (checkPlugins(',ST)
p='packages/business/src/plugin-settings.mjs'
add('36-setter-strict-keys',p,'Object.keys(args).some(key => key !== "revision" && key !== "plugins")','false',ST)
add('37-setter-revision-format',p,'if (typeof args.revision !== "string" || !/^[a-f0-9]{64}$/.test(args.revision)) {','if (false) {',ST)
add('38-setter-checks-names',p,' checkPlugins(args.plugins, "plugins");\n','',ST)
add('39-setter-actor-logged',p,'{ revision: args.revision, actor }','{ revision: args.revision, actor: "host" }',ST)
add('40-setter-writes-plugins',p,' next.plugins = structuredClone(args.plugins);\n','',ST)
add('41-copy-ships-plugins','scripts/stack-copy.sh',' packages/plugins\n','',CT)
results=[]
if not DRY:OUT.mkdir(exist_ok=True)
for c in cases:
file=ROOT/c['path'];before=file.read_text();matches=before.count(c['old'])
if not matches:raise RuntimeError('missing mutation site '+c['id'])
after=before.replace(c['old'],c['new'])
if after==before:raise RuntimeError('no change '+c['id'])
if DRY:print(c['id'],'sites',matches,flush=True);continue
file.write_text(after)
try:
env={**os.environ,'DOCKER_HOST':'unix:///nonexistent.sock'}
with (OUT/(c['id']+'.log')).open('w') as log:
p=subprocess.Popen(['node','--test',*c['test']],cwd=ROOT,env=env,stdout=log,stderr=subprocess.STDOUT,start_new_session=True)
try:rc=p.wait(timeout=300)
except subprocess.TimeoutExpired:os.killpg(p.pid,signal.SIGKILL);p.wait();rc='timeout'
verdict='KILLED' if isinstance(rc,int) and rc!=0 else 'SURVIVED' if rc==0 else 'TIMEOUT'
row={k:c[k] for k in ['id','path','test']};row.update(result=verdict,exit=rc,sites=matches);results.append(row);print(c['id'],verdict,rc,flush=True)
finally:file.write_text(before)
if not DRY:(OUT/'results.json').write_text(json.dumps(results,indent=2)+'\n')
@@ -0,0 +1,83 @@
✔ REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list (1.019749ms)
✔ REQ-PLUG-1 checkPlugins accepts one key per kind, each a list of names, empty included (0.19619ms)
✖ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (4.019474ms)
✖ REQ-PLUG-1 a kind that switches on one module takes one name, not a list (0.197387ms)
✔ REQ-PLUG-1 checkPlugins reports through the caller's fail callback (0.14334ms)
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (7.327215ms)
✖ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.03219ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.869298ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.878709ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.062596ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (25.283014ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.635104ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.773907ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (4.097903ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.445512ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.700789ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.427419ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.797037ms)
✔ REQ-PLUG-1 the loader needs a log callback (0.927329ms)
ℹ tests 19
ℹ suites 0
ℹ pass 16
ℹ fail 3
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 221.237262
✖ failing tests:
test at packages/plugins/tests/kinds.test.mjs:24:1
✖ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (4.019474ms)
AssertionError [ERR_ASSERTION]: Missing expected exception: Meter
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:30:12)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
operator: 'throws',
diff: 'simple'
}
test at packages/plugins/tests/kinds.test.mjs:35:1
✖ REQ-PLUG-1 a kind that switches on one module takes one name, not a list (0.197387ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:39:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
operator: 'throws',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:64:1
✖ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.03219ms)
AssertionError [ERR_ASSERTION]: Meter: /home/jwoltje/dewey-scratch/tmp/mosaic-plugins-JnGnKV/packages/plugins/usage-source/Meter is missing
+ actual - expected
+ 'missing'
- 'bad-name'
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:35:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:67:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 'missing',
expected: 'bad-name',
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,47 @@
✔ REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list (0.992413ms)
✔ REQ-PLUG-1 checkPlugins accepts one key per kind, each a list of names, empty included (0.223062ms)
✔ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (3.778864ms)
✔ REQ-PLUG-1 a kind that switches on one module takes one name, not a list (0.155215ms)
✔ REQ-PLUG-1 checkPlugins reports through the caller's fail callback (0.132822ms)
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (7.264915ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.291846ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.181509ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.387816ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.019959ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (28.057863ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (3.119103ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.769964ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.762968ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.392455ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.676599ms)
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.875753ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.700288ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.033515ms)
ℹ tests 19
ℹ suites 0
ℹ pass 18
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 221.082439
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:182:1
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.875753ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
'e' !== 'meter'
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:37:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:196:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'e',
expected: 'meter',
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,46 @@
✔ REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list (0.930082ms)
✔ REQ-PLUG-1 checkPlugins accepts one key per kind, each a list of names, empty included (0.223975ms)
✔ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (0.969163ms)
✖ REQ-PLUG-1 a kind that switches on one module takes one name, not a list (0.4663ms)
✔ REQ-PLUG-1 checkPlugins reports through the caller's fail callback (0.156446ms)
ℹ tests 5
ℹ suites 0
ℹ pass 4
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 46.619363
✖ failing tests:
test at packages/plugins/tests/kinds.test.mjs:35:1
✖ REQ-PLUG-1 a kind that switches on one module takes one name, not a list (0.4663ms)
AssertionError [ERR_ASSERTION]: The validation function is expected to return "true". Received false
Caught error:
PluginError: plugins.single names ["only"], which doesn't match /^[a-z][a-z0-9-]*$/
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:38:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: PluginError: plugins.single names ["only"], which doesn't match /^[a-z][a-z0-9-]*$/
at raise (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/src/kinds.mjs:30:9)
at namesFor (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/src/kinds.mjs:45:7)
at checkPlugins (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/src/kinds.mjs:63:5)
at file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:38:23
at getActual (node:assert:580:5)
at strict.throws (node:assert:728:24)
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:38:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18),
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,73 @@
✔ REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list (0.95784ms)
✔ REQ-PLUG-1 checkPlugins accepts one key per kind, each a list of names, empty included (0.21576ms)
✖ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (4.189325ms)
✔ REQ-PLUG-1 a kind that switches on one module takes one name, not a list (0.177823ms)
✔ REQ-PLUG-1 checkPlugins reports through the caller's fail callback (0.140868ms)
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (7.852303ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.447333ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.626598ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.494359ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.220796ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (25.026034ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.683959ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.82915ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.830976ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.417758ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.651658ms)
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (3.300099ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.696203ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.086076ms)
ℹ tests 19
ℹ suites 0
ℹ pass 17
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 212.526387
✖ failing tests:
test at packages/plugins/tests/kinds.test.mjs:24:1
✖ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (4.189325ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:32:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
operator: 'throws',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:182:1
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (3.300099ms)
AssertionError [ERR_ASSERTION]: meter: old setting kept
+ actual - expected
{
'limit-check': [
'cap'
],
'usage-source': [
'meter',
+ 'meter'
]
}
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:32:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:197:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [ 'meter', 'meter' ], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,77 @@
✔ REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list (0.905149ms)
✔ REQ-PLUG-1 checkPlugins accepts one key per kind, each a list of names, empty included (0.198894ms)
✖ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (1.017801ms)
✔ REQ-PLUG-1 a kind that switches on one module takes one name, not a list (0.145613ms)
✖ REQ-PLUG-1 checkPlugins reports through the caller's fail callback (0.197794ms)
ℹ tests 5
ℹ suites 0
ℹ pass 3
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 44.992544
✖ failing tests:
test at packages/plugins/tests/kinds.test.mjs:24:1
✖ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (1.017801ms)
AssertionError [ERR_ASSERTION]: The validation function is expected to return "true". Received false
Caught error:
TypeError: Cannot read properties of undefined (reading 'switch')
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:26:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: TypeError: Cannot read properties of undefined (reading 'switch')
at namesFor (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/src/kinds.mjs:36:12)
at checkPlugins (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/src/kinds.mjs:63:5)
at file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:26:23
at getActual (node:assert:580:5)
at strict.throws (node:assert:728:24)
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:26:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19),
operator: 'throws',
diff: 'simple'
}
test at packages/plugins/tests/kinds.test.mjs:42:1
✖ REQ-PLUG-1 checkPlugins reports through the caller's fail callback (0.197794ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /x plugins names unknown kind/. Input:
"TypeError: Cannot read properties of undefined (reading 'switch')"
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:45:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: TypeError: Cannot read properties of undefined (reading 'switch')
at namesFor (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/src/kinds.mjs:36:12)
at checkPlugins (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/src/kinds.mjs:63:5)
at file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:45:23
at getActual (node:assert:580:5)
at strict.throws (node:assert:728:24)
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:45:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19),
expected: /x plugins names unknown kind/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,32 @@
✔ REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list (0.986263ms)
✔ REQ-PLUG-1 checkPlugins accepts one key per kind, each a list of names, empty included (0.223227ms)
✖ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (0.652142ms)
✔ REQ-PLUG-1 a kind that switches on one module takes one name, not a list (0.199955ms)
✔ REQ-PLUG-1 checkPlugins reports through the caller's fail callback (0.154114ms)
ℹ tests 5
ℹ suites 0
ℹ pass 4
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 46.622015
✖ failing tests:
test at packages/plugins/tests/kinds.test.mjs:24:1
✖ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (0.652142ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:25:50)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,82 @@
✖ REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list (1.599459ms)
✔ REQ-PLUG-1 checkPlugins accepts one key per kind, each a list of names, empty included (0.26875ms)
✔ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (3.810855ms)
✔ REQ-PLUG-1 a kind that switches on one module takes one name, not a list (0.177014ms)
✔ REQ-PLUG-1 checkPlugins reports through the caller's fail callback (0.131927ms)
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (7.204067ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.272346ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.219628ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.350046ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.002748ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (25.717132ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (3.082999ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.703152ms)
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (4.445377ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.507044ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.655494ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.548617ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.841555ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.106435ms)
ℹ tests 19
ℹ suites 0
ℹ pass 17
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 216.916959
✖ failing tests:
test at packages/plugins/tests/kinds.test.mjs:8:1
✖ REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list (1.599459ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
exports: [
'mayLaunch',
- 'mustStop'
],
interfaceVersion: 1,
kind: 'limit-check',
switch: 'list'
}
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:12:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { kind: 'limit-check', interfaceVersion: 1, exports: [ 'mayLaunch' ], switch: 'list' },
expected: { kind: 'limit-check', interfaceVersion: 1, exports: [ 'mayLaunch', 'mustStop' ], switch: 'list' },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:142:1
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (4.445377ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- [
- 'limit-check',
- 'half',
- 'missing-export'
- ]
- ]
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:153:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [],
expected: [ [ 'limit-check', 'half', 'missing-export' ] ],
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,392 @@
✖ REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list (1.636999ms)
✔ REQ-PLUG-1 checkPlugins accepts one key per kind, each a list of names, empty included (0.284927ms)
✔ REQ-PLUG-1 checkPlugins refuses bad shapes, unknown kinds, bad names and duplicates (3.752159ms)
✔ REQ-PLUG-1 a kind that switches on one module takes one name, not a list (0.176765ms)
✔ REQ-PLUG-1 checkPlugins reports through the caller's fail callback (0.133452ms)
✖ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (8.131187ms)
✖ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (2.299204ms)
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.173819ms)
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.040617ms)
✖ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.392711ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (25.450577ms)
✖ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.230341ms)
✖ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (1.977788ms)
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (1.998245ms)
✖ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.129628ms)
✖ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (2.007332ms)
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.384492ms)
✖ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (1.753096ms)
✔ REQ-PLUG-1 the loader needs a log callback (0.950668ms)
ℹ tests 19
ℹ suites 0
ℹ pass 6
ℹ fail 13
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 112.266641
✖ failing tests:
test at packages/plugins/tests/kinds.test.mjs:8:1
✖ REQ-PLUG-1 REQ-LIMIT-4 the registry: usage-source and limit-check at interface version 1, each a list (1.636999ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
exports: [
'readTurn'
],
+ interfaceVersion: 2,
- interfaceVersion: 1,
kind: 'usage-source',
switch: 'list'
}
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/kinds.test.mjs:10:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { kind: 'usage-source', interfaceVersion: 2, exports: [ 'readTurn' ], switch: 'list' },
expected: { kind: 'usage-source', interfaceVersion: 1, exports: [ 'readTurn' ], switch: 'list' },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:44:1
✖ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (8.131187ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ {
+ code: 'interface-version',
+ event: 'plugin-refused',
+ kept: [],
+ kind: 'usage-source',
+ message: 'usage-source fixture-usage implements interface 1, not 2',
+ name: 'fixture-usage'
+ }
+ ]
- []
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:56:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ { event: 'plugin-refused', kind: 'usage-source', name: 'fixture-usage', code: 'interface-version', message: 'usage-source fixture-usage implements interface 1, not 2', kept: [] } ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:64:1
✖ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (2.299204ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ {
+ code: 'interface-version',
+ event: 'plugin-refused',
+ kept: [],
+ kind: 'usage-source',
+ message: 'usage-source meter implements interface 1, not 2',
+ name: 'meter'
+ }
+ ]
- []
at loadedTree (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:22:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:65:13)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ { event: 'plugin-refused', kind: 'usage-source', name: 'meter', code: 'interface-version', message: 'usage-source meter implements interface 1, not 2', kept: [] } ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:71:1
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.173819ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ {
+ code: 'interface-version',
+ event: 'plugin-refused',
+ kept: [],
+ kind: 'usage-source',
+ message: 'usage-source meter implements interface 1, not 2',
+ name: 'meter'
+ }
+ ]
- []
at loadedTree (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:22:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:72:13)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ { event: 'plugin-refused', kind: 'usage-source', name: 'meter', code: 'interface-version', message: 'usage-source meter implements interface 1, not 2', kept: [] } ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:80:1
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.040617ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ {
+ code: 'interface-version',
+ event: 'plugin-refused',
+ kept: [],
+ kind: 'usage-source',
+ message: 'usage-source meter implements interface 1, not 2',
+ name: 'meter'
+ }
+ ]
- []
at loadedTree (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:22:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:81:13)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ { event: 'plugin-refused', kind: 'usage-source', name: 'meter', code: 'interface-version', message: 'usage-source meter implements interface 1, not 2', kept: [] } ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:91:1
✖ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.392711ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ {
+ code: 'interface-version',
+ event: 'plugin-refused',
+ kept: [],
+ kind: 'usage-source',
+ message: 'usage-source meter implements interface 1, not 2',
+ name: 'meter'
+ }
+ ]
- []
at loadedTree (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:22:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:92:13)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ { event: 'plugin-refused', kind: 'usage-source', name: 'meter', code: 'interface-version', message: 'usage-source meter implements interface 1, not 2', kept: [] } ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:119:1
✖ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.230341ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ {
+ code: 'interface-version',
+ event: 'plugin-refused',
+ kept: [],
+ kind: 'usage-source',
+ message: 'usage-source meter implements interface 1, not 2',
+ name: 'meter'
+ }
+ ]
- []
at loadedTree (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:22:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:121:13)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ { event: 'plugin-refused', kind: 'usage-source', name: 'meter', code: 'interface-version', message: 'usage-source meter implements interface 1, not 2', kept: [] } ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:134:1
✖ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (1.977788ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ {
+ code: 'interface-version',
+ event: 'plugin-refused',
+ kept: [],
+ kind: 'usage-source',
+ message: 'usage-source meter implements interface 1, not 2',
+ name: 'meter'
+ }
+ ]
- []
at loadedTree (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:22:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:135:13)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ { event: 'plugin-refused', kind: 'usage-source', name: 'meter', code: 'interface-version', message: 'usage-source meter implements interface 1, not 2', kept: [] } ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:142:1
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (1.998245ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ {
+ code: 'interface-version',
+ event: 'plugin-refused',
+ kept: [],
+ kind: 'usage-source',
+ message: 'usage-source meter implements interface 1, not 2',
+ name: 'meter'
+ }
+ ]
- []
at loadedTree (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:22:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:143:13)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ { event: 'plugin-refused', kind: 'usage-source', name: 'meter', code: 'interface-version', message: 'usage-source meter implements interface 1, not 2', kept: [] } ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:157:1
✖ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.129628ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ {
+ code: 'interface-version',
+ event: 'plugin-refused',
+ kept: [],
+ kind: 'usage-source',
+ message: 'usage-source meter implements interface 1, not 2',
+ name: 'meter'
+ }
+ ]
- []
at loadedTree (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:22:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:158:13)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ { event: 'plugin-refused', kind: 'usage-source', name: 'meter', code: 'interface-version', message: 'usage-source meter implements interface 1, not 2', kept: [] } ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:165:1
✖ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (2.007332ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
'meter' !== 'absent'
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:178:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'meter',
expected: 'absent',
operator: 'strictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:182:1
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.384492ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ {
+ code: 'interface-version',
+ event: 'plugin-refused',
+ kept: [],
+ kind: 'usage-source',
+ message: 'usage-source meter implements interface 1, not 2',
+ name: 'meter'
+ }
+ ]
- []
at loadedTree (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:22:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:183:13)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ { event: 'plugin-refused', kind: 'usage-source', name: 'meter', code: 'interface-version', message: 'usage-source meter implements interface 1, not 2', kept: [] } ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:203:1
✖ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (1.753096ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ {
+ code: 'interface-version',
+ event: 'plugin-refused',
+ kept: [],
+ kind: 'usage-source',
+ message: 'usage-source meter implements interface 1, not 2',
+ name: 'meter'
+ }
+ ]
- []
at loadedTree (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:22:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:204:13)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ { event: 'plugin-refused', kind: 'usage-source', name: 'meter', code: 'interface-version', message: 'usage-source meter implements interface 1, not 2', kept: [] } ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,50 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.833324ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.351686ms)
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.261896ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (3.081717ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.242198ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (26.898108ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.640177ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.812263ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.885226ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.529495ms)
✖ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.461865ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.649674ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.375855ms)
✔ REQ-PLUG-1 the loader needs a log callback (0.922027ms)
ℹ tests 14
ℹ suites 0
ℹ pass 12
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 208.085813
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:71:1
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.261896ms)
Error: ENOENT: no such file or directory, lstat '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-Ti9sln/packages/plugins/usage-source/absent'
at lstatSync (node:fs:2138:25)
at component (file:///home/jwoltje/dewey-scratch/tmp/mosaic-plugins-Ti9sln/packages/plugins/src/loader.mjs:25:10)
at loadModule (file:///home/jwoltje/dewey-scratch/tmp/mosaic-plugins-Ti9sln/packages/plugins/src/loader.mjs:42:3)
at apply (file:///home/jwoltje/dewey-scratch/tmp/mosaic-plugins-Ti9sln/packages/plugins/src/loader.mjs:115:27) {
errno: -2,
code: 'ENOENT',
syscall: 'lstat',
path: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-Ti9sln/packages/plugins/usage-source/absent'
}
test at packages/plugins/tests/loader.test.mjs:165:1
✖ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.461865ms)
Error: ENOENT: no such file or directory, lstat '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-BynJ1f/packages/plugins/usage-source/absent'
at lstatSync (node:fs:2138:25)
at component (file:///home/jwoltje/dewey-scratch/tmp/mosaic-plugins-BynJ1f/packages/plugins/src/loader.mjs:25:10)
at loadModule (file:///home/jwoltje/dewey-scratch/tmp/mosaic-plugins-BynJ1f/packages/plugins/src/loader.mjs:42:3)
at apply (file:///home/jwoltje/dewey-scratch/tmp/mosaic-plugins-BynJ1f/packages/plugins/src/loader.mjs:115:27) {
errno: -2,
code: 'ENOENT',
syscall: 'lstat',
path: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-BynJ1f/packages/plugins/usage-source/absent'
}
@@ -0,0 +1,44 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.877318ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.364457ms)
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (4.180545ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.43889ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.015758ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (27.897067ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.677038ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.845477ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (4.07145ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.490785ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.70964ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.407065ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.77907ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.005272ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 215.391262
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:71:1
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (4.180545ms)
AssertionError [ERR_ASSERTION]: dir-index: usage-source dir-index failed to load: Directory import '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-2d8rfF/packages/plugins/usage-source/dir-index/index.mjs' is not supported resolving ES modules imported from /home/jwoltje/dewey-scratch/tmp/mosaic-plugins-2d8rfF/packages/plugins/src/loader.mjs
+ actual - expected
+ 'load-failed'
- 'missing'
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:35:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:77:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 'load-failed',
expected: 'missing',
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,87 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.88192ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.258634ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.253365ms)
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.617817ms)
✖ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.004227ms)
✖ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (26.289127ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.536292ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.911378ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.87808ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.462569ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.668134ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.46598ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.358423ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.097693ms)
ℹ tests 14
ℹ suites 0
ℹ pass 11
ℹ fail 3
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 212.729308
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:80:1
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.617817ms)
AssertionError [ERR_ASSERTION]: linked: /home/jwoltje/dewey-scratch/tmp/mosaic-plugins-nN0MjR/packages/plugins/usage-source/linked is not a directory
+ actual - expected
+ 'missing'
- 'symlink'
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:35:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:84:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 'missing',
expected: 'symlink',
operator: 'strictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:91:1
✖ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.004227ms)
AssertionError [ERR_ASSERTION]: meter: /home/jwoltje/dewey-scratch/tmp/mosaic-plugins-hKFPYI/packages/plugins/usage-source is not a directory
+ actual - expected
+ 'missing'
- 'symlink'
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:35:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:96:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 'missing',
expected: 'symlink',
operator: 'strictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:99:1
✖ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (26.289127ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+ actual - expected
+ 'missing'
- 'symlink'
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:115:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'missing',
expected: 'symlink',
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,49 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.998377ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.143351ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.865685ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.848392ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.099821ms)
✖ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (26.435447ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.569113ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.773836ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.82258ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.429166ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.71285ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.428391ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.65503ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.099315ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 214.429386
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:99:1
✖ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (26.435447ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ 'meter'
+ ]
- []
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:114:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ 'meter' ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,42 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.593701ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.212895ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.888251ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.509948ms)
✖ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.306208ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (27.377016ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.551264ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.747411ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.81103ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.484707ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.722459ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.32935ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (101.563951ms)
✔ REQ-PLUG-1 the loader needs a log callback (0.939414ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 212.880268
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:91:1
✖ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.306208ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /plugins\/usage-source is a symlink/. Input:
'/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-UIXF8X/packages/plugins/usage-source/meter/index.mjs resolves to /home/jwoltje/dewey-scratch/tmp/mosaic-plugins-UIXF8X/packages/plugins/usage-real/meter/index.mjs'
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:39:23)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:96:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-UIXF8X/packages/plugins/usage-source/meter/index.mjs resolves to /home/jwoltje/dewey-scratch/tmp/mosaic-plugins-UIXF8X/packages/plugins/usage-real/meter/index.mjs',
expected: /plugins\/usage-source is a symlink/,
operator: 'match',
diff: 'simple'
}
@@ -0,0 +1,60 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (7.069078ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.363505ms)
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.770453ms)
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.769557ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.135156ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (25.54869ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.563781ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.671021ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.916433ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.531167ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.659198ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.33605ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (103.00535ms)
✔ REQ-PLUG-1 the loader needs a log callback (0.977649ms)
ℹ tests 14
ℹ suites 0
ℹ pass 12
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 211.789602
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:71:1
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.770453ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /absent is missing/. Input:
'/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-lzldLH/packages/plugins/usage-source/absent/index.mjs is missing'
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:39:23)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:73:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-lzldLH/packages/plugins/usage-source/absent/index.mjs is missing',
expected: /absent is missing/,
operator: 'match',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:80:1
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.769557ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /usage-source\/linked is a symlink/. Input:
'/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-6cGaEj/packages/plugins/usage-source/linked/index.mjs resolves to /home/jwoltje/dewey-scratch/tmp/mosaic-plugins-6cGaEj/packages/plugins/usage-source/meter/index.mjs'
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:39:23)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:84:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-6cGaEj/packages/plugins/usage-source/linked/index.mjs resolves to /home/jwoltje/dewey-scratch/tmp/mosaic-plugins-6cGaEj/packages/plugins/usage-source/meter/index.mjs',
expected: /usage-source\/linked is a symlink/,
operator: 'match',
diff: 'simple'
}
@@ -0,0 +1,62 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.669425ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.288117ms)
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.996879ms)
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.389418ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (1.958031ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (26.137076ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.648837ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.830627ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.837263ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.514245ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.731791ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.506587ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.096339ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.089699ms)
ℹ tests 14
ℹ suites 0
ℹ pass 12
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 211.346926
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:71:1
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.996879ms)
AssertionError [ERR_ASSERTION]: dir-index: usage-source dir-index failed to load: Directory import '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-FLeWxh/packages/plugins/usage-source/dir-index/index.mjs' is not supported resolving ES modules imported from /home/jwoltje/dewey-scratch/tmp/mosaic-plugins-FLeWxh/packages/plugins/src/loader.mjs
+ actual - expected
+ 'load-failed'
- 'missing'
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:35:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:77:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: 'load-failed',
expected: 'missing',
operator: 'strictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:80:1
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.389418ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /link-index\/index\.mjs is a symlink/. Input:
'/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-Ptff10/packages/plugins/usage-source/link-index/index.mjs resolves to /home/jwoltje/dewey-scratch/tmp/mosaic-plugins-Ptff10/packages/plugins/usage-source/meter/index.mjs'
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:39:23)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:88:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-Ptff10/packages/plugins/usage-source/link-index/index.mjs resolves to /home/jwoltje/dewey-scratch/tmp/mosaic-plugins-Ptff10/packages/plugins/usage-source/meter/index.mjs',
expected: /link-index\/index\.mjs is a symlink/,
operator: 'match',
diff: 'simple'
}
@@ -0,0 +1,51 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.922864ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.390817ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.223786ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.351304ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.026554ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (25.327643ms)
✖ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (3.412784ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.773448ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (4.081731ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.556199ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.771353ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.509257ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.911073ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.097565ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 208.743698
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:119:1
✖ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (3.412784ms)
AssertionError [ERR_ASSERTION]: swapped: old setting kept
+ actual - expected
{
'limit-check': [
'cap'
],
'usage-source': [
+ 'swapped'
- 'meter'
]
}
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:32:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:131:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [ 'swapped' ], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,38 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.809289ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.219533ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.187227ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.249774ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (1.990612ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (24.645016ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.530895ms)
✖ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.190033ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.72017ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.421533ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (2.334617ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.518576ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.747627ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.008007ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 208.82718
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:134:1
✖ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.190033ms)
SyntaxError: Unexpected end of input
at compileSourceTextModule (node:internal/modules/esm/utils:355:16)
at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:93:18)
at #translate (node:internal/modules/esm/loader:441:20)
at afterLoad (node:internal/modules/esm/loader:509:29)
at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:514:12)
at #getOrCreateModuleJobAfterResolve (node:internal/modules/esm/loader:567:36)
at afterResolve (node:internal/modules/esm/loader:614:52)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:620:12)
at node:internal/modules/esm/loader:639:32
at TracingChannel.tracePromise (node:diagnostics_channel:547:22)
@@ -0,0 +1,42 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (7.406725ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.475628ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.308359ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.407971ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.040936ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (26.352842ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.47606ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.68165ms)
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.066011ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.569421ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.71873ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.363872ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (103.290582ms)
✔ REQ-PLUG-1 the loader needs a log callback (0.912748ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 215.51481
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:142:1
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.066011ms)
AssertionError [ERR_ASSERTION]: The input did not match the regular expression /doesn't export readTurn/. Input:
"usage-source no-read export readTurn isn't a function"
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:39:23)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:145:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: "usage-source no-read export readTurn isn't a function",
expected: /doesn't export readTurn/,
operator: 'match',
diff: 'simple'
}
@@ -0,0 +1,51 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.844388ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.22488ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.207108ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.403113ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (1.954469ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (25.825422ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.6038ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.753518ms)
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (4.45008ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.463883ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.757319ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.496356ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.668213ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.054778ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 210.457666
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:142:1
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (4.45008ms)
AssertionError [ERR_ASSERTION]: not-fn: old setting kept
+ actual - expected
{
'limit-check': [
'cap'
],
'usage-source': [
+ 'not-fn'
- 'meter'
]
}
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:32:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:149:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [ 'not-fn' ], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,51 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.986754ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.379131ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.511931ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.699931ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.105484ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (25.094191ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.606943ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (3.001531ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.8912ms)
✖ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.832185ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.757138ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.528069ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (103.008723ms)
✔ REQ-PLUG-1 the loader needs a log callback (0.954085ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 213.202544
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:157:1
✖ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.832185ms)
AssertionError [ERR_ASSERTION]: v2: old setting kept
+ actual - expected
{
'limit-check': [
'cap'
],
'usage-source': [
+ 'v2'
- 'meter'
]
}
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:32:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:160:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [ 'v2' ], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,51 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.99163ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.465303ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.320673ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.422629ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.134835ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (29.945876ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.660907ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (3.034549ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (7.001891ms)
✖ REQ-PLUG-1 a different interface version refuses and keeps the old setting (3.310869ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.683265ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.451737ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.625381ms)
✔ REQ-PLUG-1 the loader needs a log callback (0.997343ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 215.248193
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:157:1
✖ REQ-PLUG-1 a different interface version refuses and keeps the old setting (3.310869ms)
AssertionError [ERR_ASSERTION]: v-string: old setting kept
+ actual - expected
{
'limit-check': [
'cap'
],
'usage-source': [
+ 'v-string'
- 'meter'
]
}
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:32:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:162:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [ 'v-string' ], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,239 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.8395ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.239847ms)
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.596576ms)
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.113078ms)
✖ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (1.991423ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (29.529134ms)
✖ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.697901ms)
✖ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.43814ms)
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (2.263911ms)
✖ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.635264ms)
✖ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.802495ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.450199ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (103.301695ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.139123ms)
ℹ tests 14
ℹ suites 0
ℹ pass 6
ℹ fail 8
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 214.201489
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:71:1
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.596576ms)
AssertionError [ERR_ASSERTION]: empty: old setting kept
+ actual - expected
{
'limit-check': [
'cap'
],
+ 'usage-source': []
- 'usage-source': [
- 'meter'
- ]
}
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:32:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:75:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:80:1
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.113078ms)
AssertionError [ERR_ASSERTION]: linked: old setting kept
+ actual - expected
{
'limit-check': [
'cap'
],
+ 'usage-source': []
- 'usage-source': [
- 'meter'
- ]
}
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:32:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:84:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:91:1
✖ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (1.991423ms)
AssertionError [ERR_ASSERTION]: meter: old setting kept
+ actual - expected
{
'limit-check': [
'cap'
],
+ 'usage-source': []
- 'usage-source': [
- 'meter'
- ]
}
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:32:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:96:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:119:1
✖ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.697901ms)
AssertionError [ERR_ASSERTION]: swapped: old setting kept
+ actual - expected
{
'limit-check': [
'cap'
],
+ 'usage-source': []
- 'usage-source': [
- 'meter'
- ]
}
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:32:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:131:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:134:1
✖ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.43814ms)
AssertionError [ERR_ASSERTION]: syntax: old setting kept
+ actual - expected
{
'limit-check': [
'cap'
],
+ 'usage-source': []
- 'usage-source': [
- 'meter'
- ]
}
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:32:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:137:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:142:1
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (2.263911ms)
AssertionError [ERR_ASSERTION]: no-read: old setting kept
+ actual - expected
{
'limit-check': [
'cap'
],
+ 'usage-source': []
- 'usage-source': [
- 'meter'
- ]
}
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:32:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:145:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:157:1
✖ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.635264ms)
AssertionError [ERR_ASSERTION]: v2: old setting kept
+ actual - expected
{
'limit-check': [
'cap'
],
+ 'usage-source': []
- 'usage-source': [
- 'meter'
- ]
}
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:32:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:160:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:165:1
✖ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.802495ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
'limit-check': [
'cap'
],
+ 'usage-source': [
+ 'meter'
+ ]
- 'usage-source': []
}
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:172:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,50 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (7.718935ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.889936ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.781559ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.560221ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.145269ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (25.53739ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.714359ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.87242ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.845104ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.528986ms)
✖ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (2.540733ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.524789ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.77856ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.126618ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 216.678541
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:165:1
✖ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (2.540733ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
'limit-check': [
'cap'
],
+ 'usage-source': [
+ 'meter'
+ ]
- 'usage-source': []
}
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:172:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,331 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (7.182235ms)
✖ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.222902ms)
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.65768ms)
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.694535ms)
✖ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.361319ms)
✖ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (25.628682ms)
✖ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.698805ms)
✖ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.4078ms)
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (2.384788ms)
✖ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.870483ms)
✖ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.930742ms)
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.492929ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (103.153904ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.002101ms)
ℹ tests 14
ℹ suites 0
ℹ pass 3
ℹ fail 11
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 212.877553
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:64:1
✖ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.222902ms)
AssertionError [ERR_ASSERTION]: Meter: logged once
+ actual - expected
+ []
- [
- {
- code: 'bad-name',
- event: 'plugin-refused',
- kept: [
- 'meter'
- ],
- kind: 'usage-source',
- message: `plugins.usage-source names "Meter", which doesn't match /^[a-z][a-z0-9-]*$/`,
- name: 'Meter'
- }
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:40:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:67:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: [],
expected: [ { event: 'plugin-refused', kind: 'usage-source', name: 'Meter', code: 'bad-name', message: `plugins.usage-source names "Meter", which doesn't match /^[a-z][a-z0-9-]*$/`, kept: [Array] } ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:71:1
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.65768ms)
AssertionError [ERR_ASSERTION]: absent: logged once
+ actual - expected
+ []
- [
- {
- code: 'missing',
- event: 'plugin-refused',
- kept: [
- 'meter'
- ],
- kind: 'usage-source',
- message: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-fezqA2/packages/plugins/usage-source/absent is missing',
- name: 'absent'
- }
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:40:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:73:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: [],
expected: [ { event: 'plugin-refused', kind: 'usage-source', name: 'absent', code: 'missing', message: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-fezqA2/packages/plugins/usage-source/absent is missing', kept: [Array] } ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:80:1
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.694535ms)
AssertionError [ERR_ASSERTION]: linked: logged once
+ actual - expected
+ []
- [
- {
- code: 'symlink',
- event: 'plugin-refused',
- kept: [
- 'meter'
- ],
- kind: 'usage-source',
- message: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-s1cgVI/packages/plugins/usage-source/linked is a symlink',
- name: 'linked'
- }
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:40:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:84:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: [],
expected: [ { event: 'plugin-refused', kind: 'usage-source', name: 'linked', code: 'symlink', message: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-s1cgVI/packages/plugins/usage-source/linked is a symlink', kept: [Array] } ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:91:1
✖ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.361319ms)
AssertionError [ERR_ASSERTION]: meter: logged once
+ actual - expected
+ []
- [
- {
- code: 'symlink',
- event: 'plugin-refused',
- kept: [
- 'meter'
- ],
- kind: 'usage-source',
- message: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-rkYMNt/packages/plugins/usage-source is a symlink',
- name: 'meter'
- }
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:40:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:96:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: [],
expected: [ { event: 'plugin-refused', kind: 'usage-source', name: 'meter', code: 'symlink', message: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-rkYMNt/packages/plugins/usage-source is a symlink', kept: [Array] } ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:99:1
✖ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (25.628682ms)
TypeError: Cannot read properties of undefined (reading 'code')
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:115:30)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
test at packages/plugins/tests/loader.test.mjs:119:1
✖ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.698805ms)
AssertionError [ERR_ASSERTION]: swapped: logged once
+ actual - expected
+ []
- [
- {
- code: 'symlink',
- event: 'plugin-refused',
- kept: [
- 'meter'
- ],
- kind: 'usage-source',
- message: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-Wjfc7m/packages/plugins/usage-source/swapped/index.mjs resolves to /home/jwoltje/dewey-scratch/tmp/mosaic-plugins-Wjfc7m/packages/plugins/usage-source/meter/index.mjs',
- name: 'swapped'
- }
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:40:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:131:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: [],
expected: [ { event: 'plugin-refused', kind: 'usage-source', name: 'swapped', code: 'symlink', message: '/home/jwoltje/dewey-scratch/tmp/mosaic-plugins-Wjfc7m/packages/plugins/usage-source/swapped/index.mjs resolves to /home/jwoltje/dewey-scratch/tmp/mosaic-plugins-Wjfc7m/packages/plugins/usage-source/meter/index.mjs', kept: [Array] } ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:134:1
✖ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.4078ms)
AssertionError [ERR_ASSERTION]: syntax: logged once
+ actual - expected
+ []
- [
- {
- code: 'load-failed',
- event: 'plugin-refused',
- kept: [
- 'meter'
- ],
- kind: 'usage-source',
- message: 'usage-source syntax failed to load: Unexpected end of input',
- name: 'syntax'
- }
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:40:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:137:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: [],
expected: [ { event: 'plugin-refused', kind: 'usage-source', name: 'syntax', code: 'load-failed', message: 'usage-source syntax failed to load: Unexpected end of input', kept: [Array] } ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:142:1
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (2.384788ms)
AssertionError [ERR_ASSERTION]: no-read: logged once
+ actual - expected
+ []
- [
- {
- code: 'missing-export',
- event: 'plugin-refused',
- kept: [
- 'meter'
- ],
- kind: 'usage-source',
- message: "usage-source no-read doesn't export readTurn",
- name: 'no-read'
- }
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:40:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:145:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: [],
expected: [ { event: 'plugin-refused', kind: 'usage-source', name: 'no-read', code: 'missing-export', message: "usage-source no-read doesn't export readTurn", kept: [Array] } ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:157:1
✖ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.870483ms)
AssertionError [ERR_ASSERTION]: v2: logged once
+ actual - expected
+ []
- [
- {
- code: 'interface-version',
- event: 'plugin-refused',
- kept: [
- 'meter'
- ],
- kind: 'usage-source',
- message: 'usage-source v2 implements interface 2, not 1',
- name: 'v2'
- }
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:40:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:160:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: [],
expected: [ { event: 'plugin-refused', kind: 'usage-source', name: 'v2', code: 'interface-version', message: 'usage-source v2 implements interface 2, not 1', kept: [Array] } ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:165:1
✖ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.930742ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
0 !== 1
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:175:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 0,
expected: 1,
operator: 'strictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:182:1
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.492929ms)
AssertionError [ERR_ASSERTION]: meter: logged once
+ actual - expected
+ []
- [
- {
- code: 'bad-setting',
- event: 'plugin-refused',
- kept: [
- 'meter'
- ],
- kind: 'usage-source',
- message: 'plugins.usage-source must be a list of module names',
- name: 'meter'
- }
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:40:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:196:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: [],
expected: [ { event: 'plugin-refused', kind: 'usage-source', name: 'meter', code: 'bad-setting', message: 'plugins.usage-source must be a list of module names', kept: [Array] } ],
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,60 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.632796ms)
✖ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (2.753212ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.836629ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.810412ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.033716ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (27.31341ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.582536ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.719239ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (4.065324ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.731089ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.972685ms)
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.631946ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.316851ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.006712ms)
ℹ tests 14
ℹ suites 0
ℹ pass 12
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 208.891452
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:64:1
✖ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (2.753212ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
null !== 'Meter'
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:37:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:67:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: null,
expected: 'Meter',
operator: 'strictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:182:1
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.631946ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
null !== 'meter'
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:37:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:197:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: null,
expected: 'meter',
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,226 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (7.072996ms)
✖ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.122063ms)
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.514345ms)
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.271425ms)
✖ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.654462ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (25.55133ms)
✖ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.620809ms)
✖ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.342699ms)
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (2.32443ms)
✖ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.295485ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.817804ms)
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.353885ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (101.834887ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.114604ms)
ℹ tests 14
ℹ suites 0
ℹ pass 5
ℹ fail 9
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 208.112329
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:64:1
✖ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.122063ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- 'meter'
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:38:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:67:5)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [],
expected: [ 'meter' ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:71:1
✖ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (2.514345ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- 'meter'
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:38:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:73:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [],
expected: [ 'meter' ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:80:1
✖ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.271425ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- 'meter'
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:38:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:84:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [],
expected: [ 'meter' ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:91:1
✖ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.654462ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- 'meter'
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:38:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:96:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [],
expected: [ 'meter' ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:119:1
✖ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.620809ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- 'meter'
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:38:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:131:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [],
expected: [ 'meter' ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:134:1
✖ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.342699ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- 'meter'
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:38:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:137:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [],
expected: [ 'meter' ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:142:1
✖ REQ-PLUG-1 a missing export refuses and keeps the old setting (2.32443ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- 'meter'
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:38:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:145:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [],
expected: [ 'meter' ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:157:1
✖ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.295485ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- 'meter'
- ]
at refusesKeepingOld (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:38:10)
at async TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:160:3)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [],
expected: [ 'meter' ],
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:182:1
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.353885ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ []
- {
- 'limit-check': [
- 'cap'
- ],
- 'usage-source': [
- 'meter'
- ]
- }
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:193:12)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [],
expected: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,48 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.821612ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.263019ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.343928ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.599749ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.172649ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (28.376672ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.710912ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.787536ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (4.024711ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.526155ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.705364ms)
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.880786ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (101.916601ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.0899ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 215.858895
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:182:1
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.880786ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- [
- null,
- 'bad-setting'
- ]
- ]
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:192:12)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [],
expected: [ [ null, 'bad-setting' ] ],
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,48 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.915724ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.336951ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.097832ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.327882ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (1.970929ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (26.963609ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.61179ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.70741ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (4.25572ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.444816ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.691608ms)
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.514607ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.701729ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.179424ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 214.626067
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:182:1
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.514607ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ []
- [
- [
- 'made-up',
- 'unknown-kind'
- ]
- ]
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:185:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [],
expected: [ [ 'made-up', 'unknown-kind' ] ],
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,50 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.725011ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.293591ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.31681ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.420713ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (1.937332ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (26.147018ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.716228ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (3.283449ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.856648ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.570609ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.712432ms)
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (3.255117ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.485727ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.02065ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 212.674943
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:182:1
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (3.255117ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
{
'limit-check': [
'cap'
],
+ 'usage-source': [
+ 'meter'
+ ]
- 'usage-source': []
}
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:200:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: { 'usage-source': [ 'meter' ], 'limit-check': [ 'cap' ] },
expected: { 'usage-source': [], 'limit-check': [ 'cap' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,45 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (6.946924ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.245391ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.395265ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.517284ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.181388ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (26.223094ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.71118ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.794417ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.970293ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.546724ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.748063ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.564548ms)
✖ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (103.281619ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.564009ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 215.310585
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:203:1
✖ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (103.281619ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
[
+ 'slow'
- 'fast'
]
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:215:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ 'slow' ],
expected: [ 'fast' ],
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,38 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (8.133845ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.833528ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (4.715311ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.951368ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.210481ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (37.264201ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (3.680476ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (3.722472ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (6.169751ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (3.580114ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (3.074277ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (5.360314ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (103.789793ms)
✖ REQ-PLUG-1 the loader needs a log callback (2.626562ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 247.556609
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:218:1
✖ REQ-PLUG-1 the loader needs a log callback (2.626562ms)
AssertionError [ERR_ASSERTION]: Missing expected exception.
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:221:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
expected: /log callback/,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,72 @@
✖ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (5.909884ms)
✖ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (38.81033ms)
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (2.49137ms)
✖ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (4.753088ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (5.156712ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.941754ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (4.001745ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.995627ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (34.859618ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (3.431905ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (4.128101ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (5.468892ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (3.212562ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (2.572052ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.961947ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (103.237844ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.201948ms)
ℹ tests 17
ℹ suites 0
ℹ pass 14
ℹ fail 3
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 255.659538
✖ failing tests:
test at packages/business/tests/plugin-settings.test.mjs:28:1
✖ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (5.909884ms)
Error [BusinessError]: unsupported business file key: "plugins"
at refuse (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/errors.mjs:12:9)
at rejectUnknownKeys (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/util.mjs:17:33)
at validateBusinessDocument (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/business.mjs:140:3)
at check (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:30:22)
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:33:20)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
exitCode: 2
}
test at packages/business/tests/plugin-settings.test.mjs:42:1
✖ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (38.81033ms)
Error [BusinessError]: unsupported business file key: "plugins"
at refuse (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/errors.mjs:12:9)
at rejectUnknownKeys (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/util.mjs:17:33)
at validateBusinessDocument (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/business.mjs:140:3)
at file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/organization.mjs:186:7
at withLock (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/organization.mjs:128:14)
at Object.write (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/organization.mjs:180:12)
at Object.set (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/plugin-settings.mjs:33:28)
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:46:27)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25) {
exitCode: 2
}
test at packages/plugins/tests/loader.test.mjs:44:1
✖ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (4.753088ms)
Error [BusinessError]: unsupported business file key: "plugins"
at refuse (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/errors.mjs:12:9)
at rejectUnknownKeys (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/util.mjs:17:33)
at validateBusinessDocument (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/business.mjs:140:3)
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:53:20)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
exitCode: 2
}
@@ -0,0 +1,28 @@
✖ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (8.505894ms)
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (99.731971ms)
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.569722ms)
ℹ tests 3
ℹ suites 0
ℹ pass 2
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 167.144527
✖ failing tests:
test at packages/business/tests/plugin-settings.test.mjs:28:1
✖ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (8.505894ms)
AssertionError [ERR_ASSERTION]: Missing expected exception (BusinessError): {"made-up":[]}
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:38:12)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,77 @@
✖ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (5.953642ms)
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (114.027752ms)
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.942631ms)
✖ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (8.989553ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (4.728041ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.634174ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (3.569675ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.564529ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (30.475345ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (3.392ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (3.634274ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (4.922906ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (3.164925ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (2.283272ms)
✔ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.964489ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (103.294067ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.469376ms)
ℹ tests 17
ℹ suites 0
ℹ pass 15
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 247.482272
✖ failing tests:
test at packages/business/tests/plugin-settings.test.mjs:28:1
✖ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (5.953642ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
undefined !== {}
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:31:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: undefined,
expected: {},
operator: 'deepStrictEqual',
diff: 'simple'
}
test at packages/plugins/tests/loader.test.mjs:44:1
✖ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (8.989553ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ [
+ {
+ code: 'bad-setting',
+ event: 'plugin-refused',
+ kept: {
+ 'limit-check': [],
+ 'usage-source': []
+ },
+ kind: null,
+ message: 'plugins must be an object with one key per kind',
+ name: null
+ }
+ ]
- []
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:56:10)
at async Test.run (node:internal/test_runner/test:1409:7)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: [ { event: 'plugin-refused', kind: null, name: null, code: 'bad-setting', message: 'plugins must be an object with one key per kind', kept: [Object] } ],
expected: [],
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,29 @@
✖ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (5.453961ms)
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (109.660944ms)
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (2.284831ms)
ℹ tests 3
ℹ suites 0
ℹ pass 2
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 172.649417
✖ failing tests:
test at packages/business/tests/plugin-settings.test.mjs:28:1
✖ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (5.453961ms)
AssertionError [ERR_ASSERTION]: the document itself stays mutable
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:36:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
@@ -0,0 +1,30 @@
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (8.628537ms)
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (119.966425ms)
✖ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (58.039007ms)
ℹ tests 3
ℹ suites 0
ℹ pass 2
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 251.130511
✖ failing tests:
test at packages/business/tests/plugin-settings.test.mjs:63:1
✖ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (58.039007ms)
AssertionError [ERR_ASSERTION]: Missing expected exception: {"revision":"7d713440bac5d73780f79d370be9053848788f24bbda61b285a94841ac5b2a4a","plugins":{},"limits":{}}
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:81:12)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: undefined,
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,40 @@
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (9.309187ms)
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (99.966774ms)
✖ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (21.552171ms)
ℹ tests 3
ℹ suites 0
ℹ pass 2
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 191.132875
✖ failing tests:
test at packages/business/tests/plugin-settings.test.mjs:63:1
✖ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (21.552171ms)
AssertionError [ERR_ASSERTION]: {"revision":"nope","plugins":{}}
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:81:12)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: Error: organization revision changed; read again
at fail (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/organization.mjs:20:9)
at file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/organization.mjs:185:32
at withLock (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/organization.mjs:128:14)
at Object.write (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/organization.mjs:180:12)
at Object.set (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/plugin-settings.mjs:33:28)
at file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:81:36
at getActual (node:assert:580:5)
at strict.throws (node:assert:728:24)
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:81:12)
at Test.runInAsyncScope (node:async_hooks:226:14),
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,40 @@
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (7.506489ms)
✔ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (95.204781ms)
✖ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (21.754824ms)
ℹ tests 3
ℹ suites 0
ℹ pass 2
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 178.271573
✖ failing tests:
test at packages/business/tests/plugin-settings.test.mjs:63:1
✖ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (21.754824ms)
AssertionError [ERR_ASSERTION]: {"revision":"857491f64b4cf557c4158834969e269ac02d37ca0c68c2998bf03970570b050f","plugins":{"made-up":["x"]}}
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:81:12)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: BusinessError: /home/jwoltje/dewey-scratch/tmp/mosaic-business-awvc8I/businesses/acme.json plugins names unknown kind "made-up"; adding a kind is a core change
at refuse (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/errors.mjs:12:9)
at fail (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/business.mjs:185:101)
at checkPlugins (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/src/kinds.mjs:61:7)
at validateBusinessDocument (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/business.mjs:185:23)
at file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/organization.mjs:186:7
at withLock (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/organization.mjs:128:14)
at Object.write (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/organization.mjs:180:12)
at Object.set (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/src/plugin-settings.mjs:32:28)
at file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:81:36
at getActual (node:assert:580:5),
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,34 @@
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (7.094138ms)
✖ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (54.057314ms)
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.321911ms)
ℹ tests 3
ℹ suites 0
ℹ pass 2
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 110.902522
✖ failing tests:
test at packages/business/tests/plugin-settings.test.mjs:42:1
✖ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (54.057314ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
'host' !== 'jason'
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:52:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 'host',
expected: 'jason',
operator: 'strictEqual',
diff: 'simple'
}
@@ -0,0 +1,40 @@
✔ REQ-PLUG-1 schema: plugins is optional, names known kinds and checked module names (7.142387ms)
✖ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (55.630027ms)
✔ REQ-PLUG-1 REQ-ROLE-3 a refused setter write throws a code, changes nothing and logs nothing (1.215772ms)
ℹ tests 3
ℹ suites 0
ℹ pass 2
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 110.48639
✖ failing tests:
test at packages/business/tests/plugin-settings.test.mjs:42:1
✖ REQ-PLUG-1 REQ-ROLE-3 the setter writes plugins through the organization writer and logs old and new names (55.630027ms)
AssertionError [ERR_ASSERTION]: Expected values to be strictly deep-equal:
+ actual - expected
+ undefined
- {
- 'usage-source': [
- 'meter'
- ]
- }
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/business/tests/plugin-settings.test.mjs:47:10)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
at Test.postRun (node:internal/test_runner/test:1542:19)
at Test.run (node:internal/test_runner/test:1467:12)
at async startSubtestAfterBootstrap (node:internal/test_runner/harness:387:3) {
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: undefined,
expected: { 'usage-source': [ 'meter' ] },
operator: 'deepStrictEqual',
diff: 'simple'
}
@@ -0,0 +1,72 @@
✖ shared copy: npm ci, pushed reachability, readonly/existing copy, webui delegation (335.775914ms)
✔ wrapper preserves Q81 lease and marker, rejects foreign/symlink; check uses effective roots (374.038444ms)
✔ bus unit copy/pm pins, install refuses all busy states and failed reads, drop-in refusal (383.625565ms)
✔ missing package manifest refuses before npm; npm failure removes the unpublished temp copy (222.422533ms)
✖ real pinned install loads bus and Console; same-copy mosaic talk passes proof, other trees refuse (2216.368185ms)
✔ bus status reads effective copy/pm/drop-ins without writes; check refuses an overridden ExecStart (433.30476ms)
✔ drop-ins in either override field and malformed metadata refuse; uninstall also checks --no-reload (150.490483ms)
✔ install never shows a template name on systemd 261; template drop-ins still refuse (146.033353ms)
✔ unlisted instance drop-in is found in another unit lookup directory; path discovery errors refuse (137.711332ms)
✔ inherited mosaic-.service.d override refuses without changing the unit (156.933026ms)
✔ inherited service.d override refuses without changing the unit (154.245135ms)
✔ inherited drop-in unreadable directory or file refuses before writing (131.568501ms)
ℹ tests 12
ℹ suites 0
ℹ pass 10
ℹ fail 2
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 4893.420664
✖ failing tests:
test at packages/cli/tests/stack-copy.test.mjs:34:1
✖ shared copy: npm ci, pushed reachability, readonly/existing copy, webui delegation (335.775914ms)
AssertionError [ERR_ASSERTION]: packages/plugins/src/kinds.mjs
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/cli/tests/stack-copy.test.mjs:37:234)
at Test.runInAsyncScope (node:async_hooks:226:14)
at Test.run (node:internal/test_runner/test:1402:25)
at Test.start (node:internal/test_runner/test:1262:17)
at startSubtestAfterBootstrap (node:internal/test_runner/harness:387:17) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: false,
expected: true,
operator: '==',
diff: 'simple'
}
test at packages/cli/tests/stack-copy.test.mjs:101:1
✖ real pinned install loads bus and Console; same-copy mosaic talk passes proof, other trees refuse (2216.368185ms)
AssertionError [ERR_ASSERTION]: node:internal/modules/esm/resolve:272
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/jwoltje/dewey-scratch/tmp/stack-copy-pSa3Jz/copies/2a913bf71e8830fc2f219cf91b88251feec289fa/packages/plugins/src/kinds.mjs' imported from /home/jwoltje/dewey-scratch/tmp/stack-copy-pSa3Jz/copies/2a913bf71e8830fc2f219cf91b88251feec289fa/packages/business/src/business.mjs
at finalizeResolution (node:internal/modules/esm/resolve:272:11)
at moduleResolve (node:internal/modules/esm/resolve:879:10)
at defaultResolve (node:internal/modules/esm/resolve:1006:11)
at #cachedDefaultResolve (node:internal/modules/esm/loader:708:20)
at #resolveAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:728:38)
at ModuleLoader.resolveSync (node:internal/modules/esm/loader:766:56)
at #resolve (node:internal/modules/esm/loader:690:17)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:610:35)
at ModuleJob.syncLink (node:internal/modules/esm/module_job:277:33)
at ModuleJob.link (node:internal/modules/esm/module_job:389:17) {
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///home/jwoltje/dewey-scratch/tmp/stack-copy-pSa3Jz/copies/2a913bf71e8830fc2f219cf91b88251feec289fa/packages/plugins/src/kinds.mjs'
}
Node.js v26.8.1
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/cli/tests/stack-copy.test.mjs:111:84)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: '',
expected: /bus host up/,
operator: 'match',
diff: 'simple'
}
@@ -0,0 +1,44 @@
✔ REQ-PLUG-1 fixture modules of each kind switch on from a test organization file, no core file changed (7.745043ms)
✔ REQ-PLUG-1 a name not matching ^[a-z][a-z0-9-]*$ refuses and keeps the old setting (3.471768ms)
✔ REQ-PLUG-1 a missing module directory or index.mjs refuses and keeps the old setting (3.419603ms)
✔ REQ-PLUG-1 a symlinked module directory or index.mjs refuses and keeps the old setting (2.480328ms)
✔ REQ-PLUG-1 a symlinked kind directory refuses and keeps the old setting (2.213009ms)
✔ REQ-PLUG-1 a symlinked packages/plugins refuses (run with --preserve-symlinks so the path keeps it) (26.330237ms)
✔ REQ-PLUG-1 index.mjs swapped for a symlink after the lstat checks refuses at the realpath check (2.766962ms)
✔ REQ-PLUG-1 a module that fails to load refuses and keeps the old setting (2.782821ms)
✔ REQ-PLUG-1 a missing export refuses and keeps the old setting (3.897327ms)
✔ REQ-PLUG-1 a different interface version refuses and keeps the old setting (2.593477ms)
✔ REQ-PLUG-1 a kind refused on its first load has nothing switched on, and the log names kind and name (1.766915ms)
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.409866ms)
✔ REQ-PLUG-1 loads run one at a time, so the last requested setting wins (102.429521ms)
✔ REQ-PLUG-1 the loader needs a log callback (1.177561ms)
ℹ tests 14
ℹ suites 0
ℹ pass 13
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 221.228312
✖ failing tests:
test at packages/plugins/tests/loader.test.mjs:182:1
✖ REQ-PLUG-1 the setting's shape: unknown kinds refuse alone, a bad whole setting keeps everything (2.409866ms)
AssertionError [ERR_ASSERTION]: made-up
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:188:12)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7) {
generatedMessage: false,
code: 'ERR_ASSERTION',
actual: TypeError: Cannot read properties of undefined (reading 'map')
at Object.modules (file:///home/jwoltje/dewey-scratch/tmp/mosaic-plugins-ixWRKD/packages/plugins/src/loader.mjs:140:29)
at file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:188:34
at getActual (node:assert:580:5)
at strict.throws (node:assert:728:24)
at TestContext.<anonymous> (file:///home/jwoltje/dewey-scratch/r131/wt/packages/plugins/tests/loader.test.mjs:188:12)
at async Test.run (node:internal/test_runner/test:1409:7)
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7),
operator: 'throws',
diff: 'simple'
}
@@ -0,0 +1,429 @@
[
{
"id": "01-name-pattern",
"path": "packages/plugins/src/kinds.mjs",
"test": [
"packages/plugins/tests/kinds.test.mjs",
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "02-list-cardinality",
"path": "packages/plugins/src/kinds.mjs",
"test": [
"packages/plugins/tests/kinds.test.mjs",
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "03-one-cardinality",
"path": "packages/plugins/src/kinds.mjs",
"test": [
"packages/plugins/tests/kinds.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "04-no-duplicates",
"path": "packages/plugins/src/kinds.mjs",
"test": [
"packages/plugins/tests/kinds.test.mjs",
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "05-known-kind",
"path": "packages/plugins/src/kinds.mjs",
"test": [
"packages/plugins/tests/kinds.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "06-plugins-object",
"path": "packages/plugins/src/kinds.mjs",
"test": [
"packages/plugins/tests/kinds.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "07-registry-exports",
"path": "packages/plugins/src/kinds.mjs",
"test": [
"packages/plugins/tests/kinds.test.mjs",
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "08-registry-version",
"path": "packages/plugins/src/kinds.mjs",
"test": [
"packages/plugins/tests/kinds.test.mjs",
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "09-missing-directory",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "10-component-type",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "11-symlink-test",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "12-plugins-dir-checked",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "13-kind-dir-checked",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "14-name-dir-checked",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "15-index-checked",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "16-realpath-before-import",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "17-load-failure",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "18-export-present",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "19-export-function",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "20-interface-version",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "21-version-strict",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "22-keep-old-setting",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "23-first-load-nothing-on",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "24-refusal-logged",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "25-log-names-module",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "26-log-kept",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "27-whole-setting-object",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "28-unknown-kind-logged",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "29-absent-key-off",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "30-loads-serialized",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "31-log-required",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "42-modules-known-kind",
"path": "packages/plugins/src/loader.mjs",
"test": [
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "32-schema-key",
"path": "packages/business/src/business.mjs",
"test": [
"packages/business/tests/plugin-settings.test.mjs",
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "33-schema-check",
"path": "packages/business/src/business.mjs",
"test": [
"packages/business/tests/plugin-settings.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "34-schema-returned",
"path": "packages/business/src/business.mjs",
"test": [
"packages/business/tests/plugin-settings.test.mjs",
"packages/plugins/tests/loader.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "35-schema-copy",
"path": "packages/business/src/business.mjs",
"test": [
"packages/business/tests/plugin-settings.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "36-setter-strict-keys",
"path": "packages/business/src/plugin-settings.mjs",
"test": [
"packages/business/tests/plugin-settings.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "37-setter-revision-format",
"path": "packages/business/src/plugin-settings.mjs",
"test": [
"packages/business/tests/plugin-settings.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "38-setter-checks-names",
"path": "packages/business/src/plugin-settings.mjs",
"test": [
"packages/business/tests/plugin-settings.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "39-setter-actor-logged",
"path": "packages/business/src/plugin-settings.mjs",
"test": [
"packages/business/tests/plugin-settings.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "40-setter-writes-plugins",
"path": "packages/business/src/plugin-settings.mjs",
"test": [
"packages/business/tests/plugin-settings.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
},
{
"id": "41-copy-ships-plugins",
"path": "scripts/stack-copy.sh",
"test": [
"packages/cli/tests/stack-copy.test.mjs"
],
"result": "KILLED",
"exit": 1,
"sites": 1
}
]
@@ -0,0 +1,57 @@
c3ef59884b9a37046393fa1b275acc9d0d5246a91b68ea984b8f1151d72bb67e agents/dewey/work/queue-131/r1/base.txt
9d2e752cf867d0c305106499316e80644abbee81aa76af77ef1532adef1a1400 agents/dewey/work/queue-131/r1/BUILD.md
47eb6a84112ff9a6950d34cf8ff61e241c1378346d9f73e973654eda8d00c1f1 agents/dewey/work/queue-131/r1/build.patch
80b4ce21ac419bd3a57d5e4f842790f684a9a88ce1653da838c8953a2bbd27e5 agents/dewey/work/queue-131/r1/candidate-manifest.sha256
8e5672baa0c759580df057f287d926bc8d102e61d371f88077aa87ef71e21105 agents/dewey/work/queue-131/r1/gate/pkg-business.log
adc752ea19487413c9eb2786d6fac03a2aa99ae7829a3ce65dc0f1abdb9977e0 agents/dewey/work/queue-131/r1/gate/pkg-bus.log
1214da0b593afe454f640860f936d95f0bb095aae5d2a256e15b968b0c4861e5 agents/dewey/work/queue-131/r1/gate/pkg-cli.log
7b25f88820beadaeaf1fe8b27fbac202144c2087f07e2ef02a9bb4e5c960d46f agents/dewey/work/queue-131/r1/gate/pkg-plugins.log
8b93e798ed5f0014b9efa760aae2b742924093de4c8fead8ab2863412d383d7f agents/dewey/work/queue-131/r1/gate/pkg-webui.log
405eeb04c81623c9d577f6ca83c2025b2b9e76c37945450b342e9f899edd2b3a agents/dewey/work/queue-131/r1/gate-run.log
a9b6f95d44b7e7a822d8e6fb8cae27cd71dad5faef151e8c1550659bd1ebeb8f agents/dewey/work/queue-131/r1/gate.sh
89df99c041a3d157e6b3e4d9f843f829bb13374be4623e6eec119e301a933ad2 agents/dewey/work/queue-131/r1/mutants/01-name-pattern.log
23f7432c8bd9e0a17d8931f29cb4fe1498033eeacdc0831cc38d913787f20c3c agents/dewey/work/queue-131/r1/mutants/02-list-cardinality.log
0f8347f5bd2539cf1080bf94f46e0840c5624633a2a886d96ea3cf040954f672 agents/dewey/work/queue-131/r1/mutants/03-one-cardinality.log
0a90b93de0cb5bc8567a33696d3468cae50fe71a44747e5900ab00e3e4c2883b agents/dewey/work/queue-131/r1/mutants/04-no-duplicates.log
57f4ed3cbd6b59fa363eaabde5f570ae2e6318dee1b49d28300004e5c4bcf28f agents/dewey/work/queue-131/r1/mutants/05-known-kind.log
1fe904c0fdf50b4d2ae046e4cb55855418f64b87df6b411f98b4f17bce2c95da agents/dewey/work/queue-131/r1/mutants/06-plugins-object.log
2edf8819fe4e06d424f3a512a19510b2718703b5d76ef90934cb71d448cadc2f agents/dewey/work/queue-131/r1/mutants/07-registry-exports.log
e208344ce5564804a377133c00da6d14cd0ba5c91ff3f4058bf14950db6cdc2a agents/dewey/work/queue-131/r1/mutants/08-registry-version.log
13eb8b1400d9c891717e26da7f7d70a3db78d815d17253ac788d830d55e9c8ee agents/dewey/work/queue-131/r1/mutants/09-missing-directory.log
c9528aefc67dc06c74c8f4ace0a052965594be510c0599862dc29c03ac563464 agents/dewey/work/queue-131/r1/mutants/10-component-type.log
f5c9efe600d8187c4cf289af28c34fccdade8a2b5f57a954ede8c6830b8c455e agents/dewey/work/queue-131/r1/mutants/11-symlink-test.log
63de70674ca1e08cb438ac9ed9773dfdcbadefe31240947d85a1cee5805c098b agents/dewey/work/queue-131/r1/mutants/12-plugins-dir-checked.log
60a08c05d519b1647b38783232f4b02d5b6426a5d30578f65a09ea3531d86ada agents/dewey/work/queue-131/r1/mutants/13-kind-dir-checked.log
4ef19feb906ca0f4e08344f7897ebf55cdd66858b234bae8452156826cbd559a agents/dewey/work/queue-131/r1/mutants/14-name-dir-checked.log
1e5d799983277c2ff63dc8800d012f2242e26645e7493369efa35919616bcd18 agents/dewey/work/queue-131/r1/mutants/15-index-checked.log
cd2a3cfcdef0a99dcae9d7e29362555541f9c079df9a82ca757ac583f5b04f56 agents/dewey/work/queue-131/r1/mutants/16-realpath-before-import.log
8a268b15b4ee887b2b97f20fc64cb1d7f18a570c473ac4cc76ae02c6558c5508 agents/dewey/work/queue-131/r1/mutants/17-load-failure.log
a87ae8b2e5f1a7fc7aba8147da5e3492c50b335c219afb4ef0d7090347209d07 agents/dewey/work/queue-131/r1/mutants/18-export-present.log
6785b4c97be4bd002c978c598a12133d1c5acb6357313ceb9e7a1d56007b9851 agents/dewey/work/queue-131/r1/mutants/19-export-function.log
d04b8829cd992075915c4dad99dd1de514a72b71e2daa3754f49ceac6a86ce5a agents/dewey/work/queue-131/r1/mutants/20-interface-version.log
7fdd33177df9f4989c3df83b98be5804e43c7fce199e4ea6321324a4c32d809c agents/dewey/work/queue-131/r1/mutants/21-version-strict.log
1418da52940e1e6f953220d657bb071a5181e4f3acf903adf280abc82a184bbe agents/dewey/work/queue-131/r1/mutants/22-keep-old-setting.log
43f1277ddd8446bce328cf974c35f543e8b3e3c740b0f8e46613c2dec9ea522e agents/dewey/work/queue-131/r1/mutants/23-first-load-nothing-on.log
98aecca2d7fc77696db96ccb3e9b3aee8698fc30f6820d6a3d3990fa2cdb6855 agents/dewey/work/queue-131/r1/mutants/24-refusal-logged.log
1c9bbcd4677cd7364653241ce02731d58d15db9f2543fab56d5c428cbfab18b4 agents/dewey/work/queue-131/r1/mutants/25-log-names-module.log
7760c3ad76c7ab65f07a3bbd907b5da1b5f8e600e1125a71a957cc08abd88a84 agents/dewey/work/queue-131/r1/mutants/26-log-kept.log
341632a7b3383b18de4f8f85dab31742a8e194ca551fb8c52ab94ff0504ce3c9 agents/dewey/work/queue-131/r1/mutants/27-whole-setting-object.log
efb87db99b3edb756492d483fd3ce0217de4bffc18751c068887ff3f153d6354 agents/dewey/work/queue-131/r1/mutants/28-unknown-kind-logged.log
a724428b430031dc444b2430a01f2227a669e1ab3e0dc97477f7d388fa3f78f0 agents/dewey/work/queue-131/r1/mutants/29-absent-key-off.log
f5adf4ce0c9fd90cb48025954abf1d0745591ef103e9bee900ba5d4e9865bdf4 agents/dewey/work/queue-131/r1/mutants/30-loads-serialized.log
8893b780118d312f49470f7dfd01681b40cb504953e62d19f8106b4241ac3e86 agents/dewey/work/queue-131/r1/mutants/31-log-required.log
f249067c7c0113107ba658f1967cb439f3060c658654bf32e6c96fbf30e1455c agents/dewey/work/queue-131/r1/mutants/32-schema-key.log
a436d600929e7c8598ccb9872c7f52c97d8d733484a65b0c5034512ddcc25519 agents/dewey/work/queue-131/r1/mutants/33-schema-check.log
3f0f599cf19a99eab5e762bb046ffd30f7e74fa67e58a76c080c142d4171ad7e agents/dewey/work/queue-131/r1/mutants/34-schema-returned.log
2c4d525cb1483c7a71475e5597eb652f9fafe04141ec02b77ea6200abdf4f796 agents/dewey/work/queue-131/r1/mutants/35-schema-copy.log
b9bb61bd5d8d3758d8d97787b74e885c1b14931eb0d0667dd4195443d0a173f4 agents/dewey/work/queue-131/r1/mutants/36-setter-strict-keys.log
464218397b4dcf106725b259bc2195e6a99d85641f8e2c88f4eba056702fffab agents/dewey/work/queue-131/r1/mutants/37-setter-revision-format.log
52fc1dfc49a2eea08c1ac26ea20b120ed637859c2a62caf2bd8c760c5fb32121 agents/dewey/work/queue-131/r1/mutants/38-setter-checks-names.log
50111b32180b07269724ad707914db6f23a19ec0c8d50e1a1a4e1e2c2e27641e agents/dewey/work/queue-131/r1/mutants/39-setter-actor-logged.log
8ccf6d25d1c6fcc18c772ee19e9fbd713e50744b9cb6c41807333479bbd8a9e8 agents/dewey/work/queue-131/r1/mutants/40-setter-writes-plugins.log
35be573fe968be669fbdf8bb1d62932cd500e219e194d80f8e50344e8bc6db2c agents/dewey/work/queue-131/r1/mutants/41-copy-ships-plugins.log
0acedd36d78c2f7c4d543b50e45abb2ec62f3542c67fff97de46e9d9078ba6d1 agents/dewey/work/queue-131/r1/mutants/42-modules-known-kind.log
17c5a2545269118e92ca2a71f9f691bc0db3594adfa45784e5f0cc5eeb336a6d agents/dewey/work/queue-131/r1/MUTANTS.md
1cf81cdff754bfb6b8133f00e4128cc077f049590641c558b17a8e34f23b135e agents/dewey/work/queue-131/r1/mutants.py
42dd219e424d538123e40b9c8601508ec1c15be491d101b7f88b0bd8a2708984 agents/dewey/work/queue-131/r1/mutants/results.json
dcd947e7c5c33aa970f0786b8bd0ac4d1ed533e1d9e61892e7df002e56a43205 agents/dewey/work/queue-131/r1/mutants-run.log