row 175 (#1673) round 2: the tester's project init refuses the area root itself (N1), bus start and stop are in the --tester refusal list (N2), and the reader view comment names name.set and project.create (N3)

This commit is contained in:
dewey committed 2026-10-19 18:44:06 -05:00
1 parent 3067ae93ea
commit 989699f7c4
5 files changed
+16 -6

No files matched your search

+2
View File
@@ -1285,6 +1285,8 @@ export class Broker {
fail('read-only');
if (s.tester && !TESTER_VERBS.includes(request.verb)) fail('tester-refused');
// A tester reads the human's views, as a reader does, and its message verbs are its own.
// Since row 175 the view also carries the writes name.set and project.create. Neither
// branch checks `reader`: each passes a tester by `s.tester` and records `tester`.
const view = s.tester && !request.verb.startsWith('message.') ? { ...s, reader: true } : s;
const result = this.#dispatch(view, request.verb, args);
this.#secretCheck(result);
+5 -3
View File
@@ -183,11 +183,13 @@ mosaic launches list [--json]
exits 3. A run launched but not yet claimed counts as not running. The
role's reply comes back to the tester, not to the human's inbox. `decide`, `stop`, `launches`,
`inbox --read` or `--read-all`, `limits set`, `reset` and `migrate`,
`project check`, the `thread` commands and `talk` or `follow --thread`
take no `--tester` (exit 4).
`project check`, the `thread` commands, `talk` or `follow --thread` and
`bus start` or `stop` take no `--tester` (exit 4).
- `project init --tester` (row 175, mos-0872 Q2) may run inside an agent.
Before anything touches the disk it refuses a repository outside
`<dataRoot>/projects/<business>/` (`tester-project-area`, exit 2), then
`<dataRoot>/projects/<business>/`, or that directory itself, since a
repository there would hold the other projects' default ones
(`tester-project-area`, exit 2), then
binds the tester with one `agents` read, so `tester-off` and
`tester-required` refuse (exit 3), then commits the layout with `tester`
as author and committer. Tests: `tests/tester-reads.test.mjs`.
+2
View File
@@ -102,6 +102,8 @@ test("row 175: --tester on limits set, reset and migrate and every write form ou
["launches", "off", "--business", "acme"],
["launches", "on", "--business", "acme"],
["launches", "list"],
["bus", "start", "acme"],
["bus", "stop"],
["project", "check", "acme", "stack"],
["project", "init", "acme", "stack", "--tester"],
]) {
+4 -2
View File
@@ -48,9 +48,11 @@ export function projectRepo({ business, project, dataRoot }) {
}
// Row 175 (#1673, mos-0872 Q2): the tester initializes only a repository in the data root's
// project area, <dataRoot>/projects/<business id>/. It writes nothing; projectRepo only lstats the path.
// The area itself is refused too: a repository there would hold every other project's default one.
export function testerProjectArea(context) {
const repo = projectRepo(context);
if (!contains(join(context.dataRoot, 'projects', context.business.id), repo))
throw new ProjectError('tester-project-area', `the tester initializes only a repository under ${join(context.dataRoot, 'projects', context.business.id)}`);
const area = join(context.dataRoot, 'projects', context.business.id);
if (relative(area, repo) === '' || !contains(area, repo))
throw new ProjectError('tester-project-area', `the tester initializes only a repository under ${area}`);
return repo;
}
+3 -1
View File
@@ -34,7 +34,7 @@ test('row 175: init refuses an author that is no id before it makes any director
assert.equal(existsSync(c.dataRoot), false);
});
test('row 175: testerProjectArea passes a repository in the business area and refuses any other, touching nothing', (t) => {
test('row 175: testerProjectArea passes a repository inside the business area and refuses the area itself and any other, touching nothing', (t) => {
const inside = [
null,
(root, dataRoot) => join(dataRoot, 'projects', 'acme', 'records'),
@@ -50,6 +50,8 @@ test('row 175: testerProjectArea passes a repository in the business area and re
(root, dataRoot) => join(dataRoot, 'projects', 'other', 'stack'),
// A sibling whose name starts with the business id is not inside it.
(root, dataRoot) => join(dataRoot, 'projects', 'acme-2', 'stack'),
// The area itself: a repository there would hold the other projects' default repositories.
(root, dataRoot) => join(dataRoot, 'projects', 'acme'),
];
for (const repo of outside) {
const c = context(t, repo);