Skip to content

Commit db31ddf

Browse files
khaliqgantRicky Schema Cascadeclaude
authored
feat(deploy/persona-kit): cloud mode + relayfile watch personas (spec 03) (#130)
* feat(deploy/persona-kit): cloud mode + relayfile watch personas Spec 03 workforce-side remaining work. Unstubs the cloud deploy mode so proactive personas reach the cloud proactive-personas endpoint, and adds the persona-kit surface (watch rules, mount.enabled, lint codes) that makes those personas authorable. - packages/deploy/src/modes/cloud/: replace the 1013-line stub cloud.ts with a real launcher that routes proactive personas to /api/v1/workspaces/:workspaceId/proactive-personas/:personaId. - packages/deploy/src/{deploy,index,types,modes/input-values.test}.ts: redirect cloud.js imports to cloud/index.js. - packages/deploy/src/modes/cloud.test.ts: assert proactive personas hit the proactive-personas endpoint exactly once and non-proactive personas do not. - packages/persona-kit/schemas/persona.schema.json: add WatchRule definition and mount.enabled field. - packages/persona-kit/src/{triggers,parse,plan,types,index}.ts + matching .test.ts files: lint codes watch_path_not_absolute / watch_empty_events, parse + plan the watch field, surface in the public API. - packages/persona-kit/src/__fixtures__/personas/proactive-watch-persona.json: reference fixture for the new schema. - examples/proactive-issue-resolver/: end-to-end example using a watch-rule persona to react to relayfile changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(deploy/cloud): consolidate proactive personas onto /deployments Per the v1-consolidation discussion on AgentWorkforce/cloud#912 (closed) → AgentWorkforce/cloud#919 (draft). Previously the cloud launcher bifurcated on `isProactivePersona(persona)`: proactive personas (persona.watch.length > 0) hit /api/v1/workspaces/{ws}/proactive-personas/{id} with a body that duplicated watch[] + mount{} as sibling fields, and used deleteProactivePersona to tear down. Regular personas hit /api/v1/workspaces/{ws}/deployments and used deleteAgent. The v1 surface (agents + deployments) already handles persona.watch via cloud's preparePersonaDeploy + the new agents.watch_rules column (cloud#919). The parallel surface was ~90% redundant per the reviewer's stabilization-trail analysis. Now every persona — proactive or not — flows through: - POST /api/v1/workspaces/{ws}/deployments with the same body shape the regular path always used. The persona's top-level watch[] travels inside the persona object; cloud extracts and persists it as watch_rules on the agents row. - DELETE /api/v1/workspaces/{ws}/deployments/{agentId} via deleteAgent. - The handleExistingPersona check now applies universally (was previously skipped for proactive personas). Removed: - isProactivePersona, readPersonaWatch, readPersonaMount helpers - proactivePersonasEndpoint URL builder - deleteProactivePersona network helper - Sibling `watch` + `mount` fields from the POST body (read from persona) Tests: - cloud.test.ts: the two proactive-specific tests now assert the unified /deployments path (and that /proactive-personas is never called). The "keeps non-proactive on deployments" test is unchanged. The persona-kit watch / mount.enabled schema additions remain in this PR unchanged — those are how authors *declare* the watch DSL, independent of how the launcher routes it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: address proactive resolver review comments --------- Co-authored-by: Ricky Schema Cascade <ricky@agent-relay.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 25c1a1f commit db31ddf

27 files changed

Lines changed: 6435 additions & 52 deletions
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# proactive-issue-resolver (v1, local, manually-triggered)
2+
3+
Local agent that turns a GitHub issue into a spec, hands the spec to the
4+
[`@agentworkforce/ricky`](https://www.npmjs.com/package/@agentworkforce/ricky)
5+
SDK to generate + run a workflow that opens a PR via
6+
`@agent-relay/github-primitive`, and DMs the result to Slack.
7+
8+
**Important runtime note:** workforce `--mode dev` does NOT subscribe to live
9+
GitHub events. The runtime reads NDJSON envelopes from stdin
10+
(`packages/runtime/src/runner.ts:184`); live event ingress is a `--mode cloud`
11+
feature that isn't wired up yet. So v1 is **manually-triggered per-issue** via
12+
`trigger-issue.sh`, not background-subscribed.
13+
14+
This is actually a safer v1 — the agent only acts on issues you point it at,
15+
not every issue opened in the repo.
16+
17+
The full event-subscribed end-to-end design (and the trust envelope / approval
18+
gates that go with it) lives in [`SPEC.md`](./SPEC.md).
19+
20+
## Flow
21+
22+
```
23+
trigger-issue.sh owner repo N
24+
→ gh api repos/owner/repo/issues/N (fetch real issue)
25+
→ wrap as github.issues.opened envelope (NDJSON)
26+
→ pipe → agentworkforce deploy --mode dev (runner consumes one envelope)
27+
→ handler claims issue (`gh issue edit --add-label ricky-claimed`)
28+
→ handler comments :robot: on the issue
29+
→ claude harness investigates repo + writes spec.md
30+
→ createRickySdk({cwd}).generateLocalWorkflow({ spec, run: true, autoFixAttempts: 3, bestJudgement: false })
31+
→ Ricky-generated workflow opens PR via @agent-relay/github-primitive
32+
→ Slack DM (or channel post) with PR URL on success, failure summary on hard fail
33+
→ runner exits when stdin closes
34+
```
35+
36+
## Prerequisites
37+
38+
| Check | Command |
39+
| --- | --- |
40+
| `gh` CLI authed | `gh auth status` |
41+
| Workforce logged in | `cat ~/.agentworkforce/active.json` (should show a workspace UUID) |
42+
| `agentworkforce` CLI on PATH | `which agentworkforce` |
43+
| `jq` installed | `brew install jq` |
44+
| Slack provider connected in Relayfile | `relayfile integration list` should show `slack` |
45+
| `ricky` installed in this dir | `ls node_modules/@agentworkforce/ricky/dist/index.js` |
46+
47+
## Install
48+
49+
```sh
50+
REPO_ROOT=$(git rev-parse --show-toplevel)
51+
cd "$REPO_ROOT/examples/proactive-issue-resolver"
52+
npm install
53+
```
54+
55+
This pulls `@agentworkforce/ricky` into the local `node_modules`. esbuild (used
56+
by the workforce bundler) leaves `@agentworkforce/runtime` external but bundles
57+
`@agentworkforce/ricky` inline — see
58+
`packages/deploy/src/bundle.ts:62`.
59+
60+
## Configure
61+
62+
| Var | Effect |
63+
| --- | --- |
64+
| `PROACTIVE_SLACK_USER` | DM this Slack user ID (preferred for v1). |
65+
| `PROACTIVE_SLACK_CHANNEL` | Post in this Slack channel ID. |
66+
| `PROACTIVE_USE_CLOUD` | `true` → dispatch via `ricky.generateCloudWorkflow` instead of `generateLocalWorkflow`. Requires `AGENTWORKFORCE_TOKEN` + `AGENTWORKFORCE_WORKSPACE_ID`. Today the Ricky cloud executor returns `runtime-not-wired`; this branch surfaces that as a hard fail. See [`specs/cloud-mode-stub.md`](./specs/cloud-mode-stub.md). |
67+
| `AGENTWORKFORCE_TOKEN` | Cloud bearer token. Required iff `PROACTIVE_USE_CLOUD=true`. |
68+
| `AGENTWORKFORCE_WORKSPACE_ID` | Cloud workspace ID. Required iff `PROACTIVE_USE_CLOUD=true`. |
69+
70+
If both Slack vars are set, `PROACTIVE_SLACK_USER` wins.
71+
72+
## Run (right-now path)
73+
74+
From the repo you want the PR opened against (so the sandbox cwd is that
75+
repo's checkout):
76+
77+
```sh
78+
REPO_ROOT=$(git rev-parse --show-toplevel)
79+
cd "$REPO_ROOT" # or any repo
80+
PROACTIVE_SLACK_USER=U0ADJH4P83T \
81+
"$REPO_ROOT/examples/proactive-issue-resolver/trigger-issue.sh" \
82+
AgentWorkforce workforce 123
83+
```
84+
85+
What happens:
86+
87+
1. `gh` fetches issue #123 and the repo metadata.
88+
2. The script wraps both in a `github.issues.opened` envelope and pipes it to
89+
`agentworkforce deploy ... --mode dev`.
90+
3. The handler adds the `ricky-claimed` label to issue #123 and acquires a
91+
deterministic Git ref lock before dispatch.
92+
4. Handler comments `:robot: Proactive agent picked up #123. Investigating…`.
93+
5. Spec gets written to `.proactive/issue-123-spec.md`.
94+
6. Ricky generates and runs `workflows/generated/resolve-issue-workforce-123.ts`.
95+
7. On success a PR is opened; Slack DM with PR URL.
96+
8. The runner exits when stdin closes.
97+
98+
## Validate without running
99+
100+
```sh
101+
REPO_ROOT=$(git rev-parse --show-toplevel)
102+
agentworkforce deploy \
103+
"$REPO_ROOT/examples/proactive-issue-resolver/persona.json" \
104+
--mode dev --dry-run
105+
```
106+
107+
Should print `ok: proactive-issue-resolver (dry-run)`. The persona is checked
108+
against this output today.
109+
110+
## What was sanity-checked
111+
112+
- `agentworkforce` CLI v3.0.14 installed at `~/.local/share/mise/installs/node/22.22.1/bin/agentworkforce`.
113+
- `~/.agentworkforce/active.json` shows an active workspace.
114+
- `gh auth status` shows logged-in `khaliqgant` with `repo` scope.
115+
- `agentworkforce deploy ... --mode dev --dry-run` returns `ok`.
116+
- Handler signature matches `packages/runtime/src/types.ts:259`:
117+
`handler((ctx, event) => ...)`.
118+
- Envelope shape matches `RawGatewayEnvelope` in
119+
`packages/runtime/src/shim.ts:17`; type `github.issues.opened` splits to
120+
source=github, type=issues.opened, payload=resource.
121+
- `--mode dev` pipes parent stdin to runner stdin
122+
(`packages/deploy/src/modes/dev.ts:57`), so single-envelope stdin pipe →
123+
single dispatch → runner exits.
124+
- esbuild bundles `@agentworkforce/ricky` inline; `@agentworkforce/runtime`
125+
stays external (`packages/deploy/src/bundle.ts:62`).
126+
- Intent `relay-orchestrator` is in `PERSONA_INTENTS`
127+
(`packages/persona-kit/src/constants.ts:50`).
128+
- Persona must have `cloud: true` to be opt-in to `deploy` (even for `--mode
129+
dev`); we set it.
130+
131+
## v1 limitations (deferred to v2 — see SPEC.md)
132+
133+
- **Manual trigger only.** No auto-subscription to live GitHub webhooks; that
134+
needs `--mode cloud` ingress (not wired) or a local Relayfile-backed event
135+
bridge.
136+
- **Label + Git ref claim only, no in-flight registry.** If the persona crashes
137+
between claim and dispatch, manual cleanup is required (`gh issue edit
138+
--remove-label ricky-claimed` and delete the `agentworkforce/locks/...` ref).
139+
- **No trust envelope.** v1 ships a PR for every triggered issue regardless
140+
of label or path. v2 reads `.proactive/trust-policy.yaml`.
141+
- **No human approval gate.** v2 adds a `:thumbsup:` reactji gate.
142+
- **No PR verification.** v2 GETs the PR to confirm `Closes #N` and
143+
non-default branch.
144+
- **Single-attempt Ricky run.** No retry on hard fail.
145+
146+
## Files
147+
148+
| File | Purpose |
149+
| --- | --- |
150+
| `persona.json` | Workforce persona definition. |
151+
| `agent.ts` | Handler. |
152+
| `package.json` | Pulls `@agentworkforce/ricky` into local node_modules. |
153+
| `trigger-issue.sh` | Manual trigger: `owner repo N` → envelope → deploy. |
154+
| `SPEC.md` | v2 end-to-end design (event subscription, trust envelope, etc.). |

0 commit comments

Comments
 (0)