bus: tester identity for PM tests (row 110, #1604)

A tester binds with a process proof from the bus's own tester-cli.mjs
when the business var bus.tester is true. It may send [mos test] messages
to a role, receive replies on the tester address and use the read verbs;
every other verb refuses. Messages store from_role tester and write no
human.input event. turnRequest() marks them as never Jason's instruction.
human.mjs is unchanged.

Candidate manifest bdaf7f476abd0b2b5095ab2f7f43e38540e6ba48ee14a02a4f4995a1469b79df
(Darkwing round 2, packet 9f1563ec1), BASE badfa52b9. Approved by Filbert
(comment 28360) and Sage (comment 28361). Sage gate rerun passed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-13 20:43:40 -05:00
co-authored by Claude Opus 5.5
parent 67ac176f2a
commit 179697a8a3
19 changed files with 822 additions and 41 deletions
+4
View File
@@ -141,6 +141,10 @@ each layer that sets one narrows it, and the result also narrows the role
definition. `limits.authority` is an allowlist, so a role action it leaves
out becomes gated.
`bus.tester` is business-only and defaults to false. Set to `true`, the bus
binds a tester from `tester-cli.mjs` for PM tests; it reads the value at bus
start, so a change takes a bus restart.
`resolve` prints `provenance` beside `vars`. For a plain key it's the
layer that set the value (`default`, `system`, `business:<id>`,
`project:<id>`, `project:<id>:roles.<instance>`,
+2
View File
@@ -96,6 +96,8 @@ export const REGISTRY = Object.freeze({
return v;
}),
"gitea.baseUrl": def(["business"], baseUrl),
// On: the bus binds a tester from tester-cli.mjs for PM tests (row 110). Read at bus start.
"bus.tester": def(["business"], boolean, { default: false }),
// Project.
"tracker.project": def(["project"], (v, key) => requirePositiveInt(v, `variable ${key}`)),
+8 -1
View File
@@ -33,7 +33,7 @@ test("unknown keys and wrong layers refuse", () => {
test("types", () => {
const ok = (vars, layer) => assert.deepEqual({ ...checkVars(vars, layer, "v") }, vars);
ok({ "tracker.baseUrl": "https://tasks.example:3456", "tracker.reconcileMinutes": 1, "tracker.pollSeconds": 10, "human.discordUserId": "123456789012345678" }, "business");
ok({ "tracker.baseUrl": "https://tasks.example:3456", "tracker.reconcileMinutes": 1, "tracker.pollSeconds": 10, "human.discordUserId": "123456789012345678", "bus.tester": true }, "business");
ok({ "tracker.project": 3, "git.workingBranch": "refactor", "git.protectedBranches": ["main", "next"], suites: ["scripts/test-task.sh"], "issues.repo": "mosaicstack/stack", "tracker.descendants": true }, "project");
ok({ harness: "claude-code", model: "claude-opus-5-5", thinking: "high", "limits.tools": [], "limits.network": "none", "limits.authority": ["message.send"] }, "agent");
@@ -53,6 +53,11 @@ test("types", () => {
[{ "tracker.descendants": "true" }, "project", /true or false/],
[{ "tracker.descendants": 1 }, "project", /true or false/],
[{ "tracker.descendants": true }, "business", /business layer \(allowed: project\)/],
[{ "bus.tester": "true" }, "business", /true or false/],
[{ "bus.tester": 1 }, "business", /true or false/],
[{ "bus.tester": true }, "project", /project layer \(allowed: business\)/],
[{ "bus.tester": true }, "agent", /agent layer \(allowed: business\)/],
[{ "bus.tester": true }, "system", /system layer \(allowed: business\)/],
[{ "git.workingBranch": "-x" }, "project", /git branch name/],
[{ "git.workingBranch": "a..b" }, "project", /git branch name/],
[{ "git.workingBranch": "x.lock" }, "project", /git branch name/],
@@ -83,6 +88,8 @@ test("merge: defaults, then the most specific layer wins", () => {
assert.equal(vars["tracker.kind"], "vikunja");
assert.equal(vars["tracker.descendants"], false);
assert.equal(provenance["tracker.descendants"], "default");
assert.equal(vars["bus.tester"], false);
assert.equal(provenance["bus.tester"], "default");
assert.equal(vars.harness, undefined);
assert.equal(provenance.harness, undefined);
});