Skip to content

One-click deploy: persona as single source of truth (persona-kit + CLI)#146

Open
khaliqgant wants to merge 3 commits into
mainfrom
feat/one-click-read-persona
Open

One-click deploy: persona as single source of truth (persona-kit + CLI)#146
khaliqgant wants to merge 3 commits into
mainfrom
feat/one-click-read-persona

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

@khaliqgant khaliqgant commented May 26, 2026

Consolidates the one-click deploy work into a single PR. Previously split as #146 (persona-kit) + #145 (CLI); the CLI commit has been folded in here so the library and its only consumer land together.

What

One-click agent deploy reads the persona definition as the single source of truth — no separate manifest (the AgentManifest approach in #142 was closed/superseded as drift-prone).

persona-kit

  • PersonaIntegrationConfig.optional?: boolean (additive, default required) + parse + regenerated persona.schema.json.
  • deriveDeployRequirements(persona, opts?){ integrations, inputs, firesOn, platformSecrets }.
    • Layer A (shared platform): platformSecrets: [] — only prompt is "connect these integrations".
    • Layer B (--isolated): backend-keyed platform-secret derivation salvaged from feat(persona-kit): agent manifest schema v1 for one-click deploy #142 (PLATFORM_SECRETS_BY_BACKEND), provider→backend registry injected by the cloud caller (default nango).

CLI

  • deploy --one-click <persona.json|persona.ts>: load/compile the persona, derive requirements from the PersonaSpec, render the Layer A plan, and delegate to the existing cloud deploy (mode=cloud, onExists=update).

Diff

8 files — 6 persona-kit + cli/src/deploy-command.ts (+test). +782/-2.

Supersedes

Tests

persona-kit suite green; CLI deploy-command tests included. (Branch is ~2 commits behind main — will rebase before merge.)

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an optional flag to persona integrations, implements deploy-requirements derivation (integrations, inputs, firesOn, platform secrets) with tests, and adds a CLI one-click deploy flow that loads/compiles a persona spec, renders a plan, and delegates to cloud deploy (supports dry-run).

Changes

Deploy Requirements Feature

Layer / File(s) Summary
Schema and Type Contract
packages/persona-kit/schemas/persona.schema.json, packages/persona-kit/src/types.ts
PersonaIntegrationConfig adds an optional: boolean field with documentation stating it defaults to false and that deployment requirement derivation computes required = !optional.
Parsing Integration Optional Field
packages/persona-kit/src/parse.ts
parseIntegrationConfig now extracts and validates the optional field from input, persisting it as true only when explicitly provided; omission or false leaves it unset.
Deploy Requirements Derivation
packages/persona-kit/src/deploy-requirements.ts
deriveDeployRequirements converts a PersonaSpec into required integrations (inverting the optional flag), required inputs (excluding defaults and optional), deduplicated firesOn event strings from triggers/schedules/watch events, and optional platformSecrets for isolated mode via injected backend resolution and webhook-gap provider callbacks.
Integration Tests and Coverage
packages/persona-kit/src/deploy-requirements.test.ts
Test suite validates default integration handling with provider sorting, optional flag inversion, input filtering rules, firesOn aggregation, platform secrets behavior across Layer A vs. B, backend and webhook-gap callback resolution, and empty persona edge case.
Public API Export
packages/persona-kit/src/index.ts
Re-exports deriveDeployRequirements, PLATFORM_SECRETS_BY_BACKEND, WEBHOOK_GAP_PLATFORM_SECRET, and related TypeScript types.

CLI One‑Click Deploy

Layer / File(s) Summary
CLI tests for one-click
packages/cli/src/deploy-command.test.ts
Adds tests for runDeploy --one-click covering JSON persona, compiled TS persona, dry-run behavior, plan rendering, and delegation to deploy.
Imports and dependency wiring
packages/cli/src/deploy-command.ts
Add readFile import; extend imports to include persona-kit utilities and compilePersonaFile; add compilePersonaFile to deploy command dependencies and defaults.
runDeploy dispatch & dependency injection
packages/cli/src/deploy-command.ts
runDeploy now intercepts --one-click to call runOneClickDeploy and uses injected deployCommandDeps.deploy for the normal deploy path.
runOneClickDeploy implementation
packages/cli/src/deploy-command.ts
Adds runOneClickDeploy, argument parsing (parseOneClickDeployArgs), persona loading/compilation (loadOneClickPersona), input resolution (resolveOneClickInputs), plan formatting, dry-run handling, and final deploy delegation using injected deploy.
Formatting helpers
packages/cli/src/deploy-command.ts
Helpers to format and render the one-click plan and determine resolved inputs for plan output.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A flag called optional hops into view,
Plans and secrets gather, tidy and new,
CLI reads personas, compiles what must be,
A one‑click deploy shows the plan to see,
Then hops to cloud to make the change true.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.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 'One-click deploy: persona as single source of truth (persona-kit + CLI)' clearly summarizes the main change: introducing one-click deploy that uses the persona definition as the primary source of truth for both the library and CLI components.
Description check ✅ Passed The description is detailed and directly related to the changeset, explaining the consolidation of persona-kit and CLI one-click deploy work, the new optional field, deploy requirements derivation, and how the persona replaces the manifest approach.
Linked Issues check ✅ Passed The PR implements all key objectives from #145 and #143: persona file loading/compilation, deploy requirements derivation, Layer A plan rendering, and cloud deploy delegation. Both file summaries and objectives confirm these requirements are met.
Out of Scope Changes check ✅ Passed All changes are within scope: schema updates for optional field, deploy requirements API, parsing logic, CLI one-click flow, and corresponding tests directly address the linked issue requirements.

✏️ 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/one-click-read-persona

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a mechanism to derive deployment requirements directly from a persona definition, eliminating the need for a separate manifest. It adds support for marking integrations as optional, updates the schema and parser accordingly, and includes comprehensive unit tests. The reviewer suggested improving the robustness of the platform secret derivation by handling unknown backends defensively and aggregating provider names in the reason field for shared secrets.

Comment on lines +184 to +211
function derivePlatformSecrets(
integrations: RequiredIntegration[],
opts: DeriveDeployRequirementsOptions,
): RequiredPlatformSecret[] {
const resolveBackend = opts.resolveBackend ?? (() => 'nango' as const);
const isWebhookGap = opts.isWebhookGapProvider ?? (() => false);
const seen = new Map<string, RequiredPlatformSecret>();

for (const { provider } of integrations) {
const backend = resolveBackend(provider);
const names = [...PLATFORM_SECRETS_BY_BACKEND[backend]];
if (isWebhookGap(provider)) {
names.push(WEBHOOK_GAP_PLATFORM_SECRET);
}
for (const name of names) {
if (!seen.has(name)) {
seen.set(name, {
name,
required: true,
reason: `required for the ${provider} integration (${backend}) to function`,
provider,
});
}
}
}

return [...seen.values()].sort((a, b) => a.name.localeCompare(b.name));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Robustness & Clarity Improvement

  1. Defensive Programming: If resolveBackend returns a backend that is not registered in PLATFORM_SECRETS_BY_BACKEND (e.g., due to a new backend added in the cloud app before persona-kit is updated), PLATFORM_SECRETS_BY_BACKEND[backend] will be undefined. Attempting to spread it ([...undefined]) will throw a runtime TypeError. We should check for this and throw a descriptive error instead.
  2. Aggregated Reasons: When multiple integrations share the same platform secret (e.g., NangoSecretKey or WebRelayauthApiKey), the current implementation associates the secret with only the first provider processed. If that provider is optional and not connected, the deployer might see a confusing reason (e.g., "required for the github integration" when they only connected slack). Aggregating the providers in the reason field makes the requirements much clearer and more accurate.
function derivePlatformSecrets(
  integrations: RequiredIntegration[],
  opts: DeriveDeployRequirementsOptions,
): RequiredPlatformSecret[] {
  const resolveBackend = opts.resolveBackend ?? (() => 'nango' as const);
  const isWebhookGap = opts.isWebhookGapProvider ?? (() => false);
  const secretProviders = new Map<string, { providers: string[]; backend: string }>();

  for (const { provider } of integrations) {
    const backend = resolveBackend(provider);
    const backendSecrets = PLATFORM_SECRETS_BY_BACKEND[backend];
    if (!backendSecrets) {
      throw new Error("Unsupported integration backend \"" + backend + "\" for provider \"" + provider + "\"");
    }
    const names = [...backendSecrets];
    if (isWebhookGap(provider)) {
      names.push(WEBHOOK_GAP_PLATFORM_SECRET);
    }
    for (const name of names) {
      let entry = secretProviders.get(name);
      if (!entry) {
        entry = { providers: [], backend };
        secretProviders.set(name, entry);
      }
      entry.providers.push(provider);
    }
  }

  return [...secretProviders.entries()]
    .map(([name, { providers, backend }]) => ({
      name,
      required: true,
      reason: "required for the following integration(s) to function: " + providers.join(', ') + " (" + backend + ")",
      provider: providers[0],
    }))
    .sort((a, b) => a.name.localeCompare(b.name));
}

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 6 files

You’re at about 91% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/persona-kit/src/deploy-requirements.ts
@khaliqgant khaliqgant changed the title feat(persona-kit): one-click reads persona directly (deriveDeployRequirements + integration optional?) One-click deploy: persona as single source of truth (persona-kit + CLI) May 27, 2026
Ricky Schema Cascade and others added 3 commits May 27, 2026 15:03
…integration optional?

One-click deploy reads the persona definition as the single source of truth (no
separate manifest — that was a drift-prone 2nd source).

- PersonaIntegrationConfig.optional?: boolean (additive, default required) +
  parse; lets persona authors mark non-essential integrations optional.
- deploy-requirements.ts: deriveDeployRequirements(persona: PersonaSpec, opts?)
  → { integrations (required = !optional), inputs (no default & not optional),
  firesOn, platformSecrets:[] for Layer A }. Layer-B (--isolated) backend-keyed
  platform-secret derivation is a documented fast-follow (needs the
  provider→backend registry; no hand-list).
- Regenerated persona.schema.json for the new field.

Salvages wf#142's secret-derivation into the read-from-persona path; AgentManifest
dropped. persona-kit typecheck + full suite green (230/230).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ation

Salvages the dropped AgentManifest's secret-derivation into the read-from-persona
path. platformSecrets is now derived for Layer-B (--isolated) from each declared
provider's credential BACKEND (PLATFORM_SECRETS_BY_BACKEND: nango→Nango+WebRelayauth,
composio→Composio+WebRelayauth; +HookdeckSigningSecret for Nango-webhook-gap providers
like gitlab) — keyed by BACKEND, not a drift-prone per-provider hand-list. The
provider→backend registry lives in the cloud app, so resolveBackend/isWebhookGapProvider
are INJECTED by the cloud caller (default backend = nango). Layer A stays platformSecrets: [].

persona-kit typecheck + full suite green (231/231).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@khaliqgant khaliqgant force-pushed the feat/one-click-read-persona branch from 19c9cbc to 785aa81 Compare May 27, 2026 13:03
Copy link
Copy Markdown
Contributor

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@packages/cli/src/deploy-command.ts`:
- Around line 86-89: The guard that routes to the one-click flow only checks for
the standalone token '--one-click' so inline flags like
'--one-click=persona.json' fall through; update the condition that calls
runOneClickDeploy(...) to detect both the exact token and the inline form (e.g.,
check args.some(a => a === '--one-click' || a.startsWith('--one-click='))). This
will ensure parseOneClickDeployArgs(...) and runOneClickDeploy(...) receive
inline args correctly and prevent the flag from being treated as unknown by
parseDeployArgs(...).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0601fa81-c655-4e45-b429-b15ecd2d0294

📥 Commits

Reviewing files that changed from the base of the PR and between ba0a0b4 and 785aa81.

📒 Files selected for processing (8)
  • packages/cli/src/deploy-command.test.ts
  • packages/cli/src/deploy-command.ts
  • packages/persona-kit/schemas/persona.schema.json
  • packages/persona-kit/src/deploy-requirements.test.ts
  • packages/persona-kit/src/deploy-requirements.ts
  • packages/persona-kit/src/index.ts
  • packages/persona-kit/src/parse.ts
  • packages/persona-kit/src/types.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/persona-kit/schemas/persona.schema.json
  • packages/persona-kit/src/index.ts
  • packages/persona-kit/src/parse.ts
  • packages/persona-kit/src/deploy-requirements.test.ts
  • packages/persona-kit/src/types.ts
  • packages/persona-kit/src/deploy-requirements.ts

Comment on lines +86 to +89
if (args.includes('--one-click')) {
await runOneClickDeploy(args);
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Dispatch --one-click=<path> through the one-click code path.

parseOneClickDeployArgs(...) supports the inline form, but this guard only recognizes a standalone --one-click token. agentworkforce deploy --one-click=persona.json currently falls through to parseDeployArgs(...) and fails as an unknown flag.

Suggested fix
-  if (args.includes('--one-click')) {
+  if (args.some((arg) => arg === '--one-click' || arg.startsWith('--one-click='))) {
     await runOneClickDeploy(args);
     return;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (args.includes('--one-click')) {
await runOneClickDeploy(args);
return;
}
if (args.some((arg) => arg === '--one-click' || arg.startsWith('--one-click='))) {
await runOneClickDeploy(args);
return;
}
🤖 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/deploy-command.ts` around lines 86 - 89, The guard that
routes to the one-click flow only checks for the standalone token '--one-click'
so inline flags like '--one-click=persona.json' fall through; update the
condition that calls runOneClickDeploy(...) to detect both the exact token and
the inline form (e.g., check args.some(a => a === '--one-click' ||
a.startsWith('--one-click='))). This will ensure parseOneClickDeployArgs(...)
and runOneClickDeploy(...) receive inline args correctly and prevent the flag
from being treated as unknown by parseDeployArgs(...).

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