@rallycry/conveyor-agent 10.13.72 → 11.0.0
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/dist/{boot-ZNL7X5LQ.js → boot-PKQ2I66D.js} +49 -2
- package/dist/{chunk-WMMBAKPE.js → chunk-GL2DIQEQ.js} +121 -8
- package/dist/{chunk-2L5THOWD.js → chunk-JQVAWRVL.js} +2 -2
- package/dist/{chunk-LSZ2KLJY.js → chunk-N4WSUTGV.js} +40 -3
- package/dist/{chunk-2SN32LM6.js → chunk-PEEGCZAR.js} +2055 -1035
- package/dist/cli.js +257 -100
- package/dist/index.d.ts +170 -3
- package/dist/index.js +3 -3
- package/dist/{serve-boot-4N3FRXVQ.js → serve-boot-YPAUKENG.js} +3 -3
- package/package.json +5 -4
- package/skills/conveyor-build/SKILL.md +262 -0
- package/skills/conveyor-build/references/pack-path.md +224 -0
- package/skills/conveyor-build/references/task-path.md +67 -0
- package/skills/conveyor-consensus/SKILL.md +99 -0
- package/skills/conveyor-consensus/references/doc-template.html +204 -0
- package/skills/conveyor-local-loop/SKILL.md +258 -0
- package/skills/conveyor-meeting-review/SKILL.md +72 -0
- package/skills/conveyor-plan/SKILL.md +177 -0
- package/skills/conveyor-plan/references/plan-format.md +134 -0
- package/skills/conveyor-review/SKILL.md +161 -0
- package/skills/conveyor-start/SKILL.md +106 -0
- package/skills/conveyor-triage/SKILL.md +174 -0
- package/skills/conveyor-workflows/SKILL.md +195 -0
- package/skills/conveyor-workflows/references/mcp-setup.md +43 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _project_shared from '@project/shared';
|
|
2
|
-
import { RunnerMode, AgentSessionServiceMethods, AgentMode, PtyChatEventPayload, WorkspaceDiscoveredPort, AgentQuestion, AgentRunnerStatus } from '@project/shared';
|
|
2
|
+
import { RunnerMode, AgentSessionServiceMethods, AgentMode, PtyChatEventPayload, WorkspaceDiscoveredPort, AgentQuestion, PackExecution, AgentRunnerStatus } from '@project/shared';
|
|
3
3
|
export * from '@project/shared';
|
|
4
4
|
import { ChildProcess } from 'node:child_process';
|
|
5
5
|
|
|
@@ -56,6 +56,31 @@ interface SpawnReviewData {
|
|
|
56
56
|
prNumber?: number | null;
|
|
57
57
|
checkoutRef?: string | null;
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Server push asking a PLANNER pod to spawn the Builder child: a second
|
|
61
|
+
* conveyor-agent process bound to a fresh `mode:"build"` WorkspaceSession. The
|
|
62
|
+
* lifecycle handoff — a human pressing Build, or a Yolomatic plan landing —
|
|
63
|
+
* without cold-booting a second pod, and without flipping the planner's own
|
|
64
|
+
* session into a builder in place.
|
|
65
|
+
*
|
|
66
|
+
* Same wire discipline as SpawnReviewData: no taskId (the child inherits the
|
|
67
|
+
* parent's CONVEYOR_TASK_ID and the server derives it from the JWT), and the
|
|
68
|
+
* runner mode rides the JWT claim rather than this field — `mode` here is what
|
|
69
|
+
* the supervisor exports as CONVEYOR_MODE for the child process.
|
|
70
|
+
*/
|
|
71
|
+
interface SpawnBuilderData {
|
|
72
|
+
sessionId: string;
|
|
73
|
+
sessionJwt: string;
|
|
74
|
+
/**
|
|
75
|
+
* Runner mode for the child — `pack` when the card has children, so the
|
|
76
|
+
* coordinator gets the single-pod pack prompt and toolset. Optional so a pod
|
|
77
|
+
* running against an older server (which sends none) still boots as `task`.
|
|
78
|
+
*/
|
|
79
|
+
mode?: "task" | "pack";
|
|
80
|
+
branch?: string | null;
|
|
81
|
+
/** The agent whose model/settings/instructions the Builder runs under. */
|
|
82
|
+
agentName?: string | null;
|
|
83
|
+
}
|
|
59
84
|
/**
|
|
60
85
|
* Server push asking the builder pod to spawn an extra interactive child on
|
|
61
86
|
* this pod: a second Claude TUI (mode "adhoc") or a raw login shell (mode
|
|
@@ -98,6 +123,8 @@ declare class AgentConnection {
|
|
|
98
123
|
private earlyPullBranches;
|
|
99
124
|
private spawnReviewCallback;
|
|
100
125
|
private earlySpawnReviews;
|
|
126
|
+
private spawnBuilderCallback;
|
|
127
|
+
private earlySpawnBuilders;
|
|
101
128
|
private spawnTuiCallback;
|
|
102
129
|
private earlySpawnTuis;
|
|
103
130
|
private probeUsageCallback;
|
|
@@ -168,6 +195,9 @@ declare class AgentConnection {
|
|
|
168
195
|
branch: string;
|
|
169
196
|
}) => void): void;
|
|
170
197
|
onSpawnReview(callback: (data: SpawnReviewData) => void): void;
|
|
198
|
+
/** Mirror of onSpawnReview for the Builder handoff. Drains the early buffer
|
|
199
|
+
* so a Build pressed during boot is not lost. */
|
|
200
|
+
onSpawnBuilder(callback: (data: SpawnBuilderData) => void): void;
|
|
171
201
|
/**
|
|
172
202
|
* Report that a same-pod review child failed to spawn (fire-and-forget).
|
|
173
203
|
* The server Ends the orphaned review session and falls back to a dedicated
|
|
@@ -175,6 +205,15 @@ declare class AgentConnection {
|
|
|
175
205
|
* it; the review session is identified separately.
|
|
176
206
|
*/
|
|
177
207
|
reportReviewSpawnFailure(reviewSessionId: string, error?: string): void;
|
|
208
|
+
/**
|
|
209
|
+
* Report that this (planner) pod could not spawn the Builder child, so the
|
|
210
|
+
* server can End the orphaned build session and reopen the card instead of
|
|
211
|
+
* leaving it InProgress with a Builder tab that never appears.
|
|
212
|
+
*
|
|
213
|
+
* sessionId is OUR (planner) session — the task-identity guard runs on it;
|
|
214
|
+
* the build session is identified separately.
|
|
215
|
+
*/
|
|
216
|
+
reportBuilderSpawnFailure(buildSessionId: string, error?: string): void;
|
|
178
217
|
/**
|
|
179
218
|
* Report that this pod's git credential is dead and refreshing did not fix
|
|
180
219
|
* it (fire-and-forget).
|
|
@@ -412,14 +451,66 @@ declare class ModeController {
|
|
|
412
451
|
private _pendingModeRestart;
|
|
413
452
|
private _runnerMode;
|
|
414
453
|
private _isAuto;
|
|
454
|
+
/**
|
|
455
|
+
* Which planner this is. Only read when `_runnerMode` is `plan`; see
|
|
456
|
+
* `effectiveMode` for the full reasoning, and `latchPlanFlavor` for why this
|
|
457
|
+
* latches toward `chat` and never away from it.
|
|
458
|
+
*/
|
|
459
|
+
private _planFlavor;
|
|
415
460
|
constructor(initialMode: AgentMode, runnerMode?: RunnerMode, isAuto?: boolean);
|
|
461
|
+
/**
|
|
462
|
+
* Promote this planner to the chat flavor, once, and never demote it.
|
|
463
|
+
*
|
|
464
|
+
* It cannot be a constructor-only assignment, because the constructor usually
|
|
465
|
+
* has nothing to go on. A GKE pod's bootstrap bundle carries no agentMode, so
|
|
466
|
+
* `CONVEYOR_AGENT_MODE` is unset and `initialMode` falls back to `"building"`
|
|
467
|
+
* for EVERY pod — which is the whole reason `applyServerMode` exists. Pinning
|
|
468
|
+
* the flavor at construction alone would therefore make every chat card in the
|
|
469
|
+
* cloud a discovery planner and silently re-open the defect this replaced.
|
|
470
|
+
*
|
|
471
|
+
* One-way, and both directions matter:
|
|
472
|
+
*
|
|
473
|
+
* - Only the literal card mode `chat` promotes. The server reports `chat` only
|
|
474
|
+
* for a card whose agentMode IS `chat`, so a discovery card can never be
|
|
475
|
+
* talked into the build-capable flavor. The original hazard — `auto` stamped
|
|
476
|
+
* onto a plan-less auto card's planner — is untouched, because `auto` is not
|
|
477
|
+
* `chat`.
|
|
478
|
+
* - Nothing demotes. The Build press overwrites `Task.agentMode` to
|
|
479
|
+
* `auto`/`building` before the server sees it, so a later stamp would
|
|
480
|
+
* otherwise strip a live conversation's prompt and file deliverables
|
|
481
|
+
* mid-turn.
|
|
482
|
+
*
|
|
483
|
+
* The asymmetry with `auto` is principled rather than convenient: an auto
|
|
484
|
+
* card's mode and its planner phase legitimately differ, while a chat card's
|
|
485
|
+
* mode IS what its session is, for the session's whole life.
|
|
486
|
+
*/
|
|
487
|
+
private latchPlanFlavor;
|
|
416
488
|
get mode(): AgentMode;
|
|
417
489
|
get isAuto(): boolean;
|
|
418
490
|
get hasExitedPlanMode(): boolean;
|
|
419
491
|
set hasExitedPlanMode(val: boolean);
|
|
420
492
|
get pendingModeRestart(): boolean;
|
|
421
493
|
set pendingModeRestart(val: boolean);
|
|
422
|
-
/**
|
|
494
|
+
/**
|
|
495
|
+
* Effective mode accounting for PM/task defaults.
|
|
496
|
+
*
|
|
497
|
+
* A `plan` runner is pinned to ONE mode for its whole life, whatever the
|
|
498
|
+
* card's own agentMode later says. That pin is what makes the planner
|
|
499
|
+
* read-only, non-build-capable, and prompted for planning — all three derive
|
|
500
|
+
* from here — and it has to be immune to `applyServerMode`, which runs AFTER
|
|
501
|
+
* boot and would otherwise stamp `auto` onto the planner of a plan-less auto
|
|
502
|
+
* card, handing it the build prompt and `--dangerously-skip-permissions`
|
|
503
|
+
* inside a session whose whole purpose is not to have them.
|
|
504
|
+
*
|
|
505
|
+
* There are TWO planner flavors, and which one this session is was decided at
|
|
506
|
+
* boot (`_planFlavor`) — never re-read from the mutable `_mode`. A chat card
|
|
507
|
+
* boots a plan session too (it is a Planner-session flavor, not a builder in
|
|
508
|
+
* disguise), and it must keep `buildChatPrompt`, its file deliverables, and
|
|
509
|
+
* the build-capable tool handler. Reading the flavor from `_mode` would put
|
|
510
|
+
* that choice back under server control and re-open the exact hole the
|
|
511
|
+
* paragraph above closes, in the other direction: a stamp of `chat` onto a
|
|
512
|
+
* discovery planner would hand it build capability.
|
|
513
|
+
*/
|
|
423
514
|
get effectiveMode(): AgentMode;
|
|
424
515
|
get isReadOnly(): boolean;
|
|
425
516
|
get isAutoPlanning(): boolean;
|
|
@@ -507,7 +598,13 @@ declare class Lifecycle {
|
|
|
507
598
|
stopGitFlush(): void;
|
|
508
599
|
startUsageSample(): void;
|
|
509
600
|
stopUsageSample(): void;
|
|
510
|
-
|
|
601
|
+
/** Start (or restart) the idle timer.
|
|
602
|
+
* @param overrideMs Optional custom delay in ms, mirroring
|
|
603
|
+
* `startDormantTimer`. SessionRunner passes a short delay when it DEFERS a
|
|
604
|
+
* shutdown because a spawned child is still working: the pod must re-check
|
|
605
|
+
* soon after that child exits, rather than granting itself a fresh full idle
|
|
606
|
+
* window every time it defers. */
|
|
607
|
+
startIdleTimer(overrideMs?: number): void;
|
|
511
608
|
cancelIdleTimer(): void;
|
|
512
609
|
/** Start (or restart) the dormant timer.
|
|
513
610
|
* @param overrideMs Optional custom delay in ms. When provided, the timer
|
|
@@ -522,10 +619,43 @@ declare class Lifecycle {
|
|
|
522
619
|
private clearIdleTimers;
|
|
523
620
|
}
|
|
524
621
|
|
|
622
|
+
/**
|
|
623
|
+
* Who else is working on this pod?
|
|
624
|
+
*
|
|
625
|
+
* The Planner is the pod's MAIN process, so its idle/dormant timeouts end the
|
|
626
|
+
* whole pod: `stopped = true` → `shutdown("finished")` → `process.exit(0)` →
|
|
627
|
+
* the supervisor logs "agent exited cleanly, shutting down pod". Nothing used
|
|
628
|
+
* to ask whether a spawned child was still working. A handed-off build that
|
|
629
|
+
* outlived the Planner's idle window therefore died mid-run — the work survived
|
|
630
|
+
* only as far as the last `conveyor-wip` flush, the janitor parked the
|
|
631
|
+
* workspace, and the card reverted to Open.
|
|
632
|
+
*
|
|
633
|
+
* This is the seam that lets `SessionRunner` ask without importing the
|
|
634
|
+
* supervisors: `cli.ts` owns them and hands the runner a probe. Keeping the
|
|
635
|
+
* dependency pointing that way also keeps the runner testable with a fake.
|
|
636
|
+
*
|
|
637
|
+
* Ordering is deliberate — `builder` first — so the deferral log names the
|
|
638
|
+
* session a human is most likely looking for when they ask why a pod is still
|
|
639
|
+
* up.
|
|
640
|
+
*/
|
|
641
|
+
/** A child supervisor, reduced to the one question the shutdown path asks. */
|
|
642
|
+
interface ChildSessionSource {
|
|
643
|
+
/**
|
|
644
|
+
* Names the child kind in the deferral log. `tui` rather than `session` —
|
|
645
|
+
* these come from `session:spawnTui`, but "session session <id>" stutters in
|
|
646
|
+
* the one place this string is read.
|
|
647
|
+
*/
|
|
648
|
+
readonly kind: "builder" | "review" | "tui";
|
|
649
|
+
/** Sessions this supervisor currently hosts. Empty when it hosts none. */
|
|
650
|
+
activeSessionIds(): string[];
|
|
651
|
+
}
|
|
652
|
+
|
|
525
653
|
interface SessionRunnerConfig {
|
|
526
654
|
connection: AgentConnectionConfig;
|
|
527
655
|
agentMode?: AgentMode;
|
|
528
656
|
runnerMode?: RunnerMode;
|
|
657
|
+
/** Only read when `runnerMode` is "pack". Defaults to single-pod. */
|
|
658
|
+
packExecution?: PackExecution;
|
|
529
659
|
isAuto?: boolean;
|
|
530
660
|
workspaceDir: string;
|
|
531
661
|
model?: string;
|
|
@@ -601,6 +731,33 @@ declare class SessionRunner {
|
|
|
601
731
|
private agentLiveReported;
|
|
602
732
|
constructor(config: SessionRunnerConfig, callbacks: SessionRunnerCallbacks, deps?: SessionRunnerDependencies);
|
|
603
733
|
get state(): AgentRunnerStatus;
|
|
734
|
+
/**
|
|
735
|
+
* Supervisors whose live children keep this pod alive. Empty until `cli.ts`
|
|
736
|
+
* wires them, which is deliberate: the supervisors are constructed AFTER the
|
|
737
|
+
* runner, and an empty list simply means "no children to protect" — the
|
|
738
|
+
* pre-existing shutdown behavior.
|
|
739
|
+
*/
|
|
740
|
+
private liveChildSources;
|
|
741
|
+
/** Called by `cli.ts` once the child supervisors exist. */
|
|
742
|
+
setLiveChildSources(sources: readonly ChildSessionSource[]): void;
|
|
743
|
+
/**
|
|
744
|
+
* Suppress an idle/dormant shutdown while a spawned child is still working.
|
|
745
|
+
*
|
|
746
|
+
* The Planner is the pod's main process, so its timeouts take the pod — and
|
|
747
|
+
* the Builder's in-flight work — down with it. Returns true when the shutdown
|
|
748
|
+
* was deferred and the caller must not proceed.
|
|
749
|
+
*
|
|
750
|
+
* Re-arms with a SHORT delay rather than a fresh full window: the pod should
|
|
751
|
+
* converge on shutdown soon after the last child exits, not one more idle
|
|
752
|
+
* window later. Bounded by the configured timeout so a test with a 50ms idle
|
|
753
|
+
* window re-checks in 50ms rather than a minute.
|
|
754
|
+
*
|
|
755
|
+
* Fail-open by construction — if the probe throws, or no sources are wired,
|
|
756
|
+
* the shutdown proceeds exactly as before. The opposite bias (a pod that
|
|
757
|
+
* cannot die) is the more expensive mistake here only in money; killing a
|
|
758
|
+
* live build costs work.
|
|
759
|
+
*/
|
|
760
|
+
private deferShutdownForLiveChild;
|
|
604
761
|
get sessionId(): string;
|
|
605
762
|
get isStopped(): boolean;
|
|
606
763
|
/** Wire the boot supervisor handle post-construction — cli.ts constructs it
|
|
@@ -815,6 +972,16 @@ interface AgentRunnerConfig {
|
|
|
815
972
|
mode?: _project_shared.RunnerMode;
|
|
816
973
|
isAuto?: boolean;
|
|
817
974
|
agentSettings?: _project_shared.AgentSettings;
|
|
975
|
+
/**
|
|
976
|
+
* How a `mode: "pack"` session executes its children.
|
|
977
|
+
*
|
|
978
|
+
* "single-pod" (the default): this session implements every child itself,
|
|
979
|
+
* serially. "fan-out": the legacy coordinator — it fires one pod per child
|
|
980
|
+
* and writes no code. Carried as a sub-flag rather than a new `RunnerMode`
|
|
981
|
+
* so `resolveModeEnv`, the JWT mode claim, and every `mode === "pack"`
|
|
982
|
+
* reader keep working unchanged.
|
|
983
|
+
*/
|
|
984
|
+
packExecution?: _project_shared.PackExecution;
|
|
818
985
|
}
|
|
819
986
|
interface AgentRunnerCallbacks {
|
|
820
987
|
onEvent: (event: Record<string, unknown>) => void | Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SessionRunner,
|
|
3
3
|
unshallowRepo
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-PEEGCZAR.js";
|
|
5
5
|
import "./chunk-XORJ6SII.js";
|
|
6
6
|
import {
|
|
7
7
|
AgentConnection,
|
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
stageAndCommit,
|
|
17
17
|
updateRemoteToken,
|
|
18
18
|
workspacePathExists
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import "./chunk-
|
|
19
|
+
} from "./chunk-N4WSUTGV.js";
|
|
20
|
+
import "./chunk-GL2DIQEQ.js";
|
|
21
21
|
import "./chunk-IA45XHOA.js";
|
|
22
22
|
import {
|
|
23
23
|
getWorkbenchClient
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
WorkspaceCommandSupervisor
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JQVAWRVL.js";
|
|
4
4
|
import {
|
|
5
5
|
AgentConnection,
|
|
6
6
|
CodespacePortVisibility,
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
createServiceLogger,
|
|
12
12
|
ensureOnTaskBranch,
|
|
13
13
|
loadConveyorConfig
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-N4WSUTGV.js";
|
|
15
|
+
import "./chunk-GL2DIQEQ.js";
|
|
16
16
|
import "./chunk-IA45XHOA.js";
|
|
17
17
|
import "./chunk-EXQ6AHOY.js";
|
|
18
18
|
import "./chunk-KMB3BU4S.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rallycry/conveyor-agent",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.0",
|
|
4
4
|
"description": "Conveyor Agent Runner v10 - PTY harness for the task chat (SDK harness for audit/project-chat). Agent-as-User architecture with BaseService patterns. Works locally too.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
17
|
"dist",
|
|
18
|
-
"runtime"
|
|
18
|
+
"runtime",
|
|
19
|
+
"skills"
|
|
19
20
|
],
|
|
20
21
|
"type": "module",
|
|
21
22
|
"main": "./dist/index.js",
|
|
@@ -36,8 +37,8 @@
|
|
|
36
37
|
"typecheck": "tsgo --noEmit"
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
|
-
"@anthropic-ai/claude-agent-sdk": "^0.3.
|
|
40
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
40
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.246",
|
|
41
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
41
42
|
"node-pty": "^1.0.0",
|
|
42
43
|
"socket.io-client": "^4.8.3",
|
|
43
44
|
"tar": "^7.5.21",
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: conveyor-build
|
|
3
|
+
description: Execute a planned Conveyor card to a pull request — one task or a whole feature-branch pack — then stay wake-able for review feedback and CI. Use when the user says "/conveyor-build <card>", "build this card", "implement this card", "run this pack", or when a claudespace session is told to follow its card's plan. Works identically in a cloud pod and in a local checkout; the card is the spec and task chat is the log. For producing the plan in the first place use conveyor-plan; to work a whole queue of Open cards use conveyor-local-loop.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Conveyor Build
|
|
7
|
+
|
|
8
|
+
Take a card that already has a plan and drive it to a pull request, then stay
|
|
9
|
+
reachable for review feedback and CI. The card IS the spec, task chat is the
|
|
10
|
+
log, and the host repo's CLAUDE.md governs gates, verification, and PR
|
|
11
|
+
mechanics. This skill adds routing, claiming, and the pack chain.
|
|
12
|
+
|
|
13
|
+
**One skill, two environments.** Everything below applies in a cloud pod and in
|
|
14
|
+
a local checkout alike. Where they genuinely differ, the difference is called
|
|
15
|
+
out in an **Environment** note — never assume; check which one you are in.
|
|
16
|
+
|
|
17
|
+
## Ground rules
|
|
18
|
+
|
|
19
|
+
- **Conveyor is the state store.** A build spans hours or days and the session
|
|
20
|
+
gets compacted. Re-derive state from `mcp__conveyor__get_task` +
|
|
21
|
+
`mcp__conveyor__list_subtasks` at every decision point, never from
|
|
22
|
+
conversation memory.
|
|
23
|
+
- **All Conveyor tools fully-qualified** — `mcp__conveyor__get_task`, not
|
|
24
|
+
`get_task`. Bare names fail.
|
|
25
|
+
- **The card must stand alone.** If you find yourself relying on session
|
|
26
|
+
memory or on this conversation, stop and re-read the card. A plan that fails
|
|
27
|
+
the context-free-reader bar is a blocker, not something to wing: post what is
|
|
28
|
+
missing to chat and stop.
|
|
29
|
+
- **Never approve or merge your own PR into `dev`.** Finish line = the card in
|
|
30
|
+
ReviewPR with CI started. The exception is narrow and lives in the pack path:
|
|
31
|
+
you ARE the reviewer of record for a pack's CHILD PRs into the pack branch,
|
|
32
|
+
because the automated reviewer skips those.
|
|
33
|
+
- **Never boot another environment for work you are doing.** Locally that
|
|
34
|
+
means never `mcp__conveyor__start_task` (it exists only on the local/MCP
|
|
35
|
+
surface, and it spawns a cloud pod that duplicates you). In a pod driving a
|
|
36
|
+
pack it means never `mcp__conveyor__start_child_cloud_build` /
|
|
37
|
+
`mcp__conveyor__stop_child_build` — you implement the children yourself,
|
|
38
|
+
serially. Parallel fan-out is a deliberate choice the user makes by pressing
|
|
39
|
+
Build on the parent, not something a build session opts into.
|
|
40
|
+
|
|
41
|
+
## Environment
|
|
42
|
+
|
|
43
|
+
Resolve which one you are in before touching git or the filesystem.
|
|
44
|
+
`mcp__conveyor__get_connection_context` answers it: a pod session reports a
|
|
45
|
+
task binding and no user account.
|
|
46
|
+
|
|
47
|
+
**In a pod:**
|
|
48
|
+
|
|
49
|
+
- Uncommitted work is auto-snapshotted to `conveyor-wip/<branch>` after every
|
|
50
|
+
turn, so a sleep or crash does not lose the tree. Commit and push anyway as
|
|
51
|
+
soon as a chunk is coherent — committed work never depends on that mechanism.
|
|
52
|
+
- `mcp__conveyor__create_pull_request` stages, commits, and pushes for you.
|
|
53
|
+
- Capture UI evidence with the repo's own harness (in this repo,
|
|
54
|
+
`scripts/agent/shot.ts`) and attach with `mcp__conveyor__upload_attachment`
|
|
55
|
+
before opening the PR.
|
|
56
|
+
- The database and dev server belong to this pod alone — experiment freely.
|
|
57
|
+
|
|
58
|
+
**Locally:**
|
|
59
|
+
|
|
60
|
+
- There is NO WIP autosync. Committed-and-pushed is the only durable state:
|
|
61
|
+
push the branch (`-u origin`) the moment it exists, and always before a long
|
|
62
|
+
background job or a wait.
|
|
63
|
+
- `mcp__conveyor__create_pull_request` opens a PR for a branch you have ALREADY
|
|
64
|
+
pushed. Push first.
|
|
65
|
+
- Work in the main checkout, **never a git worktree** — the provisioned
|
|
66
|
+
workspace (installed dependencies, env/direnv auth wiring, a running dev
|
|
67
|
+
stack) is the entire value of local execution, and worktrees have none of it.
|
|
68
|
+
A dirty `git status` before any branch switch is a hard stop: report and let
|
|
69
|
+
the user commit or stash. Do not solve it with a second checkout.
|
|
70
|
+
- The dev database and dev-server ports are shared with the user's own
|
|
71
|
+
sessions. No destructive experiments, never reset the dev DB, and reuse a
|
|
72
|
+
running dev stack rather than fighting over ports.
|
|
73
|
+
|
|
74
|
+
## Resolve and route
|
|
75
|
+
|
|
76
|
+
1. Resolve the card from the argument — slug, id, or URL. **None given → ask
|
|
77
|
+
which card.** Never infer one from the branch you happen to be on.
|
|
78
|
+
2. `mcp__conveyor__get_task` for the full plan and
|
|
79
|
+
`mcp__conveyor__read_task_chat` for addenda and user answers. Consult
|
|
80
|
+
`mcp__conveyor__get_tag` on the card's tags before diving in — a tag's
|
|
81
|
+
overview and linked files are the fast path into the subsystem.
|
|
82
|
+
3. Route on the card's shape:
|
|
83
|
+
|
|
84
|
+
| Card shape | Path |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| No children | **Task path** — [references/task-path.md](references/task-path.md) |
|
|
87
|
+
| Has children, children PR into a **feature branch** | **Pack path** — [references/pack-path.md](references/pack-path.md) |
|
|
88
|
+
| Has children that PR straight into `dev` | Not a pack chain. Build the children one at a time via the task path; leave the parent alone. |
|
|
89
|
+
| Has a `parentTaskId` | Task path, but **only while the parent is parked** (neither InProgress nor ReviewPR). The child's base is the parent's feature branch, never `dev`. |
|
|
90
|
+
|
|
91
|
+
> **Environment — a pod's pack runs one branch, not one branch per child.** The
|
|
92
|
+
> pod's `create_pull_request` takes no task argument: it always opens the PR for
|
|
93
|
+
> the card the session is bound to. A pod bound to the pack PARENT therefore
|
|
94
|
+
> cannot open a CHILD's pull request, and must not try. It implements each child
|
|
95
|
+
> directly on the pack branch, commits per child, moves the child's status with
|
|
96
|
+
> `mcp__conveyor__update_task` (`task_id` + `status`), and opens exactly ONE pull
|
|
97
|
+
> request at the end — the parent's, into `dev`.
|
|
98
|
+
>
|
|
99
|
+
> **Locally** each child gets its own branch and its own PR into the pack
|
|
100
|
+
> branch, which is what makes them individually reviewable. Read the pack path
|
|
101
|
+
> with that substitution in mind: everywhere it says "the child's PR", a pod
|
|
102
|
+
> means "the child's commits on the pack branch", and the merge step becomes a
|
|
103
|
+
> status write rather than a merge.
|
|
104
|
+
|
|
105
|
+
**The parked-parent rule is load-bearing, not bookkeeping.** An actively
|
|
106
|
+
orchestrating parent reads a headless InProgress child as a dead agent
|
|
107
|
+
environment and "recovers" it onto a cloud pod — duplicate implementation of
|
|
108
|
+
the same child. If the parent is InProgress or ReviewPR, decline the child and
|
|
109
|
+
say why.
|
|
110
|
+
|
|
111
|
+
## Claim
|
|
112
|
+
|
|
113
|
+
Re-confirm the card is still claimable, then `mcp__conveyor__update_task` →
|
|
114
|
+
`status: "InProgress"`, then `mcp__conveyor__post_to_chat` with a claim marker
|
|
115
|
+
naming where you are running. Status changed under you → someone else took it;
|
|
116
|
+
stop.
|
|
117
|
+
|
|
118
|
+
**A pack parent is claimed differently** — see the pack path. The parent stays
|
|
119
|
+
parked and the chat marker IS the claim.
|
|
120
|
+
|
|
121
|
+
## Work the plan
|
|
122
|
+
|
|
123
|
+
Post to chat at real milestones only — claim, a blocking discovery, gates
|
|
124
|
+
green, PR — never play-by-play. Your turn output is not visible to the team;
|
|
125
|
+
chat is.
|
|
126
|
+
|
|
127
|
+
**Saving the plan and card fields:** `mcp__conveyor__update_task` handles
|
|
128
|
+
title, description, plan, status, and `githubBranch` on both surfaces. Story
|
|
129
|
+
points and risk are the one split: the local `update_task` carries them, the
|
|
130
|
+
pod's does not — in a pod they go through
|
|
131
|
+
`mcp__conveyor__update_task_properties`. Correct them when the work turns out
|
|
132
|
+
materially bigger or smaller than the estimate, in either direction; a stale
|
|
133
|
+
estimate is worse than a corrected one. Never re-size a card to make a gate, a
|
|
134
|
+
transition, or a review pass.
|
|
135
|
+
|
|
136
|
+
> **Environment — exiting plan mode in a pod.** A pod in discovery mode cannot
|
|
137
|
+
> leave plan mode until the card has a plan, a title, story points, AND a risk
|
|
138
|
+
> level. `update_task` carries neither of the last two, so that step needs
|
|
139
|
+
> `mcp__conveyor__update_task_properties`. A pod exposes it in `discovery`,
|
|
140
|
+
> `auto`, `building` and `chat` modes — but NOT in `review`, and not to a pack
|
|
141
|
+
> parent. If you hold neither tool, you are not the session that is supposed to
|
|
142
|
+
> be sizing the card: say so in chat rather than trying to force the transition.
|
|
143
|
+
|
|
144
|
+
## Verify
|
|
145
|
+
|
|
146
|
+
Follow the host repo's CLAUDE.md verification policy — scope the gates to your
|
|
147
|
+
diff rather than running everything. Never open a PR with known-failing gates,
|
|
148
|
+
and never silence a failure with a filter, a `.skip`, or a "known flake" note:
|
|
149
|
+
either your diff broke it, or the test is broken or environment-dependent and
|
|
150
|
+
you fix the test.
|
|
151
|
+
|
|
152
|
+
**A UI-visible change needs visual proof before the PR**, in both environments:
|
|
153
|
+
capture a screenshot (static) or a short recording (interaction) with the host
|
|
154
|
+
repo's own tooling and attach it with `mcp__conveyor__upload_attachment`, then
|
|
155
|
+
embed the returned URL in the PR body. A UI PR without it is incomplete.
|
|
156
|
+
|
|
157
|
+
**On a deep pack the gate surface grows, and that is expected.**
|
|
158
|
+
`test:affected` diffs against `origin/dev`, so by the fourth child it also
|
|
159
|
+
covers the three already merged into the pack branch. That is correct
|
|
160
|
+
behavior — do not go looking for why "unrelated" tests are running.
|
|
161
|
+
|
|
162
|
+
> **Repo config, not an environment difference — check it, don't assume it.**
|
|
163
|
+
> Where CI is configured to run only on PRs into `dev`/`main` (this is the
|
|
164
|
+
> Conveyor repo's config), a child PR into a pack branch gets **no automated
|
|
165
|
+
> verification at all**, and your local gate pass is the ONLY gate before that
|
|
166
|
+
> child merges — mandatory, never skippable. A consumer repo may trigger CI
|
|
167
|
+
> more broadly; if it does, let CI finish before merging. Confirm which case
|
|
168
|
+
> you are in rather than assuming, since the two lead to opposite behavior.
|
|
169
|
+
|
|
170
|
+
## Not every task ends in a PR
|
|
171
|
+
|
|
172
|
+
`create_pull_request` is for work that changes code. Plenty of cards don't:
|
|
173
|
+
support requests, config or credential help, answering a question,
|
|
174
|
+
investigations, research whose deliverable is an answer or a file.
|
|
175
|
+
|
|
176
|
+
If you finish with **no code changes** — an empty `git diff <base>..HEAD` —
|
|
177
|
+
do NOT open a PR. An empty or throwaway PR exists only so a human can close it.
|
|
178
|
+
Instead: post the answer, config, or findings with
|
|
179
|
+
`mcp__conveyor__post_to_chat`, attach anything the user should keep with
|
|
180
|
+
`mcp__conveyor__upload_attachment` (any file type, up to 25MB), and complete
|
|
181
|
+
the card directly with `force_update_task_status("Complete")` — there is no PR
|
|
182
|
+
or review step for a no-code task. Never publish a deliverable as an off-card
|
|
183
|
+
link; the card is where it belongs.
|
|
184
|
+
|
|
185
|
+
When unsure, check the diff: a real diff means open a PR, no diff means finish
|
|
186
|
+
in chat and mark it Complete.
|
|
187
|
+
|
|
188
|
+
## Open the PR
|
|
189
|
+
|
|
190
|
+
Refresh against the card's base first, then gate, then open — in that order, so
|
|
191
|
+
nothing invalidates the verification you just did:
|
|
192
|
+
|
|
193
|
+
1. `git fetch origin <base> && git merge origin/<base> --no-edit`
|
|
194
|
+
2. One verification pass, scoped to the diff.
|
|
195
|
+
3. `mcp__conveyor__create_pull_request`, naming the base branch **explicitly**.
|
|
196
|
+
**The two surfaces spell these differently and unknown keys are silently
|
|
197
|
+
dropped, not rejected** — locally it is `head:` / `base:`; in a pod it is
|
|
198
|
+
`branch:` / `baseBranch:`. Passing the local names in a pod does not error,
|
|
199
|
+
it just leaves the base unset, and the server then falls back to the base
|
|
200
|
+
recorded on the card. That fallback is usually right and is exactly why the
|
|
201
|
+
mistake is invisible — until the card's recorded branch has drifted, and the
|
|
202
|
+
PR opens against `dev`.
|
|
203
|
+
4. **Publish the PR guide** with `mcp__conveyor__publish_review_guide`, right
|
|
204
|
+
after `create_pull_request` succeeds — it is part of opening a PR, not a
|
|
205
|
+
follow-up. `sections` is a top-level array argument, not prose stuffed into
|
|
206
|
+
`overview`; keep `overview` a short intro and order the sections with core
|
|
207
|
+
behavior first, tests and generated files later. Reference only files this
|
|
208
|
+
diff actually changed, never context you merely read. Anchors are optional
|
|
209
|
+
and strict — `{"path": "..."}` alone is the safe form. Republish for the new
|
|
210
|
+
head SHA after any later push, or the card's Guide tab shows stale. It is
|
|
211
|
+
best-effort: if it still fails after one corrected retry, carry on — it
|
|
212
|
+
never blocks opening or updating the PR.
|
|
213
|
+
5. Post a chat summary: what shipped, how it was verified, what a reviewer
|
|
214
|
+
should look at, and anything you did NOT do.
|
|
215
|
+
|
|
216
|
+
Then confirm CI actually started (read-only `gh pr checks`). Do not wait on it.
|
|
217
|
+
|
|
218
|
+
**Do not re-merge the base and do not re-run a gate that already passed.** If
|
|
219
|
+
the base moved while the gates ran, open the PR anyway — CI validates against
|
|
220
|
+
the merged base.
|
|
221
|
+
|
|
222
|
+
## Aftercare
|
|
223
|
+
|
|
224
|
+
The card is in ReviewPR. What happens next depends on who is driving:
|
|
225
|
+
|
|
226
|
+
> **Environment — a pod is woken; a standalone local run is not.** In a **pod**
|
|
227
|
+
> you stay wake-able: the system wakes you on CI results and review comments,
|
|
228
|
+
> so go idle rather than polling. In a **standalone local run** there is no
|
|
229
|
+
> wake mechanism — report the card and PR state to the user and STOP. No
|
|
230
|
+
> pacing, no loop, no babysitting; a follow-up CI fix is a fresh ask. Under
|
|
231
|
+
> `conveyor-local-loop` the loop's own Babysit tier owns aftercare instead, and
|
|
232
|
+
> its pacing rules govern.
|
|
233
|
+
|
|
234
|
+
When you are the one handling it:
|
|
235
|
+
|
|
236
|
+
- **Red CI** → fix it now, on the same branch. Read the failure before
|
|
237
|
+
concluding it is yours: a suite that fails on a file your diff never touched
|
|
238
|
+
is usually environment-dependent, and the honest fix is to the test, in the
|
|
239
|
+
same PR or a dedicated one.
|
|
240
|
+
- **Review comments or requested changes** → address them, then reply saying
|
|
241
|
+
what changed.
|
|
242
|
+
- **Green and quiet** → leave it alone. Do not poll.
|
|
243
|
+
|
|
244
|
+
## Blocked
|
|
245
|
+
|
|
246
|
+
After two genuinely different failed approaches, or on a decision only the user
|
|
247
|
+
can make: post the reason AND the specific question to chat, set the card back
|
|
248
|
+
to `"Open"`, restore the tree (locally, `git checkout dev` — leaving a shared
|
|
249
|
+
checkout parked on an abandoned feature branch is how the next session starts
|
|
250
|
+
from the wrong base), and stop. A vague "this is hard" is not a handoff; the
|
|
251
|
+
question is what makes it one.
|
|
252
|
+
|
|
253
|
+
**A thin plan is a different case, and it does not stop a pack.** A card whose
|
|
254
|
+
plan fails the context-free-reader bar: post what is missing to its chat and
|
|
255
|
+
stop — but inside a pack, skip that child and take the next ready one instead
|
|
256
|
+
of halting the whole chain.
|
|
257
|
+
|
|
258
|
+
## Improve This Skill
|
|
259
|
+
|
|
260
|
+
If this skill was insufficient or slowed the work down, file it with
|
|
261
|
+
`mcp__conveyor__create_suggestion` on the Conveyor project: the issue,
|
|
262
|
+
evidence, and proposed fix.
|