agent-conveyor 0.1.16 → 0.1.18

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
@@ -324,7 +324,14 @@ tmux attach -t codex-live-test
324
324
  asset receipt counts.
325
325
  - `campaign dashboard --name C [--json]` —
326
326
  Show a manager-oriented campaign aggregate: worker slot lifecycle states,
327
- blockers, approval counts, and the next recommended manager action.
327
+ blockers, approval counts, and the next recommended manager action. This is
328
+ also the supported way to inspect asset receipts and per-slot receipt counts;
329
+ there is no separate `campaign assets` subcommand.
330
+ - `campaign closeout --name C [--failure-mode TEXT] [--json]` —
331
+ Produce a read-only closeout report from the campaign dashboard, including
332
+ verdict, worker thread ids, blockers, receipt counts by assignment, proof
333
+ checks, and the strongest realistic failure mode evidence to carry into a
334
+ manager/operator handoff.
328
335
 
329
336
  Creative Ops Campaign manager loop:
330
337
 
@@ -608,6 +615,9 @@ stay out of receipts.
608
615
  permissions, expected tools, epilogues, evidence gates, cleanup behavior,
609
616
  disallowed actions, locked setup summary template, and suggested
610
617
  `manager-config` command. Use this before cutting a manager loose.
618
+ `campaign-duplicate-guard-dogfood` is the recipe for visible creative
619
+ campaign dogfoods that prove accidental duplicate assignment receipts are
620
+ rejected unless a worker explicitly uses `--allow-additional-receipt`.
611
621
  - `worker-ack <task> --from-stdin|--json [--correlation-id ID]` /
612
622
  `manager-ack <task> --from-stdin|--json [--correlation-id ID]` — Persist or
613
623
  read the latest structured acknowledgement from the worker or manager. Acks
@@ -384,7 +384,11 @@ function commandHelpText(program, command) {
384
384
  ` ${program} finish-task my-task --reason "Accepted criteria satisfied" --require-criteria-audit --path /tmp/work/workerctl.db --json`,
385
385
  ],
386
386
  campaign: [
387
- `usage: ${program} campaign <create|add-slot|attach-slot|rotate-slot|archive-slot|brief|assign|asset|status|dashboard> --name <campaign> [options] ${path} [--json]`,
387
+ `usage: ${program} campaign <${campaignActionsUsage()}> --name <campaign> [options] ${path} [--json]`,
388
+ "",
389
+ `Supported subcommands: ${campaignActionsUsage()}`,
390
+ "",
391
+ "Use `dashboard` to list campaign assets and slot receipt counts; there is no separate `assets` subcommand.",
388
392
  "",
389
393
  "Examples:",
390
394
  ` ${program} campaign create --name launch --objective "Create launch assets" --metadata-json '{"owner":"ops"}' --json`,
@@ -398,6 +402,7 @@ function commandHelpText(program, command) {
398
402
  ` ${program} campaign asset --name launch --slot campaign-slot-id --assignment campaign-assignment-id --asset-type copy --title "Hooks v2" --allow-additional-receipt --json`,
399
403
  ` ${program} campaign status --name launch --json`,
400
404
  ` ${program} campaign dashboard --name launch --json`,
405
+ ` ${program} campaign closeout --name launch --failure-mode "hidden duplicate receipt" --json`,
401
406
  ],
402
407
  "manager-ack": [
403
408
  `usage: ${program} manager-ack <task> --from-stdin ${path}`,
@@ -472,7 +477,8 @@ function commandHelpText(program, command) {
472
477
  };
473
478
  return linesByCommand[command] ?? [`usage: ${program} ${command} [-h] [options]`];
474
479
  }
475
- const CAMPAIGN_ACTIONS = new Set(["create", "add-slot", "attach-slot", "rotate-slot", "archive-slot", "brief", "assign", "asset", "status", "dashboard"]);
480
+ const CAMPAIGN_ACTION_NAMES = ["create", "add-slot", "attach-slot", "rotate-slot", "archive-slot", "brief", "assign", "asset", "status", "dashboard", "closeout"];
481
+ const CAMPAIGN_ACTIONS = new Set(CAMPAIGN_ACTION_NAMES);
476
482
  const CAMPAIGN_STRING_FLAGS = {
477
483
  "--artifact-path": "artifactPath",
478
484
  "--asset-type": "assetType",
@@ -480,6 +486,7 @@ const CAMPAIGN_STRING_FLAGS = {
480
486
  "--brief-json": "briefJson",
481
487
  "--channel": "channel",
482
488
  "--expected-thread-id": "expectedThreadId",
489
+ "--failure-mode": "failureMode",
483
490
  "--instructions": "instructions",
484
491
  "--objective": "objective",
485
492
  "--prompt-summary": "promptSummary",
@@ -3022,7 +3029,7 @@ function parseRuntimeArgs(args, env) {
3022
3029
  }
3023
3030
  else if (command === "campaign" && flags.action === null) {
3024
3031
  if (!CAMPAIGN_ACTIONS.has(arg)) {
3025
- return { command, enabled, error: `Unsupported campaign action: ${arg}`, explicit, flags, task };
3032
+ return { command, enabled, error: unsupportedCampaignActionMessage(arg), explicit, flags, task };
3026
3033
  }
3027
3034
  flags.action = arg;
3028
3035
  }
@@ -6253,12 +6260,25 @@ function runCampaignCommand(parsed, options) {
6253
6260
  const dashboard = campaignDashboardSync(database, campaign);
6254
6261
  return campaignResult(parsed, dashboard, renderCampaignDashboardText(dashboard));
6255
6262
  }
6256
- return errorResult(`Unsupported campaign action: ${action}`);
6263
+ if (action === "closeout") {
6264
+ const dashboard = campaignDashboardSync(database, campaign);
6265
+ const closeout = campaignCloseoutReport(dashboard, {
6266
+ failureMode: parsed.flags.failureMode,
6267
+ });
6268
+ return campaignResult(parsed, closeout, renderCampaignCloseoutText(closeout));
6269
+ }
6270
+ return errorResult(unsupportedCampaignActionMessage(action));
6257
6271
  }
6258
6272
  finally {
6259
6273
  database.close();
6260
6274
  }
6261
6275
  }
6276
+ function campaignActionsUsage() {
6277
+ return CAMPAIGN_ACTION_NAMES.join("|");
6278
+ }
6279
+ function unsupportedCampaignActionMessage(action) {
6280
+ return `Unsupported campaign action: ${action ?? "<missing>"}; expected one of: ${CAMPAIGN_ACTION_NAMES.join(", ")}. Use \`conveyor campaign dashboard --name <campaign> --json\` to list assets and receipt counts.`;
6281
+ }
6262
6282
  function campaignResult(parsed, payload, lines) {
6263
6283
  return parsed.flags.json ? jsonResult(payload) : textResult(lines);
6264
6284
  }
@@ -6331,6 +6351,107 @@ function renderCampaignDashboardText(dashboard) {
6331
6351
  }
6332
6352
  return lines;
6333
6353
  }
6354
+ function campaignCloseoutReport(dashboard, options = {}) {
6355
+ const receiptCountsByAssignment = campaignReceiptCountsByAssignment(dashboard);
6356
+ const activeSlots = dashboard.slots.filter((slot) => slot.state !== "archived");
6357
+ const slotsMissingReceipts = activeSlots.filter((slot) => slot.assignments.length > 0 && slot.asset_receipts === 0);
6358
+ const duplicateAssignmentCounts = receiptCountsByAssignment.filter((item) => item.receipt_count > 1);
6359
+ const failureMode = options.failureMode
6360
+ ?? "A hidden duplicate or missing worker receipt could make the campaign look closed while dashboard receipt counts are wrong.";
6361
+ const receiptEvidence = dashboard.slots
6362
+ .map((slot) => `${slot.slot_key}:assignments=${slot.assignments.length},receipts=${slot.asset_receipts}`)
6363
+ .join("; ");
6364
+ return {
6365
+ action: "closeout",
6366
+ approvals: dashboard.approvals,
6367
+ blockers: dashboard.blockers,
6368
+ campaign: dashboard.campaign,
6369
+ failure_mode: {
6370
+ evidence: `dashboard asset_total=${dashboard.summary.asset_total}; assignment_total=${dashboard.summary.assignment_total}; ${receiptEvidence}`,
6371
+ strongest_realistic_failure_mode: failureMode,
6372
+ },
6373
+ next_manager_action: dashboard.next_manager_action,
6374
+ proof_checks: [
6375
+ {
6376
+ check: "dashboard_loaded",
6377
+ evidence: `campaign_id=${dashboard.campaign.id}; updated_at=${dashboard.campaign.updated_at}`,
6378
+ status: "passed",
6379
+ },
6380
+ {
6381
+ check: "blockers_absent",
6382
+ evidence: `blockers=${dashboard.blockers.length}`,
6383
+ status: dashboard.blockers.length === 0 ? "passed" : "failed",
6384
+ },
6385
+ {
6386
+ check: "active_worker_slots_have_receipts",
6387
+ evidence: slotsMissingReceipts.length === 0
6388
+ ? "all active slots with assignments have at least one receipt"
6389
+ : `missing_receipt_slots=${slotsMissingReceipts.map((slot) => slot.slot_key).join(",")}`,
6390
+ status: slotsMissingReceipts.length === 0 ? "passed" : "attention",
6391
+ },
6392
+ {
6393
+ check: "assignment_receipt_counts",
6394
+ evidence: duplicateAssignmentCounts.length === 0
6395
+ ? "no assignment has more than one receipt"
6396
+ : `additional_receipt_assignments=${duplicateAssignmentCounts.map((item) => `${item.assignment_id}:${item.receipt_count}`).join(",")}`,
6397
+ status: duplicateAssignmentCounts.length === 0 ? "passed" : "attention",
6398
+ },
6399
+ {
6400
+ check: "human_review_gate",
6401
+ evidence: `needs_review=${dashboard.approvals.needs_review}; approved=${dashboard.approvals.approved}; published=${dashboard.approvals.published}`,
6402
+ status: dashboard.approvals.needs_review > 0 && dashboard.approvals.published === 0 ? "passed" : "attention",
6403
+ },
6404
+ ],
6405
+ receipt_counts_by_assignment: receiptCountsByAssignment,
6406
+ summary: dashboard.summary,
6407
+ verdict: campaignCloseoutVerdict(dashboard),
6408
+ workers: dashboard.slots.map((slot) => ({
6409
+ active_assignments: slot.active_assignments,
6410
+ asset_receipts: slot.asset_receipts,
6411
+ blockers: slot.blockers,
6412
+ channel: slot.channel,
6413
+ codex_app_thread_id: slot.codex_app_thread_id,
6414
+ codex_app_thread_title: slot.codex_app_thread_title,
6415
+ lifecycle_state: slot.lifecycle.state,
6416
+ receipt_ids: slot.assets.map((asset) => asset.id),
6417
+ slot_key: slot.slot_key,
6418
+ state: slot.state,
6419
+ })),
6420
+ };
6421
+ }
6422
+ function campaignReceiptCountsByAssignment(dashboard) {
6423
+ return dashboard.slots.flatMap((slot) => slot.assignments.map((assignment) => ({
6424
+ assignment_id: assignment.id,
6425
+ receipt_count: slot.assets.filter((asset) => asset.assignment_id === assignment.id).length,
6426
+ slot_key: slot.slot_key,
6427
+ })));
6428
+ }
6429
+ function campaignCloseoutVerdict(dashboard) {
6430
+ if (dashboard.blockers.length > 0 || dashboard.summary.blocked_assignments > 0 || dashboard.summary.blocked_slots > 0 || dashboard.summary.stale_slots > 0) {
6431
+ return "blocked";
6432
+ }
6433
+ if (dashboard.next_manager_action.action === "close_campaign") {
6434
+ return "ready_to_close";
6435
+ }
6436
+ if (dashboard.approvals.needs_review > 0 || dashboard.approvals.rejected > 0) {
6437
+ return "needs_review";
6438
+ }
6439
+ return "needs_work";
6440
+ }
6441
+ function renderCampaignCloseoutText(report) {
6442
+ return [
6443
+ `campaign ${report.campaign.name} ${report.campaign.status}`,
6444
+ `closeout verdict ${report.verdict}`,
6445
+ `next ${report.next_manager_action.action}: ${report.next_manager_action.reason}`,
6446
+ `summary slots=${report.summary.active_slots}/${report.summary.archived_slots} assignments=${report.summary.assignment_total} assets=${report.summary.asset_total} blockers=${report.blockers.length}`,
6447
+ `approvals needs_review=${report.approvals.needs_review} approved=${report.approvals.approved} rejected=${report.approvals.rejected} published=${report.approvals.published}`,
6448
+ `failure_mode ${report.failure_mode.strongest_realistic_failure_mode}`,
6449
+ `failure_mode_evidence ${report.failure_mode.evidence}`,
6450
+ ...report.proof_checks.map((check) => `proof ${check.status} ${check.check}: ${check.evidence}`),
6451
+ ...report.receipt_counts_by_assignment.map((item) => `assignment_receipts ${item.slot_key} ${item.assignment_id}=${item.receipt_count}`),
6452
+ ...report.workers.slice(0, 8).map((worker) => `worker ${worker.slot_key} ${worker.state}/${worker.lifecycle_state} assignments=${worker.active_assignments} receipts=${worker.asset_receipts} thread=${worker.codex_app_thread_id ?? "none"}`),
6453
+ ];
6454
+ }
6334
6455
  function statusCountsText(counts) {
6335
6456
  return Object.entries(counts).map(([status, count]) => `${status}=${count}`).join(" ");
6336
6457
  }
@@ -17212,6 +17333,45 @@ const MANAGER_RECIPES = {
17212
17333
  supportPatterns: ["Inbox / No-Tmux App Loop", "Recovery / Resume / Handoff"],
17213
17334
  tools: ["verification.run_tests", "context.fetch_prs"],
17214
17335
  },
17336
+ "campaign-duplicate-guard-dogfood": {
17337
+ acceptance: [
17338
+ "The campaign dashboard shows exactly one normal asset receipt for each active assignment before the duplicate probe.",
17339
+ "A worker visibly attempts an accidental duplicate `campaign asset` receipt without --allow-additional-receipt and records the expected non-zero failure.",
17340
+ "Manager independently verifies the post-probe dashboard still has the original asset_total, the probed slot still has one receipt, and blockers are empty.",
17341
+ ],
17342
+ cleanup: "off by default; archive or rotate only campaign-owned worker slots with exact expected thread ids",
17343
+ description: "Dogfood creative campaign workers while proving assignment-scoped duplicate receipts are blocked unless explicitly allowed.",
17344
+ disallowedActions: [
17345
+ "Do not use --allow-additional-receipt for the accidental duplicate probe.",
17346
+ "Do not treat the worker's duplicate-failure claim as proof until the manager verifies the dashboard.",
17347
+ "Do not publish, schedule, contact external services, inspect private content, edit product/content files, or commit during the dogfood.",
17348
+ "Do not archive or rotate a worker thread unless the campaign slot owns that exact thread id.",
17349
+ ],
17350
+ displayName: "Campaign Duplicate-Guard Dogfood",
17351
+ epilogues: [],
17352
+ evidenceGates: [
17353
+ "campaign_dashboard_pre_probe",
17354
+ "visible_worker_duplicate_failure",
17355
+ "duplicate_failure_exit_code",
17356
+ "post_probe_dashboard_no_extra_asset",
17357
+ "manager_duplicate_guard_decision",
17358
+ ],
17359
+ finalReportRequirements: [
17360
+ "Report the manager thread id, worker thread ids, assignment ids, original receipt ids, duplicate error text, pre/post dashboard counts, and residual cleanup status.",
17361
+ ],
17362
+ guidelines: [
17363
+ "Keep manager and worker sessions visibly chatty with CONVEYOR RECEIVED, WORK, and CONVEYOR SEND sections.",
17364
+ "Use `campaign dashboard --name <campaign> --json` as the supported receipt-listing surface.",
17365
+ "Ask exactly one worker to perform the missing-override duplicate probe, then require manager-side dashboard verification before closeout.",
17366
+ ],
17367
+ loopTemplate: null,
17368
+ mode: "strict",
17369
+ name: "campaign-duplicate-guard-dogfood",
17370
+ objective: "Supervise a visible campaign dogfood that proves duplicate assignment receipts fail closed without --allow-additional-receipt.",
17371
+ permissions: [],
17372
+ supportPatterns: ["Creative Ops Campaign", "Inbox / No-Tmux App Loop", "Recovery / Resume / Handoff"],
17373
+ tools: ["campaign.dashboard", "codex_app.send_message_to_thread"],
17374
+ },
17215
17375
  "nudge-whats-next": {
17216
17376
  acceptance: [
17217
17377
  "Accepted criteria are satisfied or explicitly deferred.",
@@ -17370,6 +17530,10 @@ const MANAGER_RECIPES = {
17370
17530
  },
17371
17531
  };
17372
17532
  const MANAGER_RECIPE_ALIASES = {
17533
+ "campaign duplicate guard": "campaign-duplicate-guard-dogfood",
17534
+ "campaign duplicate guard dogfood": "campaign-duplicate-guard-dogfood",
17535
+ "creative duplicate guard": "campaign-duplicate-guard-dogfood",
17536
+ "duplicate guard dogfood": "campaign-duplicate-guard-dogfood",
17373
17537
  "goalbuddy conveyor": "goalbuddy-conveyor",
17374
17538
  goalbuddy: "goalbuddy-conveyor",
17375
17539
  "nudge / what's next manager": "nudge-whats-next",