fix(paired): auto-provision paired workspace so tribunal rooms get a reviewer on registration

Rooms registered as tribunal had no work_dir and no paired_projects row, so
ensurePairedProject() returned null, no paired task was ever created, and only
the owner ran — the reviewer/arbiter never fired (web-vstock and 4 other rooms).

Add ensurePairedWorkspaceProvisioned(): defaults canonical work_dir to
groups/<folder>, guarantees it is a standalone git repo with an initial commit
(detected via a LOCAL .git so we never walk up into the EJClaw checkout and
create a stray worktree), and upserts the paired_projects row. Wire it into
setup/register.ts so every newly registered room is immediately usable by the
full owner→reviewer→arbiter flow. Adds a regression test.
This commit is contained in:
Codex
2026-07-30 01:45:28 +09:00
parent 6036430c60
commit 44c54a759c
3 changed files with 108 additions and 1 deletions

View File

@@ -123,6 +123,37 @@ describe('paired workspace manager', () => {
fs.rmSync(tempRoot, { recursive: true, force: true });
});
it('ensurePairedWorkspaceProvisioned inits a canonical repo with a commit and upserts the project', async () => {
const { db, manager } = await loadModules();
db._initTestDatabase();
const workDir = path.join(tempRoot, 'canon');
const result = manager.ensurePairedWorkspaceProvisioned({
chatJid: 'dc:test-room',
groupFolder: 'test-group',
workDir,
});
expect(result).toBe(workDir);
expect(fs.existsSync(path.join(workDir, '.git'))).toBe(true);
// HEAD must resolve so owner-workspace provisioning can `git worktree add`.
expect(runGit(['rev-parse', 'HEAD'], workDir)).toMatch(/^[0-9a-f]{40}$/);
// A paired_projects row is created from the canonical work dir, without
// which the tribunal flow throws "Paired project not found" and no
// reviewer/arbiter ever runs.
const project = db.getPairedProject('dc:test-room');
expect(project?.canonical_work_dir).toBe(workDir);
// Idempotent — a second call reuses the repo and does not throw.
expect(() =>
manager.ensurePairedWorkspaceProvisioned({
chatJid: 'dc:test-room',
groupFolder: 'test-group',
workDir,
}),
).not.toThrow();
});
it('registers the owner workspace for reviewer execution when review is requested', async () => {
const { db, manager } = await loadModules();
db._initTestDatabase();