Skip to content

feat(personas-core): add proactive-agent-builder persona + fix optional-input regression#87

Draft
khaliqgant wants to merge 2 commits into
mainfrom
feat/proactive-agent-builder-persona
Draft

feat(personas-core): add proactive-agent-builder persona + fix optional-input regression#87
khaliqgant wants to merge 2 commits into
mainfrom
feat/proactive-agent-builder-persona

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

Summary

  • New proactive-agent-builder persona that takes a natural-language agent spec ("check Reddit daily for X, summarize, DM in Slack") and scaffolds a runnable agent in a target proactive-agents project — typed agent.ts under agents/<id>/, bootstrap-wired in the Pages Function router, env vars declared, with cron/webhook handoff lines in the output contract.
  • Fixes a silent regression in the CLI's local-personas loader: parseInputsShape was dropping optional: true from input specs, so the canonical persona-maker pattern (sparse systemPrompt: "$TASK_DESCRIPTION" + inputs.TASK_DESCRIPTION: { optional: true }) failed at launch with MissingPersonaInputError for every pack-distributed persona. Added a regression test.

Why now

We're productizing the proactive-agents reference site as a workforce persona so we can type one prompt and get a deployed agent. The runtime spec (cloud PR #515) is in flight; this persona deliberately scaffolds against the pre-runtime shim (Pages Function + setEnv), matching what agents/weekly-digest, agents/notion-to-blog, and agents/manual-chatbot ship today. When relay deploy lands from M1, the "Bootstrap wiring" section of the sidecar swaps to that single call — no agent code changes needed.

What's in the persona

  • Sparse systemPrompt: "$TASK_DESCRIPTION" across all three tiers, per the persona-maker convention.
  • Operating spec in agentsMdContent (sidecar): runtime contract, three trigger templates with pointers to the canonical weekly-digest / notion-to-blog / manual-chatbot references in the proactive-agents repo, env-injection shim, destination patterns for Slack / GitHub / Linear / Notion, idempotency and dedup conventions, output contract.
  • Tier split: best = codex/gpt-5.3-codex (reasoning high), best-value = opencode/gpt-5-nano (medium), minimum = opencode/minimax-m2.5-free (low). Default routing pins the intent to best because a misshaped agent.ts ships either a dead handler or a duplicate-firing one — depth over speed.
  • Permissions allow npx tsc --noEmit, read-only git, curl smoke tests; deny rm -rf, git push, npm publish, wrangler deploy.
  • Mount is read-only on the broader project except agents/**, functions/**, and wrangler.jsonc.

What's in the fix

packages/cli/src/local-personas.ts parseInputsShape only copied description / env / default off each spec and silently dropped optional. The persona-kit resolver (resolvePersonaInputs) already supports optional: true to substitute $NAME as an empty string instead of throwing — but the flag never reached it for pack-installed personas. One-line plumb-through plus a regression test that asserts the flag round-trips through loadLocalPersonas.

Validation

  • agentworkforce agent proactive-agent-builder@best --dry-run — green (sidecar resolution, codex harness spec build, skill install all pass).
  • agentworkforce agent proactive-agent-builder@best-value --dry-run — green.
  • corepack pnpm run check (lint + typecheck + tests across all packages) — exits 0. 165 CLI tests pass including the new regression.
  • node packages/personas-core/scripts/validate-personas.mjs — 15 personas ok.

Test plan

  • corepack pnpm install && corepack pnpm -r build
  • corepack pnpm run check
  • node packages/cli/dist/cli.js install ./packages/personas-core --overwrite
  • node packages/cli/dist/cli.js agent proactive-agent-builder@best-value --dry-run
  • (Optional) Try the persona end-to-end against a proactive-agents checkout: agentworkforce agent proactive-agent-builder from inside proactive-agents/, then describe a cron-triggered Reddit→Slack agent in the TUI and confirm the scaffolded agent.ts + registry edit + env declarations land in the right places.

Follow-up (not in this PR)

When the cloud M1 runtime lands relay deploy (PR #515 / cloud-runtime-run), the sidecar's "Bootstrap wiring" section swaps from the Pages Function + setEnv shim to the relay deploy <file> call. That's a single-section edit in agentsMdContent, not a rewrite — the scaffolded agent.ts itself stays the same.

🤖 Generated with Claude Code

khaliqgant and others added 2 commits May 11, 2026 22:58
parseInputsShape in local-personas.ts was copying description / env /
default off each input spec but silently dropping `optional: true`,
which the resolvePersonaInputs helper relies on to substitute `$NAME`
as an empty string instead of throwing MissingPersonaInputError.

Effect: any pack-distributed persona that followed the canonical
persona-maker pattern (sparse `systemPrompt: "$TASK_DESCRIPTION"` +
`inputs.TASK_DESCRIPTION: { optional: true }`) failed at launch with
"Persona input TASK_DESCRIPTION is required" until the user manually
set the env var, defeating the whole point of the sentinel.

The persona-kit resolver and parser both already support optional;
this just plumbs it through the CLI's local-personas loader. Added a
regression test asserting the flag round-trips on a standalone
local persona JSON.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Scaffolds a new proactive agent (cron / watch / message-triggered) into
a target project that follows the @agent-relay/agent runtime contract.
Takes a natural-language TASK_DESCRIPTION ("check Reddit daily for
mentions of X, summarize, DM in Slack") and produces a typed agent.ts
under agents/<id>/, wires the bootstrap entry in the Pages Function
router so it runs on Cloudflare today, declares the env vars it
reads, and reports the cron line / webhook URL the user must register.

The operating spec — runtime contract, three trigger templates with
pointers to the canonical proactive-agents reference implementations,
env-injection shim pattern, destination routing for Slack / GitHub /
Linear / Notion, idempotency and dedup conventions — lives in
agentsMdContent. Each tier's systemPrompt is the sparse
"$TASK_DESCRIPTION" sentinel per the persona-maker convention; the
heavy guidance is delivered as AGENTS.md sidecar so it doesn't burn
prompt tokens on every turn.

Adds a new `scaffold-proactive-agent` entry to PERSONA_INTENTS plus a
balanced-default routing rule pinning the intent to `best` tier — a
misshaped agent.ts ships either a dead handler or a duplicate-firing
one, so depth over speed is the right default.

Validated via `agentworkforce agent proactive-agent-builder@<tier>
--dry-run` on both codex and opencode tiers; sidecar resolution,
harness spec build, and skill install all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces support for optional persona inputs and registers a new scaffold-proactive-agent intent. It adds parsing and validation for an optional boolean flag on persona inputs, a test ensuring optional inputs are preserved through loading, a complete proactive-agent-builder persona configuration with tiered execution settings, and routing integration for the new intent.

Changes

Proactive Agent Builder with Optional Input Support

Layer / File(s) Summary
Intent constant registration
packages/persona-kit/src/constants.ts
PERSONA_INTENTS exported tuple adds the new intent string 'scaffold-proactive-agent' after 'relay-orchestrator'.
Optional input parsing and validation
packages/cli/src/local-personas.ts
parseInputsShape now recognizes and validates an optional boolean property on per-input specs, rejects optional:true combined with default, and includes optional:true in the constructed PersonaInputSpec.
Input optional flag preservation test
packages/cli/src/local-personas.test.ts
Test case verifies that a standalone persona with optional: true on TASK_DESCRIPTION input is correctly loaded and retains the optional flag in the spec.
Proactive agent builder persona configuration
packages/personas-core/personas/proactive-agent-builder.json
New persona with metadata, optional TASK_DESCRIPTION input and TARGET_DIR with default, detailed agent runtime contract specification, command execution and mount permissions, and three-tier harness/model configuration using dynamic system prompt from task description.
Routing configuration for new intent
packages/workload-router/routing-profiles/default.json
The balanced-default routing policy adds scaffold-proactive-agent intent mapped to best tier with rationale for runtime-contract and wiring correctness requirements.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • willwashburn

Poem

🐰 A proactive agent builder hops in place,
With optional inputs showing their grace,
New intents registered, routing finds its way,
Parsing validates inputs, hip-hip-hooray! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the two main changes: adding a new proactive-agent-builder persona and fixing an optional-input regression in the CLI.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering the new persona details, the regression fix, validation results, and follow-up plans.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/proactive-agent-builder-persona

Comment @coderabbitai help to get the list of available commands and usage tips.

@khaliqgant khaliqgant marked this pull request as draft May 11, 2026 21:04
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/cli/src/local-personas.test.ts (1)

478-517: ⚡ Quick win

Add a negative regression test for the new invariant.

Please add a companion test that confirms persona loading warns/rejects when an input combines optional: true with default, so the new parser guard stays protected.

Suggested test addition
+test('rejects optional:true combined with default in input spec', () => {
+  withLayers(({ cwd, homeDir }) => {
+    writeJson(join(homeDir, 'bad-optional.json'), {
+      id: 'bad-optional',
+      intent: 'bad-optional',
+      tags: ['implementation'],
+      description: 'Invalid input shape.',
+      inputs: {
+        TASK_DESCRIPTION: {
+          optional: true,
+          default: 'seed'
+        }
+      },
+      tiers: {
+        best: {
+          harness: 'codex',
+          model: 'openai-codex/gpt-5.3-codex',
+          systemPrompt: '$TASK_DESCRIPTION',
+          harnessSettings: { reasoning: 'high', timeoutSeconds: 30 }
+        },
+        'best-value': {
+          harness: 'opencode',
+          model: 'opencode/gpt-5-nano',
+          systemPrompt: '$TASK_DESCRIPTION',
+          harnessSettings: { reasoning: 'medium', timeoutSeconds: 30 }
+        },
+        minimum: {
+          harness: 'opencode',
+          model: 'opencode/minimax-m2.5-free',
+          systemPrompt: '$TASK_DESCRIPTION',
+          harnessSettings: { reasoning: 'low', timeoutSeconds: 30 }
+        }
+      }
+    });
+    const loaded = loadLocalPersonas({ cwd, homeDir });
+    assert.equal(loaded.byId.has('bad-optional'), false);
+    assert.match(loaded.warnings.join('\n'), /cannot combine optional:true with a default/);
+  });
+});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/cli/src/local-personas.test.ts` around lines 478 - 517, Add a
negative regression test alongside the existing "optional input flag is
preserved on standalone local personas" test that constructs a persona where an
input combines optional: true and default: '...'; call loadLocalPersonas({ cwd,
homeDir }) and assert that loaded.warnings contains a warning about the invalid
combination (optional + default) and that the parsed persona rejects that
invalid input (e.g., spec = loaded.byId.get('standalone-scaffolder') yields
either no spec for that id or that spec?.inputs?.TASK_DESCRIPTION.optional !==
true / default is not honored). Reference loadLocalPersonas and the
TASK_DESCRIPTION input in your test so it fails if the parser guard is removed.
packages/personas-core/personas/proactive-agent-builder.json (1)

11-14: ⚡ Quick win

Clarify TARGET_DIR path semantics to avoid conflicting instructions.

Line 12 says this must be an absolute path, but Line 13 defaults to "." (relative). Please make the description explicitly allow relative paths (resolved from cwd), or change the default to an absolute-path mechanism.

Proposed wording tweak
- "description": "Absolute path to the proactive-agents project root (the repo that has `agents/`, `agents/shared/sdk.ts`, and `functions/api/cron/[agent].ts`). Defaults to the current working directory.",
+ "description": "Path to the proactive-agents project root (the repo that has `agents/`, `agents/shared/sdk.ts`, and `functions/api/cron/[agent].ts`). Relative paths are resolved from the current working directory; default is `.`.",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/personas-core/personas/proactive-agent-builder.json` around lines 11
- 14, The description for the TARGET_DIR setting is inconsistent (it says
"Absolute path" but the default is "."), so update the
personas/proactive-agent-builder.json entry for TARGET_DIR to either: (A) allow
relative paths by changing the description to "Absolute or relative path to the
proactive-agents project root (resolved from current working directory).
Defaults to '.'", or (B) make the default an absolute-path string if you must
require absolute paths; reference the TARGET_DIR key when making this change so
docs and defaults align. Ensure the wording explicitly states resolution
behavior (resolved from cwd) if you choose option A.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/cli/src/local-personas.test.ts`:
- Around line 478-517: Add a negative regression test alongside the existing
"optional input flag is preserved on standalone local personas" test that
constructs a persona where an input combines optional: true and default: '...';
call loadLocalPersonas({ cwd, homeDir }) and assert that loaded.warnings
contains a warning about the invalid combination (optional + default) and that
the parsed persona rejects that invalid input (e.g., spec =
loaded.byId.get('standalone-scaffolder') yields either no spec for that id or
that spec?.inputs?.TASK_DESCRIPTION.optional !== true / default is not honored).
Reference loadLocalPersonas and the TASK_DESCRIPTION input in your test so it
fails if the parser guard is removed.

In `@packages/personas-core/personas/proactive-agent-builder.json`:
- Around line 11-14: The description for the TARGET_DIR setting is inconsistent
(it says "Absolute path" but the default is "."), so update the
personas/proactive-agent-builder.json entry for TARGET_DIR to either: (A) allow
relative paths by changing the description to "Absolute or relative path to the
proactive-agents project root (resolved from current working directory).
Defaults to '.'", or (B) make the default an absolute-path string if you must
require absolute paths; reference the TARGET_DIR key when making this change so
docs and defaults align. Ensure the wording explicitly states resolution
behavior (resolved from cwd) if you choose option A.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8cbccf79-2e89-4e03-882f-817d06725d5b

📥 Commits

Reviewing files that changed from the base of the PR and between 98c046e and 0e3d69d.

📒 Files selected for processing (5)
  • packages/cli/src/local-personas.test.ts
  • packages/cli/src/local-personas.ts
  • packages/persona-kit/src/constants.ts
  • packages/personas-core/personas/proactive-agent-builder.json
  • packages/workload-router/routing-profiles/default.json

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant