agent-conveyor 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -132,6 +132,19 @@ command exits 0 and the JSON result reports `"ok": true`.
132
132
  Before publishing `agent-conveyor` to npm, use
133
133
  [`docs/package-release.md`](docs/package-release.md).
134
134
 
135
+ For common manager setups, start with
136
+ [`docs/manager-recipes.md`](docs/manager-recipes.md). It maps natural-language
137
+ requests such as GoalBuddy conveyor runs, test coverage loops, UX polish loops,
138
+ what-next nudging, and PR/CI/merge Ralph loops to concrete `manager-config`
139
+ settings, permissions, evidence gates, cleanup behavior, and example
140
+ manager/Dispatch/worker interactions. Use `conveyor manager-recipes --list`
141
+ or `conveyor manager-recipes --show goalbuddy-conveyor --json` for a
142
+ machine-readable setup preview.
143
+ For a package-facing overview of these modes, open
144
+ [`docs/landing-page.html`](docs/landing-page.html) locally or host it as a
145
+ static landing page. From the repo, `npm run docs:landing` serves it at
146
+ `http://127.0.0.1:8765/`.
147
+
135
148
  After install, the intended Codex app entry point is natural language. Open a
136
149
  new Codex app session in the target repo and say:
137
150
 
@@ -163,6 +176,10 @@ Use `conveyor qa-plan adversarial-triggers` to verify natural-language
163
176
  manager prompts activate Ralph-loop adversarial gates.
164
177
  Use `conveyor qa-plan goalbuddy-conveyor` when a broad request should become
165
178
  sequential GoalBuddy child boards with PR/CI/merge receipts.
179
+ Before cutting a manager loose, have it resolve the freeform setup request to a
180
+ named recipe from `docs/manager-recipes.md` or an explicit `custom` setup, then
181
+ show the saved mode, permissions, evidence gates, cleanup policy, and disallowed
182
+ actions.
166
183
  For manual QA, launch the dashboard with Dispatch enforcement so the page can
167
184
  show live proof:
168
185
 
@@ -411,6 +428,11 @@ tmux attach -t codex-live-test
411
428
  Use `--require` when a manager command should fail closed. Use
412
429
  `--require-handoff` before worker compact/clear style instructions so visible
413
430
  context is persisted first.
431
+ - `manager-recipes --list|--show RECIPE [--json]` — List or show built-in
432
+ manager setup recipes. Recipe JSON includes the supervision mode,
433
+ permissions, expected tools, epilogues, evidence gates, cleanup behavior,
434
+ disallowed actions, locked setup summary template, and suggested
435
+ `manager-config` command. Use this before cutting a manager loose.
414
436
  - `worker-ack <task> --from-stdin|--json [--correlation-id ID]` /
415
437
  `manager-ack <task> --from-stdin|--json [--correlation-id ID]` — Persist or
416
438
  read the latest structured acknowledgement from the worker or manager. Acks
package/dist/cli/main.js CHANGED
@@ -1,13 +1,16 @@
1
1
  #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
2
3
  import { programNameFromArgv } from "./program-name.js";
3
4
  import { runTypescriptRuntimeCommand } from "./typescript-runtime.js";
4
5
  const args = process.argv.slice(2);
5
6
  const program = programNameFromArgv(process.argv, process.env);
7
+ const stdin = args.includes("--from-stdin") ? readFileSync(0, "utf8") : undefined;
6
8
  const typescriptRuntime = runTypescriptRuntimeCommand({
7
9
  args,
8
10
  cwd: process.cwd(),
9
11
  env: process.env,
10
12
  program,
13
+ stdin,
11
14
  });
12
15
  if (typescriptRuntime.stdout) {
13
16
  process.stdout.write(typescriptRuntime.stdout);
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAEtE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,OAAO,GAAG,mBAAmB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;AAC/D,MAAM,iBAAiB,GAAG,2BAA2B,CAAC;IACpD,IAAI;IACJ,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;IAClB,GAAG,EAAE,OAAO,CAAC,GAAG;IAChB,OAAO;CACR,CAAC,CAAC;AAEH,IAAI,iBAAiB,CAAC,MAAM,EAAE,CAAC;IAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;AACjD,CAAC;AACD,IAAI,iBAAiB,CAAC,MAAM,EAAE,CAAC;IAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;AACjD,CAAC;AACD,OAAO,CAAC,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,CAAC"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAEtE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,OAAO,GAAG,mBAAmB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;AAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAClF,MAAM,iBAAiB,GAAG,2BAA2B,CAAC;IACpD,IAAI;IACJ,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;IAClB,GAAG,EAAE,OAAO,CAAC,GAAG;IAChB,OAAO;IACP,KAAK;CACN,CAAC,CAAC;AAEH,IAAI,iBAAiB,CAAC,MAAM,EAAE,CAAC;IAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;AACjD,CAAC;AACD,IAAI,iBAAiB,CAAC,MAAM,EAAE,CAAC;IAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;AACjD,CAAC;AACD,OAAO,CAAC,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,CAAC"}
@@ -109,6 +109,9 @@ export function runTypescriptRuntimeCommand(options) {
109
109
  if (parsed.command === "ralph-loop-presets") {
110
110
  return runRalphLoopPresetsCommand(parsed, options);
111
111
  }
112
+ if (parsed.command === "manager-recipes") {
113
+ return runManagerRecipesCommand(parsed);
114
+ }
112
115
  if (parsed.command === "loop-triggers") {
113
116
  return runLoopTriggersCommand(parsed, options);
114
117
  }
@@ -593,6 +596,7 @@ function parseRuntimeArgs(args, env) {
593
596
  && command !== "runs"
594
597
  && command !== "loop-templates"
595
598
  && command !== "ralph-loop-presets"
599
+ && command !== "manager-recipes"
596
600
  && command !== "loop-triggers"
597
601
  && command !== "manager-permission"
598
602
  && command !== "continuation"
@@ -946,7 +950,7 @@ function parseRuntimeArgs(args, env) {
946
950
  index += 1;
947
951
  }
948
952
  else if (arg === "--show") {
949
- if (command !== "runs" && command !== "loop-templates" && command !== "ralph-loop-presets") {
953
+ if (command !== "runs" && command !== "loop-templates" && command !== "ralph-loop-presets" && command !== "manager-recipes") {
950
954
  return { command, enabled, error: "Unsupported TypeScript runtime option: --show", explicit, flags, task };
951
955
  }
952
956
  const value = valueAfter(queue, index, arg);
@@ -3110,6 +3114,43 @@ function runRalphLoopPresetsCommand(parsed, options) {
3110
3114
  database.close();
3111
3115
  }
3112
3116
  }
3117
+ function runManagerRecipesCommand(parsed) {
3118
+ const unsupportedOptions = unsupportedLoopCommandOptions(parsed, {
3119
+ allowedFlags: new Set(["json", "list", "show"]),
3120
+ commandName: "manager-recipes",
3121
+ });
3122
+ if (unsupportedOptions) {
3123
+ return unsupportedRuntimeResult(parsed, unsupportedOptions);
3124
+ }
3125
+ const actionCount = [parsed.flags.list, parsed.flags.show !== null].filter(Boolean).length;
3126
+ if (actionCount !== 1) {
3127
+ return errorResult("Choose one of --list or --show");
3128
+ }
3129
+ if (parsed.flags.list) {
3130
+ const recipes = listManagerRecipes();
3131
+ if (parsed.flags.json) {
3132
+ return jsonResult({ recipes });
3133
+ }
3134
+ return textResult(recipes.map((recipe) => {
3135
+ const loop = recipe.loop_template ? ` loop=${recipe.loop_template}` : "";
3136
+ return `${recipe.name}\tmode=${recipe.mode}${loop}\t${recipe.description}`;
3137
+ }));
3138
+ }
3139
+ const recipe = managerRecipeSummary(parsed.flags.show ?? "");
3140
+ if (parsed.flags.json) {
3141
+ return jsonResult({ recipe });
3142
+ }
3143
+ const lines = [
3144
+ String(recipe.locked_summary_template),
3145
+ "",
3146
+ "manager config command:",
3147
+ ` ${recipe.manager_config_command.map(shellQuote).join(" ")}`,
3148
+ ];
3149
+ if (recipe.loop_template) {
3150
+ lines.push("", `loop template: ${recipe.loop_template}`);
3151
+ }
3152
+ return textResult(lines);
3153
+ }
3113
3154
  function runLoopTriggersCommand(parsed, _options) {
3114
3155
  const unsupported = unsupportedMigratedProofCliOptions(parsed);
3115
3156
  if (unsupported) {
@@ -13418,6 +13459,7 @@ function isDefaultRuntimeCommand(command) {
13418
13459
  || command === "loop-templates"
13419
13460
  || command === "loop-triggers"
13420
13461
  || command === "ralph-loop-presets"
13462
+ || command === "manager-recipes"
13421
13463
  || command === "qa-plan"
13422
13464
  || command === "qa-run"
13423
13465
  || command === "start"
@@ -15170,6 +15212,232 @@ function ralphLoopPresetMetadata(name, options) {
15170
15212
  ralphLoopPreset(name);
15171
15213
  return loopTemplateMetadata(name, options);
15172
15214
  }
15215
+ const MANAGER_RECIPES = {
15216
+ "goalbuddy-conveyor": {
15217
+ acceptance: [
15218
+ "Every child board has PR/CI/merge, satisfied_on_main, or blocker proof.",
15219
+ "Parent state records final status for every child.",
15220
+ ],
15221
+ cleanup: "compact between child boards after saved handoff",
15222
+ description: "Run broad work as one parent GoalBuddy board with one active child board at a time.",
15223
+ disallowedActions: [
15224
+ "Do not run two child boards at once.",
15225
+ "Do not merge without green CI.",
15226
+ "Do not compact or clear before a saved handoff.",
15227
+ ],
15228
+ displayName: "GoalBuddy Conveyor",
15229
+ epilogues: ["draft-pr", "record-handoff"],
15230
+ evidenceGates: [
15231
+ "child receipt with focused verification",
15232
+ "adversarial review",
15233
+ "PR/CI/merge or satisfied_on_main proof",
15234
+ "parent receipt update before the next child",
15235
+ ],
15236
+ guidelines: [
15237
+ "Keep exactly one child board active at a time.",
15238
+ "Before activating the next child, update the parent receipt.",
15239
+ ],
15240
+ loopTemplate: null,
15241
+ mode: "strict",
15242
+ name: "goalbuddy-conveyor",
15243
+ objective: "Run a one-child-at-a-time GoalBuddy conveyor until every child is merged, proven satisfied, or blocked with evidence.",
15244
+ permissions: ["repo.open_pr", "repo.merge_green_pr", "worker_session.compact", "worker_session.clear"],
15245
+ supportPatterns: ["Inbox / No-Tmux App Loop", "Recovery / Resume / Handoff"],
15246
+ tools: ["verification.run_tests", "context.fetch_prs"],
15247
+ },
15248
+ "nudge-whats-next": {
15249
+ acceptance: [
15250
+ "Accepted criteria are satisfied or explicitly deferred.",
15251
+ "The final summary names commands run, changed files, and residual risk.",
15252
+ ],
15253
+ cleanup: "off by default",
15254
+ description: "Observe, ask useful status questions, negotiate criteria, and keep permissions minimal.",
15255
+ disallowedActions: ["Do not grant repo or worker-session mutation permissions by default."],
15256
+ displayName: "Nudge / What's Next Manager",
15257
+ epilogues: [],
15258
+ evidenceGates: ["manager decision", "worker receipt", "accepted criteria closure"],
15259
+ guidelines: [
15260
+ "Prefer wait over nudge while the worker is active.",
15261
+ "Ask for must-have current-task criteria versus follow-ups when scope changes.",
15262
+ ],
15263
+ loopTemplate: null,
15264
+ mode: "guided",
15265
+ name: "nudge-whats-next",
15266
+ objective: "Observe the worker, ask useful status and next-step questions, and finish only with evidence.",
15267
+ permissions: [],
15268
+ supportPatterns: ["Inbox / No-Tmux App Loop", "Recovery / Resume / Handoff"],
15269
+ tools: [],
15270
+ },
15271
+ "pr-ci-merge-ralph-loop": {
15272
+ acceptance: [
15273
+ "PR URL, green CI, merge receipt, and adversarial proof are recorded.",
15274
+ "Worker handoff exists before compact or clear.",
15275
+ ],
15276
+ cleanup: "clear after saved handoff",
15277
+ description: "Drive delivery through PR readiness, CI, merge, handoff, and worker clear receipts.",
15278
+ disallowedActions: [
15279
+ "Do not open PRs before repo.open_pr is permitted.",
15280
+ "Do not merge before repo.merge_green_pr is permitted and CI is green.",
15281
+ "Do not clear before a saved handoff.",
15282
+ ],
15283
+ displayName: "PR/CI/Merge Ralph Loop",
15284
+ epilogues: ["draft-pr", "record-handoff"],
15285
+ evidenceGates: ["pr_url", "ci_green", "merge", "adversarial_check"],
15286
+ guidelines: ["Merge only after green CI and recorded manager decision evidence."],
15287
+ loopTemplate: "pr_ci_merge_loop",
15288
+ mode: "strict",
15289
+ name: "pr-ci-merge-ralph-loop",
15290
+ objective: "Drive the worker through PR readiness, CI, merge, handoff, and clear receipts.",
15291
+ permissions: ["repo.open_pr", "repo.merge_green_pr", "worker_session.compact", "worker_session.clear"],
15292
+ supportPatterns: ["Inbox / No-Tmux App Loop", "Recovery / Resume / Handoff"],
15293
+ tools: ["verification.run_tests", "context.fetch_prs"],
15294
+ },
15295
+ "test-coverage-loop": {
15296
+ acceptance: [
15297
+ "Coverage or targeted test evidence is recorded before another worker pass.",
15298
+ "Structured adversarial proof names the strongest realistic failure mode.",
15299
+ ],
15300
+ cleanup: "clear by default",
15301
+ description: "Improve or prove test confidence with coverage evidence before another pass.",
15302
+ disallowedActions: ["Do not continue after only generic tests-passed text."],
15303
+ displayName: "Test Coverage Loop",
15304
+ epilogues: [],
15305
+ evidenceGates: ["test_coverage", "adversarial_check"],
15306
+ guidelines: ["Record coverage evidence before asking for another worker pass."],
15307
+ loopTemplate: "test_coverage_loop",
15308
+ mode: "strict",
15309
+ name: "test-coverage-loop",
15310
+ objective: "Improve or prove test coverage for the requested behavior.",
15311
+ permissions: ["worker_session.compact", "worker_session.clear"],
15312
+ supportPatterns: ["Inbox / No-Tmux App Loop", "Recovery / Resume / Handoff"],
15313
+ tools: ["verification.run_tests"],
15314
+ },
15315
+ "ux-polish-loop": {
15316
+ acceptance: [
15317
+ "Reference artifact, candidate screenshot, visual diff report, and below-threshold evidence are recorded.",
15318
+ "Structured adversarial proof is recorded before another visual pass.",
15319
+ ],
15320
+ cleanup: "compact by default",
15321
+ description: "Iterate on visible UI quality using browser, screenshot, and visual-diff evidence.",
15322
+ disallowedActions: ["Do not approve a visual pass without screenshot or browser evidence."],
15323
+ displayName: "UX Polish Loop",
15324
+ epilogues: [],
15325
+ evidenceGates: [
15326
+ "reference_artifact",
15327
+ "candidate_screenshot",
15328
+ "visual_diff_report",
15329
+ "diff_below_threshold",
15330
+ "adversarial_check",
15331
+ ],
15332
+ guidelines: ["Compare visible output against references before requesting another pass."],
15333
+ loopTemplate: "visual_diff_loop",
15334
+ mode: "guided",
15335
+ name: "ux-polish-loop",
15336
+ objective: "Iterate on visible UI quality using browser and screenshot evidence.",
15337
+ permissions: ["worker_session.compact", "worker_session.clear"],
15338
+ supportPatterns: ["Inbox / No-Tmux App Loop", "Recovery / Resume / Handoff"],
15339
+ tools: ["verification.run_playwright"],
15340
+ },
15341
+ };
15342
+ const MANAGER_RECIPE_ALIASES = {
15343
+ "goalbuddy conveyor": "goalbuddy-conveyor",
15344
+ goalbuddy: "goalbuddy-conveyor",
15345
+ "nudge / what's next manager": "nudge-whats-next",
15346
+ "nudge whats next": "nudge-whats-next",
15347
+ "pr ci merge ralph loop": "pr-ci-merge-ralph-loop",
15348
+ "pr/ci/merge ralph loop": "pr-ci-merge-ralph-loop",
15349
+ "ralph loop": "pr-ci-merge-ralph-loop",
15350
+ "test coverage": "test-coverage-loop",
15351
+ "test coverage loop": "test-coverage-loop",
15352
+ "ux polish": "ux-polish-loop",
15353
+ "ux polish loop": "ux-polish-loop",
15354
+ "visual polish": "ux-polish-loop",
15355
+ "what's next": "nudge-whats-next",
15356
+ "whats next": "nudge-whats-next",
15357
+ };
15358
+ function listManagerRecipes() {
15359
+ return Object.keys(MANAGER_RECIPES).sort().map((name) => managerRecipeSummary(name));
15360
+ }
15361
+ function managerRecipeDefinition(name) {
15362
+ const key = normalizeManagerRecipeName(name);
15363
+ const recipe = MANAGER_RECIPES[key];
15364
+ if (!recipe) {
15365
+ throw new Error(`Unknown manager recipe: ${name}; expected one of: ${Object.keys(MANAGER_RECIPES).sort().join(", ")}`);
15366
+ }
15367
+ return recipe;
15368
+ }
15369
+ function normalizeManagerRecipeName(name) {
15370
+ const normalized = name.trim().toLowerCase().split(/\s+/).join(" ");
15371
+ return MANAGER_RECIPE_ALIASES[normalized] ?? normalized.replace(/_/g, "-").replace(/ /g, "-");
15372
+ }
15373
+ function managerRecipeSummary(name) {
15374
+ const recipe = managerRecipeDefinition(name);
15375
+ return {
15376
+ acceptance: [...recipe.acceptance],
15377
+ cleanup: recipe.cleanup,
15378
+ description: recipe.description,
15379
+ disallowed_actions: [...recipe.disallowedActions],
15380
+ display_name: recipe.displayName,
15381
+ epilogues: [...recipe.epilogues],
15382
+ evidence_gates: [...recipe.evidenceGates],
15383
+ guidelines: [...recipe.guidelines],
15384
+ locked_summary_template: lockedManagerRecipeSummary(recipe),
15385
+ loop_template: recipe.loopTemplate,
15386
+ manager_config_command: managerRecipeConfigCommand(recipe),
15387
+ mode: recipe.mode,
15388
+ name: recipe.name,
15389
+ objective: recipe.objective,
15390
+ permissions: [...recipe.permissions],
15391
+ support_patterns: [...recipe.supportPatterns],
15392
+ tools: [...recipe.tools],
15393
+ };
15394
+ }
15395
+ function managerRecipeConfigCommand(recipe, taskPlaceholder = "<task>") {
15396
+ const command = ["conveyor", "manager-config", taskPlaceholder, "--mode", recipe.mode, "--objective", recipe.objective];
15397
+ for (const guideline of recipe.guidelines) {
15398
+ command.push("--guideline", guideline);
15399
+ }
15400
+ for (const acceptance of recipe.acceptance) {
15401
+ command.push("--acceptance", acceptance);
15402
+ }
15403
+ const permissions = new Set(recipe.permissions);
15404
+ if (permissions.has("worker_session.compact") && permissions.has("worker_session.clear")) {
15405
+ command.push("--allow-worker-compact-clear");
15406
+ permissions.delete("worker_session.compact");
15407
+ permissions.delete("worker_session.clear");
15408
+ }
15409
+ if (permissions.has("repo.open_pr")) {
15410
+ command.push("--allow-pr");
15411
+ permissions.delete("repo.open_pr");
15412
+ }
15413
+ if (permissions.has("repo.merge_green_pr")) {
15414
+ command.push("--allow-merge-green");
15415
+ permissions.delete("repo.merge_green_pr");
15416
+ }
15417
+ for (const permission of [...permissions].sort()) {
15418
+ command.push("--permit", permission);
15419
+ }
15420
+ for (const tool of recipe.tools) {
15421
+ command.push("--tool", tool);
15422
+ }
15423
+ for (const epilogue of recipe.epilogues) {
15424
+ command.push("--epilogue", epilogue);
15425
+ }
15426
+ return command;
15427
+ }
15428
+ function lockedManagerRecipeSummary(recipe) {
15429
+ return [
15430
+ `Selected recipe: ${recipe.displayName}`,
15431
+ `Mode: ${recipe.mode}`,
15432
+ `Permissions: ${recipe.permissions.length > 0 ? recipe.permissions.join(", ") : "none"}`,
15433
+ `Tools: ${recipe.tools.length > 0 ? recipe.tools.join(", ") : "none"}`,
15434
+ `Epilogues: ${recipe.epilogues.length > 0 ? recipe.epilogues.join(", ") : "none"}`,
15435
+ `Cleanup: ${recipe.cleanup}`,
15436
+ `Evidence gates: ${recipe.evidenceGates.length > 0 ? recipe.evidenceGates.join(", ") : "manager-reviewed evidence"}`,
15437
+ `Not allowed: ${recipe.disallowedActions.length > 0 ? recipe.disallowedActions.join("; ") : "unconfirmed custom actions"}`,
15438
+ "User confirmed: <yes|no>",
15439
+ ].join("\n");
15440
+ }
15173
15441
  const LOOP_TRIGGERS = [
15174
15442
  {
15175
15443
  acceptance: "Create or reuse a loop policy whose required_before_continue includes adversarial_check.",