diff --git a/src/automation/autonomousRunner.enable.test.ts b/src/automation/autonomousRunner.enable.test.ts new file mode 100644 index 0000000..2689c29 --- /dev/null +++ b/src/automation/autonomousRunner.enable.test.ts @@ -0,0 +1,32 @@ +// Purpose: enableProject must also add the repo to allowedProjects so +// resolveProjectPath reads its openswarm.json mapping (INT-1973). +import { describe, it, expect } from 'vitest'; +import { AutonomousRunner } from './autonomousRunner.js'; +import type { AutonomousConfig } from './runnerTypes.js'; + +const cfg = (over: Partial = {}): AutonomousConfig => ({ + linearTeamId: 'team', + allowedProjects: ['/x/a'], + heartbeatSchedule: '0 * * * *', + autoExecute: false, + maxConsecutiveTasks: 1, + cooldownSeconds: 0, + dryRun: true, + ...over, +}); + +describe('AutonomousRunner.enableProject (INT-1973)', () => { + it('enabling a repo also allows it (resolveProjectPath reads only allowed paths)', () => { + const r = new AutonomousRunner(cfg()); + r.enableProject('/x/wave'); + expect(r.getEnabledProjects()).toContain('/x/wave'); + expect(r.getAllowedProjects()).toContain('/x/wave'); + }); + + it('does not duplicate an already-allowed path', () => { + const r = new AutonomousRunner(cfg({ allowedProjects: ['/x/a'] })); + r.enableProject('/x/a'); + expect(r.getAllowedProjects().filter((p) => p === '/x/a')).toHaveLength(1); + expect(r.getEnabledProjects()).toContain('/x/a'); + }); +}); diff --git a/src/automation/autonomousRunner.ts b/src/automation/autonomousRunner.ts index 5485dd4..bfff8b5 100644 --- a/src/automation/autonomousRunner.ts +++ b/src/automation/autonomousRunner.ts @@ -1358,6 +1358,14 @@ export class AutonomousRunner { enableProject(projectPath: string): void { this.enabledProjects.add(projectPath); + // Enabling a repo (via `openswarm add` / the dashboard) must also ALLOW it: + // resolveProjectPath only reads a repo's openswarm.json for paths in + // allowedProjects, so an enabled-but-not-allowed repo never resolves + // ("No repo mapped"). Keep config + DecisionEngine in sync. (INT-1970) + const allowed = this.config.allowedProjects ?? []; + if (!allowed.includes(projectPath)) { + this.updateAllowedProjects([...allowed, projectPath]); + } console.log(`[AutonomousRunner] Project enabled: ${projectPath}`); } diff --git a/src/cli/projectHandler.ts b/src/cli/projectHandler.ts index e170675..115a4f7 100644 --- a/src/cli/projectHandler.ts +++ b/src/cli/projectHandler.ts @@ -4,8 +4,10 @@ // // Manage the work-repo registry the daemon reads at startup // (~/.claude/openswarm-repos.json — the same file the web dashboard writes). -// `setWebRunner` (src/support/web.ts) merges `enabled` into both the runner's -// enabled set AND its allowedProjects, so a repo added here is actually worked. +// `setWebRunner` (src/support/web.ts) calls runner.enableProject() for each +// enabled repo, which adds it to BOTH the enabled set AND allowedProjects +// (INT-1973) — the latter is required so resolveProjectPath reads the repo's +// openswarm.json mapping. A repo added here is therefore actually worked. // // `add` also offers a Linear team/project picker (shared with `openswarm init` // via ./linearMapping) and writes the repo↔Linear mapping into the repo's