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
30 changes: 15 additions & 15 deletions web/content/docs/relayflows/plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,29 @@ Worked example — Babysitter on Software Garden:
"schema": 2,
"kind": "flow-extension",
"name": "babysitter",
"version": "0.1.0",
"description": "Live-state PR babysitter: parallel review lenses, deterministic reconciliation, exact-head merge gate.",
"version": "0.2.0",
"description": "Native Babysitter for Software Factory: turns a verified PR delivery into one cloud:babysitter-turn request.",
"compat": {
"surface": "^2.0.22",
"sdk": "^2.0.22",
"surface": "^2.0.26",
"sdk": "^2.0.26",
"base": [{ "name": "software-factory", "version": "^2.0.0" }]
},
"entry": "babysitter.flow.ts",
"extends": { "handlers": true, "hooks": ["merge-gate"], "verbs": [], "gates": [] },
"extends": { "handlers": true, "hooks": [] },
"triggers": [
{ "provider": "github", "event": "pull_request", "actions": ["opened", "synchronize", "reopened", "ready_for_review"] },
{ "provider": "github", "event": "pull_request_review", "actions": ["submitted"] },
{ "provider": "github", "event": "pull_request", "actions": ["opened", "synchronize", "reopened", "ready_for_review", "closed", "labeled", "unlabeled"] },
{ "provider": "github", "event": "pull_request_review", "actions": ["submitted", "dismissed"] },
{ "provider": "github", "event": "check_run", "actions": ["completed"] },
{ "provider": "github", "event": "issue_comment", "actions": ["created"] }
],
"permissions": {
"integrations": ["github"],
"harnesses": ["claude"],
"harnesses": ["codex"],
"mcp": [],
"writes": ["github:pull_request:comment"],
"budget": { "dollars": 8, "wallclock": "45m" }
"writes": ["cloud:babysitter-turn"],
"budget": { "dollars": 1, "wallclock": "5m" }
},
"preflight": { "credentials": [], "servers": ["https://api.github.com"] }
"preflight": { "credentials": [], "servers": [] }
}
```

Expand All @@ -67,7 +67,7 @@ Rules that matter at review time:
- Manifest triggers are validated against the surface registry `providerEventTypes`. An event the registry cannot route is `plugin_event_unroutable`.

<Warning>
GitHub `pull_request.ready_for_review`, `pull_request.labeled`, and `pull_request.unlabeled` are not in the surface registry yet. A Babysitter manifest that declares them is refused `plugin_event_unroutable` until the relayfile adapter catalog grows. The gallery states this fail-closed; it does not paper over it.
Relayflows 2.0.26 carries all eleven Babysitter event identities, but the authored executor still refuses a matching handler before either body starts because schema-2 permissions are not isolated there. Cloud may enable Babysitter only through the separately authorized existing-session lineage path; an arbitrary labelled pull request is not execution authority.
</Warning>

## `flows add`
Expand Down Expand Up @@ -118,7 +118,7 @@ The gallery and the deploy wizard display a tier. **The label is never used to s
| **verified** | bundle `identity.json` keyid matches a publisher key registered in Cloud |
| **community** | anything else |

Babysitter in catalog v1 is **community**: the owner is AgentWorkforce, but the pinned sha is not reachable from `main` until the babysitter branch merges. That is the honest label, not a downgrade of the code.
Babysitter in catalog v2 is **first-party**: its `v2.0.26` commit is reachable from `AgentWorkforce/flows` main. The label does not widen runtime authority.

<Note>
`permissions.writes` stays a reviewed declaration labelled UNENFORCED until gate 8. Displaying a first-party or verified badge does not enforce write scope, skip the digest, or route an event the registry does not carry.
Expand All @@ -129,9 +129,9 @@ Babysitter in catalog v1 is **community**: the owner is AgentWorkforce, but the
`flowPluginBadgeMarkdown()` renders the README form. `plugin` is appended so repeats survive:

```markdown
[![Install plugin](https://agentrelay.com/deploy-flow_small.svg)](https://agentrelay.com/cloud/flows/deploy?flow=https%3A%2F%2Fgithub.com%2FAgentWorkforce%2Fflows%2Fblob%2Fb4dd665eb433bd7f52d1045543aef5f14fb7891e%2Fexamples%2Fsoftware-factory%2Fsoftware-factory.flow.ts&plugin=https%3A%2F%2Fgithub.com%2FAgentWorkforce%2Fflows%2Ftree%2F05c3dff138883322e80cb793b1f5a097ad510572%2Fexamples%2Fbabysitter)
[![Install plugin](https://agentrelay.com/deploy-flow_small.svg)](https://agentrelay.com/cloud/flows/deploy?flow=https%3A%2F%2Fgithub.com%2FAgentWorkforce%2Fflows%2Fblob%2F8b33ebab8347514f80d9da5a81206a087f641714%2Fexamples%2Fsoftware-factory%2Fsoftware-factory.flow.ts&plugin=https%3A%2F%2Fgithub.com%2FAgentWorkforce%2Fflows%2Ftree%2F8b33ebab8347514f80d9da5a81206a087f641714%2Fextensions%2Fbabysitter)
```

The first plugin in the vendored catalog is Babysitter. Its base flow URL is Software Garden (`software-factory`); its plugin URL is the tree at the pinned sha. The [plugin gallery](/flows/plugins) shows the live badge.

The catalog itself is versioned JSON, `{version:1, plugins:[{name, description, source:{owner,repo,path}, ref, digest, compat, tier, base}]}`. Any public repo with a `flows-plugin.json` is installable by URL; the catalog is a curated index, not a gate.
The catalog itself is versioned JSON, `{version:2, plugins:[{name, description, source:{owner,repo,path}, ref, digest, manifestSha256, compat, tier, base}]}`. Any public repo with a `flows-plugin.json` is installable by URL; the catalog is a curated index, not a gate.
6 changes: 3 additions & 3 deletions web/content/docs/relayflows/recommended.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Recommended flows are maintained starting points. The catalog is public, version

Software Garden is the display name of the first recommended flow. Its stable catalog and authored flow ID is `software-factory`: a GitHub issue starts implementation, deterministic repository checks, adversarial review, and a pull request for a human decision.

The released source uses Claude Code for implementation and review, so catalog version 1 allows and defaults only that harness. A future catalog version can point at a new released source with a different requirement; clients do not rewrite the authored flow.
The released source uses Claude Code for implementation and review, so catalog version 2 allows and defaults only that harness. A future catalog version can point at a new released source with a different requirement; clients do not rewrite the authored flow.

Software Garden currently supports GitHub repositories. Each activation supplies one or more repositories, and Cloud scopes a GitHub listener to each one. The catalog deliberately does not define Babysitter behavior. Babysitter is a [flow plugin](/docs/relayflows/plugins) installed onto Software Garden, not a recommended flow of its own.

Expand All @@ -22,12 +22,12 @@ GET https://agentrelay.com/api/v1/flows/catalog
GET https://agentrelay.com/api/v1/flows/catalog/software-factory
```

The list response starts with `schemaVersion: 1` and `catalogVersion: 1`. Every flow has a stable `id`, its own numeric `version`, display copy, repository-host support, trigger defaults, required/default activation inputs, and an immutable source reference.
The list response starts with `schemaVersion: 1` and `catalogVersion: 2`. Every flow has a stable `id`, its own numeric `version`, display copy, repository-host support, trigger defaults, required/default activation inputs, and an immutable source reference.

The catalog does not copy or generate the flow body. `source` names the canonical `AgentWorkforce/flows` owner, repository, path, release tag, full commit SHA, GitHub blob and raw URLs, media type, and SHA-256 content digest. Both URLs contain the commit SHA, never a mutable branch:

```text
https://github.com/AgentWorkforce/flows/blob/b4dd665eb433bd7f52d1045543aef5f14fb7891e/examples/software-factory/software-factory.flow.ts
https://github.com/AgentWorkforce/flows/blob/8b33ebab8347514f80d9da5a81206a087f641714/examples/software-factory/software-factory.flow.ts
```

CI and the production release workflow resolve the release tag, fetch the pinned raw file with a size bound, and verify the digest. A broken, moved, mutable, or drifted source therefore fails before the catalog can ship. Cloud fetches it through the existing trusted source loader and stores the fetched body only as that deployment's immutable snapshot and provenance.
Expand Down
15 changes: 8 additions & 7 deletions web/data/flow-plugin-catalog.v1.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
{
"version": 1,
"version": 2,
"plugins": [
{
"name": "babysitter",
"description": "Live-state PR babysitter: parallel review lenses, deterministic reconciliation, exact-head merge gate. Fail-closed: GitHub pull_request.ready_for_review, labeled, and unlabeled are not in the surface registry, so a manifest that declares them is refused plugin_event_unroutable until the relayfile adapter catalog grows.",
"source": { "owner": "AgentWorkforce", "repo": "flows", "path": "examples/babysitter" },
"ref": "05c3dff138883322e80cb793b1f5a097ad510572",
"digest": "ae6af3335eb6d4e54559327acc1465419244b47911d8ff356850b61f6228d862",
"compat": { "surface": "^2.0.22", "sdk": "^2.0.22", "base": ["software-factory"] },
"tier": "community",
"description": "Native Babysitter for Software Garden: eleven GitHub wake events request one authorized existing-session turn. Cloud rechecks the live babysit label, pull-request head, and session binding; the extension holds no GitHub write or merge authority. Handler execution remains fail-closed until the Cloud capability adapter and native delivery path ship.",
"source": { "owner": "AgentWorkforce", "repo": "flows", "path": "extensions/babysitter" },
"ref": "8b33ebab8347514f80d9da5a81206a087f641714",
"digest": "bdf2187b9a242667d34bbc63e7a744753e146dc8cd6f4047047f2aed28f406ee",
"manifestSha256": "5631a06bbdc8186f4ee0ff955610ead24d001c5197b59fb1fe81fe422c44f226",
"compat": { "surface": "^2.0.26", "sdk": "^2.0.26", "base": ["software-factory"] },
"tier": "first-party",
"base": ["software-factory"]
}
]
Expand Down
14 changes: 7 additions & 7 deletions web/data/recommended-flow-catalog.v1.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"schemaVersion": 1,
"catalogVersion": 1,
"catalogVersion": 2,
"flows": [
{
"id": "software-factory",
"version": 1,
"version": 2,
"name": "Software Garden",
"summary": "Turn GitHub issues into reviewed, tested pull requests.",
"description": "Implement a ticket, run the repository checks outside the agent, put the change through adversarial review, and open a pull request for a human decision.",
Expand All @@ -26,12 +26,12 @@
"owner": "AgentWorkforce",
"repo": "flows",
"path": "examples/software-factory/software-factory.flow.ts",
"release": "v2.0.22",
"ref": "b4dd665eb433bd7f52d1045543aef5f14fb7891e",
"url": "https://github.com/AgentWorkforce/flows/blob/b4dd665eb433bd7f52d1045543aef5f14fb7891e/examples/software-factory/software-factory.flow.ts",
"rawUrl": "https://raw.githubusercontent.com/AgentWorkforce/flows/b4dd665eb433bd7f52d1045543aef5f14fb7891e/examples/software-factory/software-factory.flow.ts",
"release": "v2.0.26",
"ref": "8b33ebab8347514f80d9da5a81206a087f641714",
"url": "https://github.com/AgentWorkforce/flows/blob/8b33ebab8347514f80d9da5a81206a087f641714/examples/software-factory/software-factory.flow.ts",
"rawUrl": "https://raw.githubusercontent.com/AgentWorkforce/flows/8b33ebab8347514f80d9da5a81206a087f641714/examples/software-factory/software-factory.flow.ts",
"mediaType": "text/typescript",
"sha256": "41c2137179455881a0fcf568006d2b41e24386ba1cf7dc2c6051871b6f744368"
"sha256": "49c993220b9c34fab2d4b0e51911656f62b8b657f534d988691960d45bb9d9b6"
}
}
]
Expand Down
4 changes: 2 additions & 2 deletions web/lib/flow-local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ import { workflowAgents } from './flow-workflows';
* Cloud, which must render a `declined` run before any flow emits one. Switch
* the two guards together with that Cloud change, not as part of a pin bump.
*
* 2.0.22 is the current release. Beyond the above it carries
* 2.0.26 is the current release. Beyond the above it carries
* AgentWorkforce/flows#484: a Claude agent step completes on Claude's own
* result rather than on process exit, so an agent that started a background
* task no longer holds the run open until its wall-clock limit.
*/
export const RELAYFLOWS_VERSION = '2.0.22';
export const RELAYFLOWS_VERSION = '2.0.26';
export const LOCAL_PREFLIGHT = 'relay-preflight.mjs';

export const LOCAL_INSTALL = `npm install --save-dev relayflows@${RELAYFLOWS_VERSION} @relayflows/surface@${RELAYFLOWS_VERSION}`;
Expand Down
5 changes: 3 additions & 2 deletions web/lib/flow-plugin-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ export type FlowPluginCatalogEntry = {
source: { owner: string; repo: string; path: string };
ref: string;
digest: string;
manifestSha256: string;
compat: { surface: string; sdk: string; base: string[] };
tier: FlowPluginTrustTier;
base: string[];
};

export type FlowPluginCatalog = {
version: 1;
version: 2;
plugins: FlowPluginCatalogEntry[];
};

Expand All @@ -43,7 +44,7 @@ export const FLOW_PLUGIN_TRUST_TIER_DISCLAIMER =
'Tiers are displayed only. They never skip digest, compat, or event-routability checks.';

export const SOFTWARE_FACTORY_FLOW_URL =
'https://github.com/AgentWorkforce/flows/blob/b4dd665eb433bd7f52d1045543aef5f14fb7891e/examples/software-factory/software-factory.flow.ts';
'https://github.com/AgentWorkforce/flows/blob/8b33ebab8347514f80d9da5a81206a087f641714/examples/software-factory/software-factory.flow.ts';

export const BASE_FLOW_URLS: Record<string, string> = {
'software-factory': SOFTWARE_FACTORY_FLOW_URL,
Expand Down
4 changes: 3 additions & 1 deletion web/lib/test/flow-local.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ describe('local flow starter kit', () => {
// started can no longer hold the run open until its wall-clock limit.
expect(LOCAL_INSTALL).toContain(`relayflows@${RELAYFLOWS_VERSION}`);
expect(LOCAL_INSTALL).toContain(`@relayflows/surface@${RELAYFLOWS_VERSION}`);
expect(RELAYFLOWS_VERSION).toBe('2.0.22');
// 2.0.26 adds the full Babysitter GitHub trigger vocabulary and keeps the
// local starter on the same released Surface/SDK graph as the catalog.
expect(RELAYFLOWS_VERSION).toBe('2.0.26');
for (const workflow of ['traditional', 'prototype', 'simple'] as const) {
const source = factorySource({ ...draft, workflow }, 'local');
const code = withoutComments(source);
Expand Down
29 changes: 18 additions & 11 deletions web/lib/test/flow-plugin-catalog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,43 +21,50 @@ const NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
describe('flow plugin catalog', () => {
const catalog = getFlowPluginCatalog();

it('is version 1 with unique kebab-case plugin names', () => {
expect(catalog.version).toBe(1);
it('is version 2 with unique kebab-case plugin names', () => {
expect(catalog.version).toBe(2);
expect(Array.isArray(catalog.plugins)).toBe(true);
expect(catalog.plugins.length).toBeGreaterThan(0);
const names = catalog.plugins.map((plugin) => plugin.name);
expect(names.every((name) => NAME.test(name))).toBe(true);
expect(new Set(names).size).toBe(names.length);
});

it('records a fail-closed babysitter entry with a pinned sha and digest', () => {
it('records the released Babysitter artifact without claiming handler execution', () => {
const babysitter = getFlowPlugin('babysitter');
expect(babysitter).toMatchObject({
source: { owner: 'AgentWorkforce', repo: 'flows', path: 'examples/babysitter' },
tier: 'community',
source: { owner: 'AgentWorkforce', repo: 'flows', path: 'extensions/babysitter' },
ref: '8b33ebab8347514f80d9da5a81206a087f641714',
digest: 'bdf2187b9a242667d34bbc63e7a744753e146dc8cd6f4047047f2aed28f406ee',
manifestSha256: '5631a06bbdc8186f4ee0ff955610ead24d001c5197b59fb1fe81fe422c44f226',
compat: { surface: '^2.0.26', sdk: '^2.0.26', base: ['software-factory'] },
tier: 'first-party',
base: ['software-factory'],
});
expect(babysitter!.ref).toMatch(SHA);
expect(babysitter!.digest).toMatch(HEX64);
expect(babysitter!.description).toContain('plugin_event_unroutable');
expect(babysitter!.manifestSha256).toMatch(HEX64);
expect(babysitter!.description).not.toContain('plugin_event_unroutable');
expect(babysitter!.description).toContain('fail-closed');
expect(babysitter!.description).toContain('no GitHub write or merge authority');
expect(FLOW_PLUGIN_TRUST_TIERS.includes(babysitter!.tier)).toBe(true);
expect(pluginHasUnroutableTriggers(babysitter!)).toBe(true);
expect(pluginHasUnroutableTriggers(babysitter!)).toBe(false);
});

it('builds a GitHub tree URL at the pinned sha, not a branch', () => {
const babysitter = getFlowPlugin('babysitter')!;
expect(flowPluginSourceUrl(babysitter)).toBe(
`https://github.com/AgentWorkforce/flows/tree/${babysitter.ref}/examples/babysitter`,
`https://github.com/AgentWorkforce/flows/tree/${babysitter.ref}/extensions/babysitter`,
);
expect(flowPluginGithubRef(babysitter)).toBe(
`github:AgentWorkforce/flows@${babysitter.ref}#examples/babysitter`,
`github:AgentWorkforce/flows@${babysitter.ref}#extensions/babysitter`,
);
});
});

describe('flowPluginBadgeMarkdown', () => {
const pluginA =
'https://github.com/AgentWorkforce/flows/tree/05c3dff138883322e80cb793b1f5a097ad510572/examples/babysitter';
'https://github.com/AgentWorkforce/flows/tree/8b33ebab8347514f80d9da5a81206a087f641714/extensions/babysitter';
const pluginB = 'github:acme/plugins@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#extra';

it('points the Install plugin badge at /cloud/flows/deploy with flow and plugin', () => {
Expand All @@ -74,7 +81,7 @@ describe('flowPluginBadgeMarkdown', () => {
const url = new URL(href!);
expect(url.pathname).toBe('/cloud/flows/deploy');
expect(url.searchParams.get('flow')).toBe(SOFTWARE_FACTORY_FLOW_URL);
expect(SOFTWARE_FACTORY_FLOW_URL).toContain('/blob/b4dd665eb433bd7f52d1045543aef5f14fb7891e/');
expect(SOFTWARE_FACTORY_FLOW_URL).toContain('/blob/8b33ebab8347514f80d9da5a81206a087f641714/');
expect(SOFTWARE_FACTORY_FLOW_URL).not.toContain('/blob/main/');
expect(url.searchParams.getAll('plugin')).toEqual([pluginA]);
});
Expand Down
16 changes: 8 additions & 8 deletions web/lib/test/recommended-flow-catalog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ describe('recommended flow catalog', () => {
const catalog = getRecommendedFlowCatalog();
expect(catalog).toEqual({
schemaVersion: 1,
catalogVersion: 1,
catalogVersion: 2,
flows: [{
id: 'software-factory',
version: 1,
version: 2,
name: 'Software Garden',
summary: expect.any(String),
description: expect.any(String),
Expand All @@ -31,12 +31,12 @@ describe('recommended flow catalog', () => {
owner: 'AgentWorkforce',
repo: 'flows',
path: 'examples/software-factory/software-factory.flow.ts',
release: 'v2.0.22',
ref: 'b4dd665eb433bd7f52d1045543aef5f14fb7891e',
url: 'https://github.com/AgentWorkforce/flows/blob/b4dd665eb433bd7f52d1045543aef5f14fb7891e/examples/software-factory/software-factory.flow.ts',
rawUrl: 'https://raw.githubusercontent.com/AgentWorkforce/flows/b4dd665eb433bd7f52d1045543aef5f14fb7891e/examples/software-factory/software-factory.flow.ts',
release: 'v2.0.26',
ref: '8b33ebab8347514f80d9da5a81206a087f641714',
url: 'https://github.com/AgentWorkforce/flows/blob/8b33ebab8347514f80d9da5a81206a087f641714/examples/software-factory/software-factory.flow.ts',
rawUrl: 'https://raw.githubusercontent.com/AgentWorkforce/flows/8b33ebab8347514f80d9da5a81206a087f641714/examples/software-factory/software-factory.flow.ts',
mediaType: 'text/typescript',
sha256: '41c2137179455881a0fcf568006d2b41e24386ba1cf7dc2c6051871b6f744368',
sha256: '49c993220b9c34fab2d4b0e51911656f62b8b657f534d988691960d45bb9d9b6',
},
}],
});
Expand Down Expand Up @@ -75,7 +75,7 @@ describe('recommended flow catalog HTTP surface', () => {
await expect(detail.json()).resolves.toMatchObject({
id: 'software-factory',
name: 'Software Garden',
source: { ref: 'b4dd665eb433bd7f52d1045543aef5f14fb7891e' },
source: { ref: '8b33ebab8347514f80d9da5a81206a087f641714' },
});
});

Expand Down
Loading
Loading