Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/automation/autonomousRunner.enable.test.ts
Original file line number Diff line number Diff line change
@@ -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> = {}): 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');
});
});
8 changes: 8 additions & 0 deletions src/automation/autonomousRunner.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// OpenSwarm - Autonomous Runner
// Heartbeat → Decision → Execution → Report
import { Cron } from 'croner';
Expand Down Expand Up @@ -1358,6 +1358,14 @@

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}`);
}

Expand Down
6 changes: 4 additions & 2 deletions src/cli/projectHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading