From e5e641adb46fc7b2b113d3ce31ebbfc531f9964e Mon Sep 17 00:00:00 2001 From: Proactive Runtime Bot Date: Sat, 18 Jul 2026 00:39:49 +0200 Subject: [PATCH 1/3] fix(relay-feature-guardian): clone relay repo for feature manifest The persona reads .agentworkforce/features/manifest.yaml from its sandbox workspace but declared no GitHub integration, so nothing provisioned the relay repo into the workspace. It reached runner.started then failed with relay-feature-guardian.manifest-load-failed (ENOENT on /home/daytona/workspace/.agentworkforce/features/manifest.yaml). Add a github integration scoped to AgentWorkforce/relay so the proactive git-workspace derivation clones the repo into the workspace, materializing the features manifest (and critical-paths.md / verify procedures) the guardian needs to do its job. Co-Authored-By: Claude Opus 4.8 --- .agentworkforce/agents/relay-feature-guardian/persona.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.agentworkforce/agents/relay-feature-guardian/persona.json b/.agentworkforce/agents/relay-feature-guardian/persona.json index 0bc45fbed..05288276e 100644 --- a/.agentworkforce/agents/relay-feature-guardian/persona.json +++ b/.agentworkforce/agents/relay-feature-guardian/persona.json @@ -10,6 +10,11 @@ "timeoutSeconds": 300 }, "integrations": { + "github": { + "scope": { + "repo": "AgentWorkforce/relay" + } + }, "slack": { "scope": { "paths": "/slack/channels/**" From cd841175cc8c0518778642a1759f05c50e3ee591 Mon Sep 17 00:00:00 2001 From: Proactive Runtime Bot Date: Sat, 18 Jul 2026 01:15:42 +0200 Subject: [PATCH 2/3] fix(relay-feature-guardian): read cloned repo manifest --- .../relay-feature-guardian/agent.test.ts | 21 +++++++++++++++++ .../agents/relay-feature-guardian/agent.ts | 23 +++++++++++++++---- .../relay-feature-guardian/persona.json | 2 +- 3 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 .agentworkforce/agents/relay-feature-guardian/agent.test.ts diff --git a/.agentworkforce/agents/relay-feature-guardian/agent.test.ts b/.agentworkforce/agents/relay-feature-guardian/agent.test.ts new file mode 100644 index 000000000..c1de576ca --- /dev/null +++ b/.agentworkforce/agents/relay-feature-guardian/agent.test.ts @@ -0,0 +1,21 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { describe, it } from 'node:test'; +import { resolveManifestPath } from './agent.ts'; + +const persona = JSON.parse(readFileSync(new URL('./persona.json', import.meta.url), 'utf8')) as { + inputs: { SLACK_CHANNEL: { default: string } }; +}; + +describe('relay-feature-guardian runtime paths', () => { + it('reads the manifest from the cloned relay repository', () => { + assert.equal( + resolveManifestPath('/home/daytona/workspace'), + '/home/daytona/workspace/github/repos/AgentWorkforce/relay/.agentworkforce/features/manifest.yaml' + ); + }); + + it('defaults delivery to the relay feature-check channel', () => { + assert.equal(persona.inputs.SLACK_CHANNEL.default, 'C0AEKNLDNKW'); + }); +}); diff --git a/.agentworkforce/agents/relay-feature-guardian/agent.ts b/.agentworkforce/agents/relay-feature-guardian/agent.ts index 9e53b7a22..cfb9f893a 100644 --- a/.agentworkforce/agents/relay-feature-guardian/agent.ts +++ b/.agentworkforce/agents/relay-feature-guardian/agent.ts @@ -2,7 +2,7 @@ * relay-feature-guardian handler. * * Hourly cron tick: - * 1. Read the feature list from .agentworkforce/features/manifest.yaml + * 1. Read the feature list from the cloned relay repository * 2. Load feature progress from memory * 3. Pick the next unchecked feature (ordered by criticality then tier) * 4. Generate a concise quiz question via ctx.llm @@ -55,9 +55,20 @@ interface Feature { } const MANIFEST_RELPATH = '.agentworkforce/features/manifest.yaml'; +const RELAY_REPO_RELPATH = 'github/repos/AgentWorkforce/relay'; + +/** + * GitHub-scoped repositories are cloned beneath the proactive workspace root. + * WorkforceCtx currently exposes that workspace root (`sandbox.cwd`), but not + * an integration-specific repository directory, so derive the clone path from + * the documented `/github/repos/{owner}/{repo}` layout. + */ +export function resolveManifestPath(workspaceDir: string): string { + return `${workspaceDir}/${RELAY_REPO_RELPATH}/${MANIFEST_RELPATH}`; +} async function loadFeatures(ctx: WorkforceCtx): Promise { - const absPath = `${ctx.sandbox.cwd}/${MANIFEST_RELPATH}`; + const absPath = resolveManifestPath(ctx.sandbox.cwd); const raw = await ctx.sandbox.readFile(absPath); const manifest = parse(raw) as Manifest; const features: Feature[] = []; @@ -191,17 +202,21 @@ export default defineAgent({ try { features = await loadFeatures(ctx); } catch (err) { - const absPath = `${ctx.sandbox.cwd}/${MANIFEST_RELPATH}`; + const absPath = resolveManifestPath(ctx.sandbox.cwd); ctx.log('error', 'relay-feature-guardian.manifest-load-failed', { path: absPath, err: String(err) }); const isNotFound = String(err).includes('ENOENT'); const errMsg = isNotFound - ? `⚠️ *relay-feature-guardian* can't find the feature manifest at \`${MANIFEST_RELPATH}\`.\n\nThe \`feature/verify-features-workflow\` branch hasn't been merged to main yet — the sandbox runs on main, so the manifest doesn't exist. Merge PR #1283 to fix this.` + ? `⚠️ *relay-feature-guardian* can't find the feature manifest in the cloned relay repository at \`${RELAY_REPO_RELPATH}/${MANIFEST_RELPATH}\`.` : `⚠️ *relay-feature-guardian* failed to load the feature manifest: \`${String(err)}\``; await slackClient() .post(channel, errMsg) .catch(() => undefined); return; } + ctx.log('info', 'relay-feature-guardian.manifest-loaded', { + path: resolveManifestPath(ctx.sandbox.cwd), + features: features.length, + }); if (features.length === 0) { ctx.log('error', 'relay-feature-guardian.no-features', { reason: 'manifest parsed but empty' }); await slackClient() diff --git a/.agentworkforce/agents/relay-feature-guardian/persona.json b/.agentworkforce/agents/relay-feature-guardian/persona.json index 05288276e..f5c4b1e8e 100644 --- a/.agentworkforce/agents/relay-feature-guardian/persona.json +++ b/.agentworkforce/agents/relay-feature-guardian/persona.json @@ -25,7 +25,7 @@ "SLACK_CHANNEL": { "description": "Slack channel to post feature checks to (e.g. relay-health or general).", "env": "SLACK_CHANNEL", - "default": "C0BHWJSF309", + "default": "C0AEKNLDNKW", "picker": { "provider": "slack", "resource": "channels" From 221754fa479fe5246682d878c107727a1c989551 Mon Sep 17 00:00:00 2001 From: Proactive Runtime Bot Date: Sat, 18 Jul 2026 01:25:28 +0200 Subject: [PATCH 3/3] test(relay-feature-guardian): run regression in CI --- .../agents/relay-feature-guardian/agent.test.ts | 8 +++----- vitest.config.ts | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.agentworkforce/agents/relay-feature-guardian/agent.test.ts b/.agentworkforce/agents/relay-feature-guardian/agent.test.ts index c1de576ca..4269cc33c 100644 --- a/.agentworkforce/agents/relay-feature-guardian/agent.test.ts +++ b/.agentworkforce/agents/relay-feature-guardian/agent.test.ts @@ -1,6 +1,5 @@ -import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; -import { describe, it } from 'node:test'; +import { describe, expect, it } from 'vitest'; import { resolveManifestPath } from './agent.ts'; const persona = JSON.parse(readFileSync(new URL('./persona.json', import.meta.url), 'utf8')) as { @@ -9,13 +8,12 @@ const persona = JSON.parse(readFileSync(new URL('./persona.json', import.meta.ur describe('relay-feature-guardian runtime paths', () => { it('reads the manifest from the cloned relay repository', () => { - assert.equal( - resolveManifestPath('/home/daytona/workspace'), + expect(resolveManifestPath('/home/daytona/workspace')).toBe( '/home/daytona/workspace/github/repos/AgentWorkforce/relay/.agentworkforce/features/manifest.yaml' ); }); it('defaults delivery to the relay feature-check channel', () => { - assert.equal(persona.inputs.SLACK_CHANNEL.default, 'C0AEKNLDNKW'); + expect(persona.inputs.SLACK_CHANNEL.default).toBe('C0AEKNLDNKW'); }); }); diff --git a/vitest.config.ts b/vitest.config.ts index 9d39f6eef..271b84a07 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -62,6 +62,7 @@ export default defineConfig({ environment: 'node', setupFiles: [path.resolve(__dirname, './vitest.setup.ts')], include: [ + '.agentworkforce/agents/**/*.test.ts', 'tests/fixtures/**/*.test.ts', 'tests/integration/broker/evals/**/*.unit.test.ts', 'packages/**/src/**/*.test.ts',