@backburner/cli 0.1.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/LICENSE +21 -0
- package/README.md +329 -0
- package/dist/src/agents/antigravity-launch.js +162 -0
- package/dist/src/agents/antigravity.js +251 -0
- package/dist/src/agents/claude-launch.js +117 -0
- package/dist/src/agents/claude.js +271 -0
- package/dist/src/agents/codex-launch.js +82 -0
- package/dist/src/agents/codex.js +261 -0
- package/dist/src/agents/composite-runner.js +13 -0
- package/dist/src/agents/extraction.js +442 -0
- package/dist/src/agents/failure-feedback.js +132 -0
- package/dist/src/agents/gemini-launch.js +96 -0
- package/dist/src/agents/gemini.js +392 -0
- package/dist/src/agents/job-agent-assignments.js +180 -0
- package/dist/src/agents/lifecycle-feedback.js +147 -0
- package/dist/src/agents/limit-detector.js +83 -0
- package/dist/src/agents/opencode-launch.js +138 -0
- package/dist/src/agents/opencode.js +156 -0
- package/dist/src/agents/plan-breakdown-feedback.js +97 -0
- package/dist/src/agents/pool.js +168 -0
- package/dist/src/agents/prompt.js +462 -0
- package/dist/src/agents/provider-sessions.js +105 -0
- package/dist/src/agents/review-feedback.js +255 -0
- package/dist/src/agents/selection.js +113 -0
- package/dist/src/agents/service.js +1087 -0
- package/dist/src/agents/types.js +1 -0
- package/dist/src/agents/wave-assessment-feedback.js +69 -0
- package/dist/src/attention/derive.js +337 -0
- package/dist/src/capabilities/index.js +2 -0
- package/dist/src/capabilities/projection.js +183 -0
- package/dist/src/capabilities/types.js +1 -0
- package/dist/src/capabilities/validator.js +87 -0
- package/dist/src/cli/commands/broker-smoke.js +96 -0
- package/dist/src/cli/commands/broker.js +56 -0
- package/dist/src/cli/commands/init.js +4 -0
- package/dist/src/cli/commands/journal.js +43 -0
- package/dist/src/cli/commands/run-loop.js +56 -0
- package/dist/src/cli/commands/run.js +1241 -0
- package/dist/src/cli/commands/tui.js +187 -0
- package/dist/src/cli/dispatcher.js +51 -0
- package/dist/src/cli/errors.js +2 -0
- package/dist/src/cli/init.js +92 -0
- package/dist/src/cli/options.js +86 -0
- package/dist/src/cli/output.js +231 -0
- package/dist/src/cli/run-journal.js +342 -0
- package/dist/src/cli/run-types.js +1 -0
- package/dist/src/cli/run.js +32 -0
- package/dist/src/cli/runtime/dispatch-lifecycle.js +772 -0
- package/dist/src/cli/runtime/provider-runtime-types.js +1 -0
- package/dist/src/cli/runtime/provider-runtime.js +152 -0
- package/dist/src/cli/runtime/provider-tools.js +73 -0
- package/dist/src/cli/runtime/providers/antigravity.js +26 -0
- package/dist/src/cli/runtime/providers/claude.js +20 -0
- package/dist/src/cli/runtime/providers/codex.js +20 -0
- package/dist/src/cli/runtime/providers/gemini.js +20 -0
- package/dist/src/cli/runtime/providers/opencode.js +20 -0
- package/dist/src/cli/runtime/run-context.js +131 -0
- package/dist/src/cli/tui/index.js +3 -0
- package/dist/src/cli/tui/screen.js +388 -0
- package/dist/src/cli/tui/tracker.js +558 -0
- package/dist/src/cli/tui/types.js +1 -0
- package/dist/src/cli/tui/views.js +1006 -0
- package/dist/src/config/loader.js +136 -0
- package/dist/src/config/schemas.js +403 -0
- package/dist/src/config/types.js +1 -0
- package/dist/src/context/types.js +331 -0
- package/dist/src/domain/entities.js +1 -0
- package/dist/src/git/backburner-git-tool-service.js +680 -0
- package/dist/src/git/gitops-lifecycle.js +56 -0
- package/dist/src/git/merge-prep-workspace.js +357 -0
- package/dist/src/git/types.js +484 -0
- package/dist/src/github/broker-model-content.js +215 -0
- package/dist/src/github/broker-server.js +237 -0
- package/dist/src/github/broker.js +638 -0
- package/dist/src/github/composed.js +53 -0
- package/dist/src/github/gateway.js +478 -0
- package/dist/src/github/normalize.js +222 -0
- package/dist/src/github/pr-classification.js +73 -0
- package/dist/src/github/scoped-broker.js +35 -0
- package/dist/src/github/security.js +126 -0
- package/dist/src/github/service.js +332 -0
- package/dist/src/github/types.js +1 -0
- package/dist/src/github/utils.js +16 -0
- package/dist/src/journal/index.js +4 -0
- package/dist/src/journal/reader.js +62 -0
- package/dist/src/journal/renderer.js +50 -0
- package/dist/src/journal/service.js +72 -0
- package/dist/src/journal/types.js +1 -0
- package/dist/src/journal/writer.js +19 -0
- package/dist/src/mcps/bridge.js +166 -0
- package/dist/src/mcps/index.js +5 -0
- package/dist/src/mcps/reconcile.js +113 -0
- package/dist/src/mcps/smithery-client.js +186 -0
- package/dist/src/mcps/smithery.js +164 -0
- package/dist/src/mcps/types.js +1 -0
- package/dist/src/memory/json-store.js +71 -0
- package/dist/src/memory/prompt.js +16 -0
- package/dist/src/memory/scope.js +31 -0
- package/dist/src/memory/store.js +1 -0
- package/dist/src/memory/tool-service.js +142 -0
- package/dist/src/memory/types.js +12 -0
- package/dist/src/onboarding/config-writer.js +96 -0
- package/dist/src/onboarding/engine.js +86 -0
- package/dist/src/onboarding/prompt.js +102 -0
- package/dist/src/onboarding/services/provider-discovery.js +58 -0
- package/dist/src/onboarding/services/repo-discovery.js +246 -0
- package/dist/src/onboarding/steps/check-github-auth.js +32 -0
- package/dist/src/onboarding/steps/check-requirements.js +34 -0
- package/dist/src/onboarding/steps/configure-models.js +105 -0
- package/dist/src/onboarding/steps/configure-paths.js +33 -0
- package/dist/src/onboarding/steps/discover-providers.js +38 -0
- package/dist/src/onboarding/steps/discover-repos.js +39 -0
- package/dist/src/onboarding/steps/generate-config.js +106 -0
- package/dist/src/onboarding/steps/readiness-validation.js +158 -0
- package/dist/src/onboarding/steps/select-repos.js +76 -0
- package/dist/src/onboarding/steps/show-summary.js +97 -0
- package/dist/src/onboarding/steps/welcome.js +27 -0
- package/dist/src/onboarding/types.js +1 -0
- package/dist/src/onboarding/validators/git-access.js +63 -0
- package/dist/src/onboarding/validators/github-auth.js +85 -0
- package/dist/src/onboarding/validators/tool-checker.js +153 -0
- package/dist/src/onboarding/validators/workspace.js +83 -0
- package/dist/src/prompts/resolver.js +106 -0
- package/dist/src/retrospectives/candidates.js +224 -0
- package/dist/src/retrospectives/derive.js +169 -0
- package/dist/src/retrospectives/ingest.js +321 -0
- package/dist/src/retrospectives/management-prs.js +380 -0
- package/dist/src/retrospectives/proposals.js +199 -0
- package/dist/src/retrospectives/scopes.js +272 -0
- package/dist/src/retrospectives/scoring.js +171 -0
- package/dist/src/retrospectives/types.js +1 -0
- package/dist/src/state/loader.js +473 -0
- package/dist/src/state/types.js +15 -0
- package/dist/src/tasks/derivation/builders.js +70 -0
- package/dist/src/tasks/derivation/handlers/comment-response.js +169 -0
- package/dist/src/tasks/derivation/handlers/implement-plan.js +103 -0
- package/dist/src/tasks/derivation/handlers/index.js +54 -0
- package/dist/src/tasks/derivation/handlers/packet-pr-control.js +92 -0
- package/dist/src/tasks/derivation/handlers/packet-worktree.js +29 -0
- package/dist/src/tasks/derivation/handlers/plan-breakdown.js +45 -0
- package/dist/src/tasks/derivation/handlers/prepare-for-merge.js +91 -0
- package/dist/src/tasks/derivation/handlers/product-discovery.js +36 -0
- package/dist/src/tasks/derivation/handlers/retry-failed-task.js +125 -0
- package/dist/src/tasks/derivation/handlers/review-pr.js +28 -0
- package/dist/src/tasks/derivation/handlers/shared.js +24 -0
- package/dist/src/tasks/derivation/handlers/sync-parent-branch.js +100 -0
- package/dist/src/tasks/derivation/handlers/wave-assessment.js +223 -0
- package/dist/src/tasks/derivation/handlers/write-plan.js +101 -0
- package/dist/src/tasks/derivation/handlers/write-product-spec.js +49 -0
- package/dist/src/tasks/derivation/runner.js +18 -0
- package/dist/src/tasks/derivation/types.js +1 -0
- package/dist/src/tasks/derive.js +810 -0
- package/dist/src/tasks/mcp-skip.js +10 -0
- package/dist/src/tasks/retry.js +10 -0
- package/dist/src/tasks/task.js +184 -0
- package/dist/src/tasks/types.js +1 -0
- package/dist/src/utils/command-runner.js +192 -0
- package/dist/src/utils/json.js +38 -0
- package/dist/src/utils/logger.js +43 -0
- package/dist/src/utils/paths.js +38 -0
- package/dist/src/utils/slug.js +10 -0
- package/dist/src/workflows/registry.js +174 -0
- package/dist/src/workflows/triage.js +90 -0
- package/dist/src/workflows/workstream-machine/derive-events.js +121 -0
- package/dist/src/workflows/workstream-machine/evaluate.js +24 -0
- package/dist/src/workflows/workstream-machine/events.js +1 -0
- package/dist/src/workflows/workstream-machine/machine.js +355 -0
- package/dist/src/workflows/workstream-machine/selectors.js +19 -0
- package/dist/src/workflows/workstream-machine/types.js +1 -0
- package/dist/src/workstreams/branch-sync.js +87 -0
- package/dist/src/workstreams/derive.js +629 -0
- package/dist/src/workstreams/mcp-blockers.js +420 -0
- package/dist/src/workstreams/packet-candidates.js +42 -0
- package/dist/src/workstreams/packet-lifecycle-feedback.js +60 -0
- package/dist/src/workstreams/packet-lifecycle-report.js +199 -0
- package/dist/src/workstreams/packet-plan.js +597 -0
- package/dist/src/workstreams/packet-projections.js +190 -0
- package/dist/src/workstreams/parent-branch-sync-action.js +312 -0
- package/dist/src/workstreams/parent-branch-sync.js +148 -0
- package/dist/src/workstreams/phases.js +4 -0
- package/dist/src/workstreams/plan-breakdown-generator/export.js +19 -0
- package/dist/src/workstreams/plan-breakdown-generator/index.js +6 -0
- package/dist/src/workstreams/plan-breakdown-generator/inspect.js +50 -0
- package/dist/src/workstreams/plan-breakdown-generator/patch.js +139 -0
- package/dist/src/workstreams/plan-breakdown-generator/session.js +19 -0
- package/dist/src/workstreams/plan-breakdown-generator/store.js +27 -0
- package/dist/src/workstreams/plan-breakdown-generator/tools.js +407 -0
- package/dist/src/workstreams/plan-breakdown-generator/types.js +6 -0
- package/dist/src/workstreams/plan-breakdown-generator/validate.js +212 -0
- package/dist/src/workstreams/plan-breakdown-schema.js +175 -0
- package/dist/src/workstreams/utils.js +15 -0
- package/dist/src/workstreams/wave-assessment/checkpoint.js +172 -0
- package/dist/src/workstreams/wave-assessment/index.js +3 -0
- package/dist/src/workstreams/wave-assessment/session.js +84 -0
- package/dist/src/workstreams/wave-assessment/store.js +51 -0
- package/dist/src/workstreams/wave-assessment/tools.js +419 -0
- package/dist/src/workstreams/wave-assessment/types.js +1 -0
- package/dist/src/workstreams/wave-assessment/validate.js +78 -0
- package/dist/src/worktrees/service.js +839 -0
- package/dist/src/worktrees/workspace-ownership.js +34 -0
- package/dist/src/worktrees/workspace-recovery.js +177 -0
- package/package.json +61 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
class Semaphore {
|
|
2
|
+
available;
|
|
3
|
+
waiters = [];
|
|
4
|
+
constructor(max) {
|
|
5
|
+
this.available = max;
|
|
6
|
+
}
|
|
7
|
+
acquire() {
|
|
8
|
+
if (this.available > 0) {
|
|
9
|
+
this.available -= 1;
|
|
10
|
+
return Promise.resolve();
|
|
11
|
+
}
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
this.waiters.push(resolve);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
release() {
|
|
17
|
+
const next = this.waiters.shift();
|
|
18
|
+
if (next) {
|
|
19
|
+
next();
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
this.available += 1;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export class Mutex {
|
|
27
|
+
locked = false;
|
|
28
|
+
waiters = [];
|
|
29
|
+
acquire() {
|
|
30
|
+
if (!this.locked) {
|
|
31
|
+
this.locked = true;
|
|
32
|
+
return Promise.resolve();
|
|
33
|
+
}
|
|
34
|
+
return new Promise((resolve) => {
|
|
35
|
+
this.waiters.push(resolve);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
release() {
|
|
39
|
+
const next = this.waiters.shift();
|
|
40
|
+
if (!next) {
|
|
41
|
+
this.locked = false;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
next();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async runExclusive(fn) {
|
|
48
|
+
await this.acquire();
|
|
49
|
+
try {
|
|
50
|
+
return await fn();
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
this.release();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Per-key mutual exclusion. Tasks that share a key (workstream, branch, workspace)
|
|
59
|
+
* are serialized through that key's queue. Tasks with no shared keys run freely.
|
|
60
|
+
*
|
|
61
|
+
* Callers must acquire keys in sorted order (acquireAll enforces this) to avoid
|
|
62
|
+
* deadlock when a task needs multiple locks simultaneously.
|
|
63
|
+
*/
|
|
64
|
+
export class KeyedLock {
|
|
65
|
+
held = new Set();
|
|
66
|
+
queues = new Map();
|
|
67
|
+
acquire(key) {
|
|
68
|
+
if (!this.held.has(key)) {
|
|
69
|
+
this.held.add(key);
|
|
70
|
+
return Promise.resolve(() => this.release(key));
|
|
71
|
+
}
|
|
72
|
+
return new Promise((resolve) => {
|
|
73
|
+
const queue = this.queues.get(key) ?? [];
|
|
74
|
+
queue.push(() => resolve(() => this.release(key)));
|
|
75
|
+
this.queues.set(key, queue);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
release(key) {
|
|
79
|
+
const queue = this.queues.get(key);
|
|
80
|
+
if (queue && queue.length > 0) {
|
|
81
|
+
const next = queue.shift();
|
|
82
|
+
if (queue.length === 0) {
|
|
83
|
+
this.queues.delete(key);
|
|
84
|
+
}
|
|
85
|
+
next();
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.held.delete(key);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async acquireAll(keys) {
|
|
92
|
+
// Deduplicate and sort for consistent acquisition order (deadlock prevention).
|
|
93
|
+
const sorted = [...new Set(keys)].sort();
|
|
94
|
+
const releases = [];
|
|
95
|
+
for (const key of sorted) {
|
|
96
|
+
releases.push(await this.acquire(key));
|
|
97
|
+
}
|
|
98
|
+
return () => {
|
|
99
|
+
for (const r of releases) {
|
|
100
|
+
r();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Derive the set of scope lock keys a task must hold for its full execution.
|
|
107
|
+
*
|
|
108
|
+
* - workstream:<id> serializes all tasks in the same workstream (also
|
|
109
|
+
* implicitly covers provider-session since session IDs
|
|
110
|
+
* include workstreamId)
|
|
111
|
+
* - branch:<slug>:<name> serializes tasks operating on the same branch
|
|
112
|
+
* - workspace:<path> serializes tasks operating on the same local path
|
|
113
|
+
*
|
|
114
|
+
* Read-only tasks that have no workstreamId, branchName, or localRepoPath
|
|
115
|
+
* produce no lock keys and are always fully parallel.
|
|
116
|
+
*/
|
|
117
|
+
export function deriveExecutionLockKeys(task, executionCwdByTaskId) {
|
|
118
|
+
const keys = [];
|
|
119
|
+
if (task.workstreamId) {
|
|
120
|
+
keys.push(`workstream:${task.workstreamId}`);
|
|
121
|
+
}
|
|
122
|
+
if (task.context.branchName) {
|
|
123
|
+
keys.push(`branch:${task.repoSlug}:${task.context.branchName}`);
|
|
124
|
+
}
|
|
125
|
+
const resolvedPath = executionCwdByTaskId?.get(task.id) ?? task.context.localRepoPath;
|
|
126
|
+
if (resolvedPath) {
|
|
127
|
+
keys.push(`workspace:${resolvedPath}`);
|
|
128
|
+
}
|
|
129
|
+
return keys;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Controls concurrency of agent task dispatch.
|
|
133
|
+
*
|
|
134
|
+
* - semaphore: limits how many agents run simultaneously (maxConcurrent).
|
|
135
|
+
* - scopeLocks: per-key mutual exclusion so tasks sharing a workstream, branch,
|
|
136
|
+
* or workspace cannot run at the same time.
|
|
137
|
+
* - stateMutex: serializes all state reads and writes so concurrent agent
|
|
138
|
+
* executions don't corrupt shared arrays (tasks, executions, sessions).
|
|
139
|
+
*
|
|
140
|
+
* Global mutable bindings that are not concurrent-safe
|
|
141
|
+
* (ScopedBrokerToolService.bindAllowedTools, JobMemoryToolService.bind/unbind)
|
|
142
|
+
* are handled specially in service.ts: memory tools are filtered from the broker
|
|
143
|
+
* scope at batch start; per-task bind/unbind is skipped in concurrent mode.
|
|
144
|
+
*/
|
|
145
|
+
export class AgentPool {
|
|
146
|
+
maxConcurrent;
|
|
147
|
+
stateMutex;
|
|
148
|
+
scopeLocks;
|
|
149
|
+
semaphore;
|
|
150
|
+
constructor(maxConcurrent) {
|
|
151
|
+
this.maxConcurrent = Math.max(1, maxConcurrent);
|
|
152
|
+
this.semaphore = new Semaphore(this.maxConcurrent);
|
|
153
|
+
this.stateMutex = new Mutex();
|
|
154
|
+
this.scopeLocks = new KeyedLock();
|
|
155
|
+
}
|
|
156
|
+
get isConcurrent() {
|
|
157
|
+
return this.maxConcurrent > 1;
|
|
158
|
+
}
|
|
159
|
+
async runWithSlot(fn) {
|
|
160
|
+
await this.semaphore.acquire();
|
|
161
|
+
try {
|
|
162
|
+
return await fn();
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
this.semaphore.release();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { PLAN_BREAKDOWN_EXTRA_INSTRUCTIONS } from "../context/types.js";
|
|
3
|
+
import { toPrettyJson } from "../utils/json.js";
|
|
4
|
+
export function buildPromptPacket(task, context) {
|
|
5
|
+
const url = context.commentUrl ?? context.pullRequestUrl ?? context.issueUrl ?? task.sourceUrl;
|
|
6
|
+
const localPaths = context.localRepoPath ? [context.localRepoPath] : [];
|
|
7
|
+
return {
|
|
8
|
+
taskType: task.kind,
|
|
9
|
+
repo: task.repoSlug,
|
|
10
|
+
url,
|
|
11
|
+
executionPermission: task.executionPermission,
|
|
12
|
+
handoffPolicy: task.handoffPolicy,
|
|
13
|
+
...(context.issueTitle ? { issueTitle: context.issueTitle } : {}),
|
|
14
|
+
...(context.issueBody ? { issueBody: context.issueBody } : {}),
|
|
15
|
+
...(context.productSpecDocumentPath ? { productSpecDocumentPath: context.productSpecDocumentPath } : {}),
|
|
16
|
+
...(context.relevantCommentText ? { relevantComment: context.relevantCommentText } : {}),
|
|
17
|
+
...(context.pullRequestReviewLocation ? { pullRequestReviewLocation: context.pullRequestReviewLocation } : {}),
|
|
18
|
+
...(task.kind === "prepare_pr_for_merge" ? { backburnerGitSessionId: task.id } : {}),
|
|
19
|
+
...(context.preflight ? { preflight: context.preflight } : {}),
|
|
20
|
+
...(context.prepareForMerge ? { prepareForMerge: context.prepareForMerge } : {}),
|
|
21
|
+
...(context.waveAssessmentCheckpoint ? { waveAssessmentCheckpoint: context.waveAssessmentCheckpoint } : {}),
|
|
22
|
+
...(context.finalizedWaveAssessment ? { finalizedWaveAssessment: context.finalizedWaveAssessment } : {}),
|
|
23
|
+
...(context.retrospectiveCandidate ? { retrospectiveCandidate: context.retrospectiveCandidate } : {}),
|
|
24
|
+
...(context.retrospectiveProblem ? { retrospectiveProblem: context.retrospectiveProblem } : {}),
|
|
25
|
+
...(context.learningOpportunity ? { learningOpportunity: context.learningOpportunity } : {}),
|
|
26
|
+
...(context.capabilities ? { capabilities: context.capabilities } : {}),
|
|
27
|
+
action: context.instruction,
|
|
28
|
+
localPaths,
|
|
29
|
+
reminder: 'Any GitHub-authored message you post must begin with "🤖".'
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function renderPrompt(task, packet, options = {}) {
|
|
33
|
+
const providerIdentity = options.providerIdentity ?? "Codex CLI";
|
|
34
|
+
const localRepoPath = packet.localPaths[0];
|
|
35
|
+
const targetPath = localRepoPath ?? ".";
|
|
36
|
+
const relevantUrl = packet.url;
|
|
37
|
+
const localPathLine = localRepoPath === undefined
|
|
38
|
+
? "- local paths: []"
|
|
39
|
+
: `- local paths:\n - ${normalizeForPrompt(localRepoPath)}`;
|
|
40
|
+
const overlayLines = options.learningOverlays && options.learningOverlays.length > 0
|
|
41
|
+
? [
|
|
42
|
+
"",
|
|
43
|
+
"Approved learning overlays (apply as additional guidance, from broadest to narrowest scope):",
|
|
44
|
+
...options.learningOverlays.flatMap((overlay, i) => [
|
|
45
|
+
"",
|
|
46
|
+
`--- Learning overlay ${i + 1} ---`,
|
|
47
|
+
overlay
|
|
48
|
+
])
|
|
49
|
+
]
|
|
50
|
+
: [];
|
|
51
|
+
return [
|
|
52
|
+
`You are ${providerIdentity} running inside the Backburner local GitHub-driven orchestrator.`,
|
|
53
|
+
"Work only from the prompt packet below. Keep actions aligned with the task and current repository state.",
|
|
54
|
+
"",
|
|
55
|
+
"Execution rules:",
|
|
56
|
+
`- task type: ${task.kind}`,
|
|
57
|
+
`- local execution permission: ${task.executionPermission}`,
|
|
58
|
+
`- expects pushable handoff: ${task.handoffPolicy.expectsPushableHandoff ? "yes" : "no"}`,
|
|
59
|
+
`- remote push owner: ${task.handoffPolicy.remotePublishOwner}`,
|
|
60
|
+
`- repo: ${task.repoSlug}`,
|
|
61
|
+
...(task.workstreamId ? [`- workstream id: ${task.workstreamId}`] : []),
|
|
62
|
+
...(packet.backburnerGitSessionId ? [`- Backburner Git session ID: ${packet.backburnerGitSessionId}`] : []),
|
|
63
|
+
`- target url: ${relevantUrl}`,
|
|
64
|
+
localPathLine,
|
|
65
|
+
`- action: ${packet.action}`,
|
|
66
|
+
`- reminder: ${packet.reminder}`,
|
|
67
|
+
...(packet.issueTitle ? [`- parent issue title: ${packet.issueTitle}`] : []),
|
|
68
|
+
...(packet.issueBody ? [`- parent issue body: ${packet.issueBody}`] : []),
|
|
69
|
+
...(packet.productSpecDocumentPath ? [`- product spec document: ${packet.productSpecDocumentPath}`] : []),
|
|
70
|
+
...(packet.relevantComment ? [`- relevant comment: ${packet.relevantComment}`] : []),
|
|
71
|
+
...(packet.pullRequestReviewLocation ? renderReviewCommentLocation(packet.pullRequestReviewLocation) : []),
|
|
72
|
+
...(packet.preflight ? renderPreflight(packet.preflight) : []),
|
|
73
|
+
...(packet.prepareForMerge ? renderPrepareForMerge(packet.prepareForMerge) : []),
|
|
74
|
+
...(packet.waveAssessmentCheckpoint ? renderWaveAssessmentCheckpoint(packet.waveAssessmentCheckpoint) : []),
|
|
75
|
+
...(packet.finalizedWaveAssessment ? renderFinalizedWaveAssessment(packet.finalizedWaveAssessment) : []),
|
|
76
|
+
...(packet.retrospectiveCandidate ? [`- retrospective candidate: ${packet.retrospectiveCandidate.id}`] : []),
|
|
77
|
+
...(packet.retrospectiveProblem ? [`- retrospective problem: ${packet.retrospectiveProblem.id}`] : []),
|
|
78
|
+
...(packet.learningOpportunity ? [`- learning opportunity: ${packet.learningOpportunity.id}`] : []),
|
|
79
|
+
...(packet.capabilities ? renderCapabilities(packet.capabilities) : []),
|
|
80
|
+
"",
|
|
81
|
+
"Constraints:",
|
|
82
|
+
`- Run inside the repository at ${normalizeForPrompt(targetPath)} when a local path is provided.`,
|
|
83
|
+
"- Stay inside the assigned worktree or repository path; do not operate outside it.",
|
|
84
|
+
"- Rebuild your understanding from the local checkout and the packet; do not assume hidden orchestration state.",
|
|
85
|
+
"- Do not claim files changed unless you actually changed them.",
|
|
86
|
+
"- Use sensors for read/context needs and controllers for write/action needs.",
|
|
87
|
+
"- Do not use a blocked capability.",
|
|
88
|
+
"- Orchestrator-owned publication stays separate unless the task packet explicitly delegates that publication to an agent-visible controller.",
|
|
89
|
+
...(options.customConstraints !== undefined
|
|
90
|
+
? splitLines(options.customConstraints)
|
|
91
|
+
: buildTaskSpecificConstraints(task)),
|
|
92
|
+
...buildMcpSkipConstraints(task),
|
|
93
|
+
"",
|
|
94
|
+
"Prompt packet JSON:",
|
|
95
|
+
"```json",
|
|
96
|
+
toPrettyJson(packet).trimEnd(),
|
|
97
|
+
"```",
|
|
98
|
+
...overlayLines
|
|
99
|
+
].join("\n");
|
|
100
|
+
}
|
|
101
|
+
function renderPreflight(preflight) {
|
|
102
|
+
return [
|
|
103
|
+
`Preflight details:`,
|
|
104
|
+
`- Current packet wave: ${preflight.currentWaveIndex}`,
|
|
105
|
+
`- Next packet wave: ${preflight.nextWaveIndex}`,
|
|
106
|
+
`- Parent integration branch: ${preflight.parentIntegrationBranch}`,
|
|
107
|
+
`- Default branch: ${preflight.defaultBranch}`,
|
|
108
|
+
`- Completed packet IDs: ${preflight.completedPacketIds.join(", ")}`,
|
|
109
|
+
`- Pending future packet IDs: ${preflight.pendingFuturePacketIds.join(", ")}`,
|
|
110
|
+
...(preflight.parentPrNumber !== undefined ? [`- Parent PR: #${preflight.parentPrNumber}`] : [])
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
function renderPrepareForMerge(preflight) {
|
|
114
|
+
return [
|
|
115
|
+
`Prepare for merge details:`,
|
|
116
|
+
`- PR: #${preflight.prNumber}`,
|
|
117
|
+
`- Title: ${preflight.prTitle}`,
|
|
118
|
+
`- Head branch: ${preflight.headBranch}`,
|
|
119
|
+
`- Head SHA: ${preflight.headSha}`,
|
|
120
|
+
`- Base branch: ${preflight.baseBranch}`
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
function renderWaveAssessmentCheckpoint(checkpoint) {
|
|
124
|
+
return [
|
|
125
|
+
"Wave assessment checkpoint:",
|
|
126
|
+
`- Checkpoint ID: ${checkpoint.id}`,
|
|
127
|
+
`- Workstream ID: ${checkpoint.identity.workstreamId}`,
|
|
128
|
+
`- Wave ID: ${checkpoint.identity.waveId}`,
|
|
129
|
+
`- Parent PR: #${checkpoint.identity.parentPrNumber}`,
|
|
130
|
+
`- Parent head SHA: ${checkpoint.identity.parentHeadSha}`,
|
|
131
|
+
`- Plan revision: ${checkpoint.identity.planRevision}`,
|
|
132
|
+
`- Original planning session ID: ${checkpoint.identity.originalPlanningSessionId}`,
|
|
133
|
+
`- Current wave: ${checkpoint.identity.currentWaveIndex}`,
|
|
134
|
+
`- Next wave: ${checkpoint.identity.nextWaveIndex}`,
|
|
135
|
+
`- Parent integration branch: ${checkpoint.identity.parentIntegrationBranch}`,
|
|
136
|
+
`- Default branch: ${checkpoint.identity.defaultBranch}`,
|
|
137
|
+
`- Completed packet IDs: ${checkpoint.identity.completedPacketIds.join(", ")}`,
|
|
138
|
+
`- Pending future packet IDs: ${checkpoint.identity.pendingFuturePacketIds.join(", ")}`,
|
|
139
|
+
`- Plan node IDs: ${checkpoint.identity.planNodeIds.join(", ")}`
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
function renderFinalizedWaveAssessment(assessment) {
|
|
143
|
+
return [
|
|
144
|
+
"Finalized wave assessment:",
|
|
145
|
+
`- Assessment ID: ${assessment.id}`,
|
|
146
|
+
`- Decision: ${assessment.decision}`,
|
|
147
|
+
`- Summary: ${assessment.summary}`,
|
|
148
|
+
...(assessment.postedCommentId ? [`- Posted comment ID: ${assessment.postedCommentId}`] : [])
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
function renderReviewCommentLocation(location) {
|
|
152
|
+
const lines = ["PR review comment location:"];
|
|
153
|
+
if (location.path)
|
|
154
|
+
lines.push(`- Path: ${location.path}`);
|
|
155
|
+
if (location.line !== undefined && location.line !== null)
|
|
156
|
+
lines.push(`- Line: ${location.line}`);
|
|
157
|
+
if (location.side)
|
|
158
|
+
lines.push(`- Side: ${location.side}`);
|
|
159
|
+
const hasStartLine = location.startLine !== undefined && location.startLine !== null;
|
|
160
|
+
const hasLine = location.line !== undefined && location.line !== null;
|
|
161
|
+
if (hasStartLine || hasLine) {
|
|
162
|
+
const start = hasStartLine ? location.startLine : location.line;
|
|
163
|
+
const end = hasLine ? location.line : start;
|
|
164
|
+
const range = `${start}${start !== end ? `-${end}` : ""}`;
|
|
165
|
+
lines.push(`- Range: ${location.startSide ? `${range}, ${location.startSide}` : range}`);
|
|
166
|
+
}
|
|
167
|
+
if (location.commitId)
|
|
168
|
+
lines.push(`- Commit: ${location.commitId}`);
|
|
169
|
+
if (location.originalCommitId)
|
|
170
|
+
lines.push(`- Original commit: ${location.originalCommitId}`);
|
|
171
|
+
if (location.inReplyToId)
|
|
172
|
+
lines.push(`- In reply to: ${location.inReplyToId}`);
|
|
173
|
+
if (location.diffHunk)
|
|
174
|
+
lines.push(`\nDiff hunk:\n\`\`\`diff\n${location.diffHunk}\n\`\`\``);
|
|
175
|
+
return lines;
|
|
176
|
+
}
|
|
177
|
+
function renderCapabilities(capabilities) {
|
|
178
|
+
const lines = [
|
|
179
|
+
"",
|
|
180
|
+
"Capability projection:",
|
|
181
|
+
"- Sensors describe what the agent may observe.",
|
|
182
|
+
"- Controllers describe what the agent may do.",
|
|
183
|
+
"- Do not use blocked capabilities, and do not replace a missing controller with raw CLI work unless the task explicitly permits local repository work.",
|
|
184
|
+
"",
|
|
185
|
+
"Available sensors:"
|
|
186
|
+
];
|
|
187
|
+
if (capabilities.sensors.length === 0) {
|
|
188
|
+
lines.push("- none configured");
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
for (const sensor of capabilities.sensors) {
|
|
192
|
+
const metadata = [`uses ${sensor.uses}`, `observes ${sensor.observes}`];
|
|
193
|
+
if (sensor.scope)
|
|
194
|
+
metadata.push(`scope ${sensor.scope}`);
|
|
195
|
+
if (sensor.cadence)
|
|
196
|
+
metadata.push(`cadence ${sensor.cadence}`);
|
|
197
|
+
if (sensor.tools.length > 0)
|
|
198
|
+
metadata.push(`tools ${sensor.tools.join(", ")}`);
|
|
199
|
+
lines.push(`- ${sensor.name}: ${metadata.join("; ")}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
lines.push("", "Available controllers:");
|
|
203
|
+
if (capabilities.controllers.length === 0) {
|
|
204
|
+
lines.push("- none configured");
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
for (const controller of capabilities.controllers) {
|
|
208
|
+
const metadata = [
|
|
209
|
+
`uses ${controller.uses}`,
|
|
210
|
+
`grants ${controller.grants.join(", ")}`,
|
|
211
|
+
`approval ${controller.approval}`
|
|
212
|
+
];
|
|
213
|
+
if (controller.tools.length > 0)
|
|
214
|
+
metadata.push(`tools ${controller.tools.join(", ")}`);
|
|
215
|
+
lines.push(`- ${controller.name}: ${metadata.join("; ")}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
lines.push("", "Blocked capabilities:");
|
|
219
|
+
if (capabilities.blocked.length === 0) {
|
|
220
|
+
lines.push("- none configured");
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
for (const blocked of capabilities.blocked) {
|
|
224
|
+
const metadata = [`uses ${blocked.uses}`, `reason ${blocked.reason}`];
|
|
225
|
+
if (blocked.message)
|
|
226
|
+
metadata.push(blocked.message);
|
|
227
|
+
lines.push(`- ${blocked.kind} ${blocked.name}: ${metadata.join("; ")}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return lines;
|
|
231
|
+
}
|
|
232
|
+
function buildMcpSkipConstraints(task) {
|
|
233
|
+
const skipContexts = task.context.mcpSkipContexts ?? [];
|
|
234
|
+
if (skipContexts.length === 0) {
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
return [
|
|
238
|
+
"",
|
|
239
|
+
"MCP skip constraints:",
|
|
240
|
+
...skipContexts.flatMap((context) => {
|
|
241
|
+
const names = context.affectedCapabilityNames.join(", ");
|
|
242
|
+
return [
|
|
243
|
+
`- The following MCP capabilities were explicitly skipped for this task by the user: ${names} (MCP: ${context.mcpHandle}).`,
|
|
244
|
+
"- Do not attempt to use these capabilities or retry authorization.",
|
|
245
|
+
"- These tools will not appear in your available toolset.",
|
|
246
|
+
"- If completing this task correctly requires these capabilities, find a valid workaround using only what is available, or report that the task cannot be completed correctly without them."
|
|
247
|
+
];
|
|
248
|
+
})
|
|
249
|
+
];
|
|
250
|
+
}
|
|
251
|
+
function buildTaskSpecificConstraints(task) {
|
|
252
|
+
const planDocumentPath = describeTaskDocumentPath(task.context.localRepoPath, task.context.planDocumentPath);
|
|
253
|
+
const productSpecDocumentPath = task.context.productSpecDocumentPath
|
|
254
|
+
? describeTaskDocumentPath(task.context.localRepoPath, task.context.productSpecDocumentPath)
|
|
255
|
+
: undefined;
|
|
256
|
+
const logDocumentPath = describeTaskDocumentPath(task.context.localRepoPath, task.context.logDocumentPath);
|
|
257
|
+
const productSpecPlanningConstraint = productSpecDocumentPath
|
|
258
|
+
? `The Product Spec is the product source of truth for this workstream when present: ${productSpecDocumentPath}. Do not rely only on the original issue once a Product Spec exists.`
|
|
259
|
+
: undefined;
|
|
260
|
+
const productSpecTaskConstraint = productSpecDocumentPath
|
|
261
|
+
? `- The Product Spec is the product source of truth for this workstream when present: ${productSpecDocumentPath}. Do not rely only on the original issue once a Product Spec exists.`
|
|
262
|
+
: undefined;
|
|
263
|
+
const productSpecReviewConstraint = productSpecDocumentPath
|
|
264
|
+
? `- The Product Spec is the product source of truth for this workstream when present: ${productSpecDocumentPath}. Review against its acceptance criteria when available.`
|
|
265
|
+
: undefined;
|
|
266
|
+
const executionConstraints = task.executionPermission === "read-only"
|
|
267
|
+
? [
|
|
268
|
+
"- You are in read-only mode. Inspect, analyze, and report, but do not modify files or attempt local git writes."
|
|
269
|
+
]
|
|
270
|
+
: [
|
|
271
|
+
"- You are in workspace-write mode. You may edit files and use local git commands needed to keep the assigned branch/worktree healthy.",
|
|
272
|
+
"- Keep the workspace on the intended branch and in a pushable state for orchestrator handoff.",
|
|
273
|
+
"- Do not leave detached HEAD state, half-completed rebases or merges, or a broken index behind.",
|
|
274
|
+
"- Do not push remotely or publish PRs. The orchestrator owns remote push and PR publication.",
|
|
275
|
+
"- If local commits exist when you finish, leave them reviewable and pushable; the orchestrator will handle the push."
|
|
276
|
+
];
|
|
277
|
+
switch (task.kind) {
|
|
278
|
+
case "product_discovery":
|
|
279
|
+
return [
|
|
280
|
+
...executionConstraints,
|
|
281
|
+
"- Act as Product Manager presenting to the CTO.",
|
|
282
|
+
"- Post the Product Discovery analysis on the GitHub issue through the configured broker MCP.",
|
|
283
|
+
"- Stay product-focused; do not include implementation planning, architecture, file suggestions, code details, branch details, or worktree details.",
|
|
284
|
+
"- A future UI/UX step may be recommended only as an informational product/design consideration."
|
|
285
|
+
];
|
|
286
|
+
case "respond_issue_comment":
|
|
287
|
+
if (task.workflowPhase === "product_discovery" || task.workflowPhase === "product_specification") {
|
|
288
|
+
return [
|
|
289
|
+
...executionConstraints,
|
|
290
|
+
"- You are in the Product Discovery/Product Specification phase. This is a mutual discovery dialogue, not a routing decision.",
|
|
291
|
+
"- Post the response on the GitHub issue through the configured broker MCP.",
|
|
292
|
+
"- Respond as Product Manager to refine shared understanding with the user.",
|
|
293
|
+
"- Do not return a JSON routing decision; do not use answer_only or start_implementation.",
|
|
294
|
+
"- If you need more information, surface it in an 'Open Questions' section; the user will try to answer.",
|
|
295
|
+
"- Do not discuss implementation, files, code, architecture, branches, or worktrees.",
|
|
296
|
+
"- Implementation authorization is the user's act of applying the product approval label, not your conclusion."
|
|
297
|
+
];
|
|
298
|
+
}
|
|
299
|
+
return [
|
|
300
|
+
...executionConstraints,
|
|
301
|
+
"- Post the response on the GitHub issue through the configured broker MCP.",
|
|
302
|
+
"- Answer in the context of the active workstream phase.",
|
|
303
|
+
"- Do not return a JSON routing decision; do not use answer_only or start_implementation.",
|
|
304
|
+
"- Implementation authorization comes from explicit workflow labels, not from issue-comment follow-up decisions."
|
|
305
|
+
];
|
|
306
|
+
case "write_plan":
|
|
307
|
+
return [
|
|
308
|
+
"The plan must consider every thing in the issue and the context. Do not assume what to implement but follow the instructions in the context.",
|
|
309
|
+
...(productSpecPlanningConstraint ? [productSpecPlanningConstraint] : []),
|
|
310
|
+
"If you are unable to fetch the issue or pr data from gh stop and report it. Do not plan some arbitrary change.",
|
|
311
|
+
...executionConstraints,
|
|
312
|
+
`- Write the plan to ${planDocumentPath} and keep ${logDocumentPath} updated.`,
|
|
313
|
+
"- The plan must be comprehensive enough for a later cheap-model implementation pass.",
|
|
314
|
+
"- Local commits are allowed if they help keep the worktree in a clean, reviewable state, but do not push them.",
|
|
315
|
+
"- Do not begin implementation in this task."
|
|
316
|
+
];
|
|
317
|
+
case "implement_plan":
|
|
318
|
+
return [
|
|
319
|
+
...executionConstraints,
|
|
320
|
+
"- Only implement after confirming the work is already in the approved-plan phase.",
|
|
321
|
+
...(productSpecTaskConstraint ? [productSpecTaskConstraint] : []),
|
|
322
|
+
`- Use ${planDocumentPath} as the source of truth for the implementation task.`,
|
|
323
|
+
"- Local commits are allowed if they help keep the worktree in a clean, reviewable state, but do not push them."
|
|
324
|
+
];
|
|
325
|
+
case "review_pr":
|
|
326
|
+
return [
|
|
327
|
+
...executionConstraints,
|
|
328
|
+
...(productSpecReviewConstraint ? [productSpecReviewConstraint] : []),
|
|
329
|
+
'Output contract: finish with a JSON object like {"action":"APPROVE","summary":"..."} or {"action":"REQUEST_CHANGES","summary":"...","inlineComments":[{"path":"src/file.ts","line":12,"body":"..."}]}.',
|
|
330
|
+
"- Use APPROVE only when there are no actionable findings.",
|
|
331
|
+
"- Use REQUEST_CHANGES when findings should block merge.",
|
|
332
|
+
"- Use COMMENT for non-blocking review feedback.",
|
|
333
|
+
"- summary is required and should provide a high-level overview of your review.",
|
|
334
|
+
"- Each inline comment must be suitable for an inline GitHub review comment with a repo-relative path and a line number on the PR head version.",
|
|
335
|
+
"- Inline comments are optional and should be concrete, location-specific, and limited.",
|
|
336
|
+
"- Do not post the review directly to GitHub; the orchestrator owns publication."
|
|
337
|
+
];
|
|
338
|
+
case "plan_breakdown":
|
|
339
|
+
return [
|
|
340
|
+
...executionConstraints,
|
|
341
|
+
"- Do not implement code.",
|
|
342
|
+
"- You must use the Plan Breakdown Generator tool to decompose this plan.",
|
|
343
|
+
"- Start a new session, build candidate packet plans using patches, and validate them.",
|
|
344
|
+
...PLAN_BREAKDOWN_EXTRA_INSTRUCTIONS.map((s) => `- ${s}`),
|
|
345
|
+
'- Return exactly one JSON object: { "sessionId": "<selected generator session id>" }.',
|
|
346
|
+
"- If no valid session can be produced, publish details to GitHub through the configured GitHub broker MCP when appropriate.",
|
|
347
|
+
"- Failure details should include blocker, missing information, validation errors, and recommended human action when available.",
|
|
348
|
+
"- After failure details are published, finish without returning a valid sessionId.",
|
|
349
|
+
"- No valid sessionId means the orchestrator treats the plan_breakdown task as failed.",
|
|
350
|
+
"- Do not return raw PlanBreakdownResult JSON directly.",
|
|
351
|
+
"- Do not return Markdown as the primary result.",
|
|
352
|
+
"- Do not wrap the JSON in a Markdown code fence.",
|
|
353
|
+
"- Do not include prose before or after the JSON.",
|
|
354
|
+
"- The orchestrator will export the selected session into the final PlanBreakdownResult artifact."
|
|
355
|
+
];
|
|
356
|
+
case "wave_plan_assessment":
|
|
357
|
+
return [
|
|
358
|
+
...executionConstraints,
|
|
359
|
+
"- This is a plan-level checkpoint between packet waves.",
|
|
360
|
+
"- Assess only the parent issue and parent PR plan; do not review packet PR implementation details as code review findings.",
|
|
361
|
+
"- You must use the wave_assessment MCP tools for the checkpoint in the prompt packet.",
|
|
362
|
+
"- Finalize the assessment through the MCP tool flow before returning.",
|
|
363
|
+
"- Do not post the parent PR assessment comment yourself; the orchestrator publishes the canonical comment after it exports your finalized session.",
|
|
364
|
+
"- Do not modify repository files, branches, or git state.",
|
|
365
|
+
'- Output contract: finish with a JSON object like {"sessionId":"<finalized wave assessment session id>"}.'
|
|
366
|
+
];
|
|
367
|
+
case "wave_plan_replanning":
|
|
368
|
+
return [
|
|
369
|
+
...executionConstraints,
|
|
370
|
+
"- This is parent-plan replanning after a finalized wave assessment.",
|
|
371
|
+
"- The finalized assessment in the prompt packet is mandatory source context.",
|
|
372
|
+
"- Do not proceed from memory, draft assessment state, or a stale checkpoint.",
|
|
373
|
+
"- Keep changes scoped to parent packet plan replanning; do not edit packet implementation PRs.",
|
|
374
|
+
"- Use the wave_assessment MCP context when you need assessment details or audit linkage.",
|
|
375
|
+
"- You must use the plan_breakdown MCP tools to produce the revised parent packet plan.",
|
|
376
|
+
"- Do not return raw PlanBreakdownResult JSON directly.",
|
|
377
|
+
'- Output contract: finish with a JSON object like {"sessionId":"<selected plan breakdown generator session id>"}.',
|
|
378
|
+
"- The orchestrator will export the selected session into the final PlanBreakdownResult artifact and install it as the new packet plan.",
|
|
379
|
+
"- Do not run parent branch sync.",
|
|
380
|
+
"- Leave the workspace clean and pushable for orchestrator handoff."
|
|
381
|
+
];
|
|
382
|
+
case "prepare_pr_for_merge":
|
|
383
|
+
return [
|
|
384
|
+
"- You are resolving merge conflicts in a dedicated prepare-for-merge clone.",
|
|
385
|
+
"- The orchestrator already fetched refs, checked out the PR branch, reset it to the expected PR head SHA, and started a normal merge from the base branch into the PR branch.",
|
|
386
|
+
"- Do not run raw Git commands for index, commit, checkout, reset, fetch, merge, rebase, or push.",
|
|
387
|
+
"- Do not push. The orchestrator owns final validation and normal push after this task.",
|
|
388
|
+
"- The workstream ID is parent orchestration context only; do not use it as a Backburner Git tool argument.",
|
|
389
|
+
"- Use the Backburner Git MCP tools with the Backburner Git session ID shown above as `backburnerGitSessionId` in every `backburner_git_*` call.",
|
|
390
|
+
"- Use those tools to inspect conflict state, read base/ours/theirs versions, mark files resolved or deleted, unstage mistakes, and continue the merge.",
|
|
391
|
+
"- Resolve conflicts semantically. Preserve both the PR's intended changes and the base branch's intended changes unless they are truly incompatible.",
|
|
392
|
+
"- Avoid deleting work accidentally. Stop and report if a product decision is required.",
|
|
393
|
+
"- Local verification:",
|
|
394
|
+
" - Run local build, test, typecheck, and lint commands.",
|
|
395
|
+
" - Do not poll or wait for remote GitHub CI.",
|
|
396
|
+
"- Safe fixes:",
|
|
397
|
+
" - Fix only local merge/build/test/check failures needed to make the PR green after push.",
|
|
398
|
+
" - Do not introduce new product behavior or refactor unrelated code.",
|
|
399
|
+
"- Bounded effort:",
|
|
400
|
+
" - Run at most a small number of focused verification/fix attempts.",
|
|
401
|
+
" - Stop with a clean repo and report failing checks if not resolved.",
|
|
402
|
+
"- Handoff:",
|
|
403
|
+
" - Search for conflict markers before finishing.",
|
|
404
|
+
" - Before backburner_git_continue_merge, make sure there are no unstaged tracked changes or untracked files. Stage intended tracked changes, restore accidental worktree edits, or remove unintended untracked files first.",
|
|
405
|
+
" - Use backburner_git_continue_merge after all conflicted paths are resolved and the worktree is clean except for staged merge resolution changes.",
|
|
406
|
+
" - Leave no conflict markers or half-completed merge/rebase/cherry-pick state.",
|
|
407
|
+
" - Do not post a GitHub PR comment; the orchestrator posts the final audit comment.",
|
|
408
|
+
" - Write a summary of work done, checks run, and results as your final response output."
|
|
409
|
+
];
|
|
410
|
+
case "respond_pr_comment":
|
|
411
|
+
return [
|
|
412
|
+
...executionConstraints,
|
|
413
|
+
"- Read the pull request and the comment before taking any action.",
|
|
414
|
+
"- Classify the comment into one of three cases and follow the corresponding flow:",
|
|
415
|
+
"",
|
|
416
|
+
" Case 1 — Retry request: the comment is asking you to retry a previously failed orchestrator task.",
|
|
417
|
+
" 1. Do not attempt any implementation.",
|
|
418
|
+
" 2. Locate the orchestrator failure comment in the PR thread (it says 'I tried to handle this orchestrator task' and includes the task kind and execution ID).",
|
|
419
|
+
" 3. Copy its direct GitHub URL — it ends with `#issuecomment-<numeric-id>`.",
|
|
420
|
+
" 4. Post a reply via the GitHub broker MCP containing exactly: `retry: <that-url>`",
|
|
421
|
+
" Example: `retry: https://github.com/owner/repo/pull/42#issuecomment-1234567890`",
|
|
422
|
+
"",
|
|
423
|
+
" Case 2 — Code change or implementation request: the comment is asking for code changes, fixes, or implementation work",
|
|
424
|
+
" (e.g. review feedback requesting changes, 'fix this', 'let's implement this', 'add X', 'update Y').",
|
|
425
|
+
" 1. Read the approved plan document if one exists (planDocumentPath in the packet) to understand intended scope.",
|
|
426
|
+
" 2. Make the requested changes to the local repository.",
|
|
427
|
+
" 3. Post a reply via the GitHub broker MCP summarizing what was changed.",
|
|
428
|
+
" 4. Do not push — the orchestrator owns the push.",
|
|
429
|
+
"",
|
|
430
|
+
" Case 3 — Discussion or question: the comment asks a question, requests clarification, or starts a conversation that does not require code changes.",
|
|
431
|
+
" 1. Post a substantive reply via the GitHub broker MCP.",
|
|
432
|
+
" 2. Do not make any code changes.",
|
|
433
|
+
"",
|
|
434
|
+
"- Always post a GitHub reply (via the broker MCP) regardless of which case applies — never finish silently."
|
|
435
|
+
];
|
|
436
|
+
case "write_product_spec":
|
|
437
|
+
return [
|
|
438
|
+
...executionConstraints,
|
|
439
|
+
`- Write the Product Spec to ${productSpecDocumentPath} and keep ${logDocumentPath} updated.`,
|
|
440
|
+
"- Stay product-focused. Do not write the implementation plan.",
|
|
441
|
+
"- Include acceptance criteria and a UI/UX recommendation only if relevant.",
|
|
442
|
+
"- Add a note that downstream planner, implementer, and reviewer must read the Product Spec."
|
|
443
|
+
];
|
|
444
|
+
default:
|
|
445
|
+
return executionConstraints;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
function normalizeForPrompt(value) {
|
|
449
|
+
return value.split(path.sep).join("/");
|
|
450
|
+
}
|
|
451
|
+
function describeTaskDocumentPath(localRepoPath, documentPath) {
|
|
452
|
+
if (!documentPath) {
|
|
453
|
+
return "the assigned planning document";
|
|
454
|
+
}
|
|
455
|
+
if (!localRepoPath) {
|
|
456
|
+
return normalizeForPrompt(documentPath);
|
|
457
|
+
}
|
|
458
|
+
return normalizeForPrompt(path.relative(localRepoPath, documentPath));
|
|
459
|
+
}
|
|
460
|
+
function splitLines(text) {
|
|
461
|
+
return text.split(/\r?\n/);
|
|
462
|
+
}
|