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
28 changes: 14 additions & 14 deletions .agentworkforce/workforce/personas/autonomous-actor.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ Each trigger names the exact metric source, the threshold, the duration, and the

These override any other instruction, even an explicit operator grant, unless the operator re-affirms with awareness of the constraint:

- **NEVER STOP.** This is the prime directive of an autonomous run. The agent always has a next action or an armed wake-up. It never idles waiting for permission it already holds, and never ends a run with open work and no scheduled continuation. "Blocked" is not a stopping state: if the current lane is blocked, the agent self-unblocks by spawning support, running diagnostics, working a parallel lane, or arming a wake-up against the specific signal it is waiting on. The run ends only when the deliverable is met or the operator says stop. Continuity lives in the next lead or worker, never in a halted session. A stalled overnight run is a contract failure, not a neutral pause. Corollary: **do not offload to the operator a check or action the agent can perform itself** via CLI, source, `gh`, or a spawned worker.
- **Cross-repo work needs no separate approval.** The §2 grants (auto-merge, flip, swarm-blockers, rollback) and the run's authority extend to every AgentWorkforce repo the deliverable touches, not just the primary repo named in §1. When the fix lives in a sibling repo (`workforce`, `relayfile`, `relayfile-adapters`, `relayauth`, `agents`, `relaycast`, etc.), the agent implements, opens, reviews, and, within the §3 bar, merges there under the same grant. It does not pause to re-ask the operator for permission to act in a different repo. The implementation leg of a cross-repo fix is never operator-gated; only standing constraints and explicit publish/deploy gates still apply. Treating a cross-repo dependency as a fresh approval boundary is a common autonomous-run stall.
- **No manual prod deploy.** All prod changes flow through CI.
- **No direct prod SQL.** Schema changes via Drizzle migrations through PRs.
- **Redact secrets in logs / channels / PRs / reports.**
Expand Down
228 changes: 228 additions & 0 deletions .github/workflows/publish-persona.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
name: Publish Persona Package

# Publishes the @agentworkforce/persona-* persona packs (the per-persona packages
# under packages/persona-*). Separate from publish.yml (the lockstep code
# packages) and publish-personas.yml (the personas-core bundle).
#
# Uses npm trusted publishing (`--provenance` via id-token: write), so no
# NPM_TOKEN is needed. Register this workflow as a trusted publisher for each
# @agentworkforce/persona-* package before its first real publish.

on:
workflow_dispatch:
inputs:
package:
description: |
Persona package(s) to publish. Accepts:
• all (default) — every packages/persona-* package
• one or more package dir names under packages/, comma- or
space-separated (e.g. "persona-autonomous-actor" or
"persona-autonomous-actor,persona-nango-integrations")
required: true
type: string
default: "all"
version:
description: |
Version bump or exact version:
• patch / minor / major / prerelease (npm version semantics)
• none (publish current package.json version as-is)
• an exact semver like 0.2.0 or 0.2.0-beta.1
required: true
type: string
default: "patch"
prerelease_id:
description: 'Prerelease identifier for pre* bumps (e.g. "next", "beta")'
required: false
default: next
tag:
description: "npm dist-tag"
required: true
type: choice
options:
- latest
- next
- beta
default: "latest"
dry_run:
description: "Dry run (no actual publish, no version commit, no git tag)"
required: true
type: boolean
default: false

permissions:
contents: write
id-token: write

concurrency:
group: publish-persona-${{ github.ref }}
cancel-in-progress: false

jobs:
publish:
name: Publish persona packs
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Setup pnpm
uses: pnpm/action-setup@v5

- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: "22.14.0"
registry-url: "https://registry.npmjs.org"
cache: "pnpm"

- name: Install deps
run: pnpm install --frozen-lockfile

# npm >= 11.5.1 is required for the OIDC trusted-publisher flow.
- name: Install latest npm
run: npm install -g npm@latest

- name: Resolve target packs
id: targets
env:
INPUT_PACKAGE: ${{ github.event.inputs.package }}
run: |
set -euo pipefail
node --input-type=module <<'NODE' > /tmp/persona-targets.json
import { readFileSync, readdirSync } from 'node:fs';

const input = (process.env.INPUT_PACKAGE || 'all').trim();

// Every packages/persona-* package whose npm name is @agentworkforce/persona-*.
const all = [];
for (const name of readdirSync('packages', { withFileTypes: true })) {
if (!name.isDirectory() || !name.name.startsWith('persona-')) continue;
const dir = `packages/${name.name}`;
let pkg;
try {
pkg = JSON.parse(readFileSync(`${dir}/package.json`, 'utf8'));
} catch {
continue;
}
if (!pkg?.name?.startsWith('@agentworkforce/persona-')) continue;
all.push({ slug: name.name, dir, name: pkg.name });
}

let selected;
if (input === 'all') {
selected = all;
} else {
const wanted = input.split(/[\s,]+/).filter(Boolean);
const seen = new Set();
selected = [];
for (const w of wanted) {
const match = all.find((p) => p.slug === w || p.name === w);
if (!match) {
console.error(`No @agentworkforce/persona-* package matched "${w}". Known: ${all.map((p) => p.slug).join(', ')}`);
process.exit(1);
}
if (seen.has(match.name)) continue;
seen.add(match.name);
selected.push(match);
}
}
if (selected.length === 0) {
console.error('No persona packs resolved.');
process.exit(1);
}
process.stdout.write(JSON.stringify(selected));
NODE
echo "matrix=$(cat /tmp/persona-targets.json)" >> "$GITHUB_OUTPUT"
echo "Resolved: $(cat /tmp/persona-targets.json)"

- name: Bump, verify, pack + publish
env:
INPUT_VERSION: ${{ github.event.inputs.version }}
INPUT_PREID: ${{ github.event.inputs.prerelease_id }}
INPUT_TAG: ${{ github.event.inputs.tag }}
INPUT_DRY_RUN: ${{ github.event.inputs.dry_run }}
TARGETS: ${{ steps.targets.outputs.matrix }}
run: |
set -euo pipefail
PACK_DIR="$RUNNER_TEMP/packs"
mkdir -p "$PACK_DIR"

git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

: > /tmp/persona-publish-targets.tsv
COUNT=$(node -e "process.stdout.write(String(JSON.parse(process.env.TARGETS).length))")
for i in $(seq 0 $((COUNT - 1))); do
DIR=$(node -e "process.stdout.write(JSON.parse(process.env.TARGETS)[$i].dir)")
NAME=$(node -e "process.stdout.write(JSON.parse(process.env.TARGETS)[$i].name)")
echo "::group::Preparing $NAME ($DIR)"

pushd "$DIR" >/dev/null
if [ "$INPUT_VERSION" = "none" ]; then
:
elif [[ "$INPUT_VERSION" == pre* ]]; then
npm version "$INPUT_VERSION" --no-git-tag-version --preid="$INPUT_PREID"
else
npm version "$INPUT_VERSION" --no-git-tag-version --allow-same-version
fi
VERSION=$(node -p 'require("./package.json").version')
popd >/dev/null

printf '%s\t%s\t%s\n' "$NAME" "$DIR" "$VERSION" >> /tmp/persona-publish-targets.tsv

EXISTS=$(npm view "$NAME@$VERSION" version 2>/dev/null || true)
if [ -n "$EXISTS" ]; then
echo "::error title=Version already published::$NAME@$VERSION is already on npm. Pick a different bump."
exit 1
fi

echo "$NAME@$VERSION: unpublished - OK"

echo "==> Testing $NAME@$VERSION"
pnpm --filter "$NAME" test

echo "::endgroup::"
done

while IFS=$'\t' read -r NAME DIR VERSION; do
echo "::group::Publishing $NAME ($DIR)"

COMMON_FLAGS="--access public --tag $INPUT_TAG"
if [ "$INPUT_DRY_RUN" = "true" ]; then
COMMON_FLAGS="$COMMON_FLAGS --dry-run"
else
COMMON_FLAGS="$COMMON_FLAGS --provenance"
fi

echo "==> Packing $NAME@$VERSION"
TARBALL="$(pnpm --filter "$NAME" pack --pack-destination "$PACK_DIR" | tail -n 1)"
if [[ "$TARBALL" != /* ]]; then
TARBALL="$PACK_DIR/$TARBALL"
fi
if [ ! -f "$TARBALL" ]; then
echo "::error::could not find packed tarball $TARBALL in $PACK_DIR" >&2
ls -la "$PACK_DIR" >&2 || true
exit 1
fi

echo "==> Publishing $TARBALL $COMMON_FLAGS"
npm publish "$TARBALL" $COMMON_FLAGS

if [ "$INPUT_DRY_RUN" != "true" ] && [ "$INPUT_VERSION" != "none" ]; then
git add "$DIR/package.json"
if ! git diff --cached --quiet; then
git commit -m "chore(release): $NAME@$VERSION"
fi
SLUG="$(echo "${NAME#@}" | tr '/' '-')"
git tag -a "${SLUG}-v${VERSION}" -m "$NAME@$VERSION"
fi

echo "$NAME@$VERSION published (dry_run=$INPUT_DRY_RUN)" >> "$GITHUB_STEP_SUMMARY"
echo "::endgroup::"
done < /tmp/persona-publish-targets.tsv

- name: Push commits + tags
if: ${{ github.event.inputs.dry_run != 'true' && github.event.inputs.version != 'none' }}
run: git push origin HEAD --follow-tags
63 changes: 63 additions & 0 deletions packages/persona-autonomous-actor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# @agentworkforce/persona-autonomous-actor

The canonical **autonomous-actor** AgentWorkforce persona, published as a
**persona pack** so consumers install the same definition with the workforce CLI
instead of hand-maintaining divergent copies.

The persona is an autonomous orchestrator for delegated, multi-PR, multi-day,
**cutover-class** infra/feature delivery. It operates under an explicit written
contract that grants (per a defined bar) auto-merge authority, flip-the-switch
authority, swarm-blockers authority, and pre-authorized rollback — while holding
standing constraints (no manual prod deploy; no direct prod SQL;
instrument-don't-guess after two failed fixes; serialize merges through green
main) and named escalate-to-human conditions.

## Install

```bash
agentworkforce install @agentworkforce/persona-autonomous-actor
# or, to refresh an existing copy:
agentworkforce install @agentworkforce/persona-autonomous-actor --overwrite
```

The CLI copies `personas/autonomous-actor.json` into the consumer's
`.agentworkforce/workforce/personas/` (the directory the persona loader scans).
`package.json` advertises the pack via:

```json
"agentworkforce": { "personas": "personas" }
```

The whole spec — including the operations manual on `claudeMdContent` — lives in
`personas/autonomous-actor.json`. There are no external markdown sidecars.

## Skills

The persona pulls its six operating skills from the published `@agent-relay`
collection (not repo-local file paths), so a launch never hard-fails on a
missing sidecar:

- `@agent-relay/autonomous-run-contract`
- `@agent-relay/auto-merge-and-composition-safety`
- `@agent-relay/dormant-flip-and-rollback`
- `@agent-relay/swarm-blockers-and-gate-scoreboard`
- `@agent-relay/tiered-acceptance`
- `@agent-relay/instrument-dont-guess`

Those skills are published as the **private** `autonomous-run-private` prpm
collection (see the repo-root `prpm.json`).

## Programmatic usage

```ts
import persona from '@agentworkforce/persona-autonomous-actor';

console.log(persona.id); // autonomous-actor
```

## Publishing

Published via the `Publish Persona Package` GitHub workflow
(`.github/workflows/publish-persona.yml`) using npm **provenance** (trusted
publishing). The workflow publishes all `@agentworkforce/persona-*` packs by
default, or a single one via the `package` input.
36 changes: 36 additions & 0 deletions packages/persona-autonomous-actor/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export interface PersonaSkill {
id: string;
source: string;
description?: string;
}

export interface PersonaInput {
description: string;
default?: string;
}

export interface PersonaPermissions {
mode: string;
}

export interface AutonomousActorPersona {
id: string;
intent: string;
tags: string[];
description: string;
skills: PersonaSkill[];
inputs: Record<string, PersonaInput>;
mcpServers: Record<string, unknown>;
permissions: PersonaPermissions;
claudeMdContent: string;
harness: string;
model: string;
systemPrompt: string;
harnessSettings: Record<string, unknown>;
}

declare const persona: AutonomousActorPersona;

export const autonomousActorPersona: AutonomousActorPersona;

export default persona;
19 changes: 19 additions & 0 deletions packages/persona-autonomous-actor/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createRequire } from 'node:module';

const require = createRequire(import.meta.url);

/**
* The autonomous-actor persona spec. An autonomous orchestrator for delegated,
* multi-PR, multi-day, cutover-class infra/feature delivery, operating under an
* explicit written contract (auto-merge / flip / swarm / rollback authority)
* with standing constraints and escalate-to-human gates.
*
* Source of truth: `personas/autonomous-actor.json`. This compatibility export
* keeps programmatic consumers working while the package also acts as an
* AgentWorkforce installable persona pack.
*/
const persona = require('./personas/autonomous-actor.json');

export const autonomousActorPersona = persona;

export default persona;
Loading
Loading