@snappedly-tools/shipyard 0.9.2 → 0.10.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.
Files changed (46) hide show
  1. package/README.md +4 -2
  2. package/dist/{chunk-NQRFVKCU.js → chunk-2SEMX2NX.js} +2 -2
  3. package/dist/{chunk-NQRFVKCU.js.map → chunk-2SEMX2NX.js.map} +1 -1
  4. package/dist/{chunk-57EEKW3R.js → chunk-BAP7BWQ2.js} +17 -8
  5. package/dist/chunk-BAP7BWQ2.js.map +1 -0
  6. package/dist/{chunk-HXSZM52J.js → chunk-IVE7QDGC.js} +17 -11
  7. package/dist/chunk-IVE7QDGC.js.map +1 -0
  8. package/dist/{chunk-JZUBT4WG.js → chunk-KSTSHZNH.js} +3 -3
  9. package/dist/{chunk-JZUBT4WG.js.map → chunk-KSTSHZNH.js.map} +1 -1
  10. package/dist/{createSandbox-DmbnWAZv.d.ts → createSandbox-Bqopwday.d.ts} +6 -4
  11. package/dist/index.d.ts +2 -2
  12. package/dist/index.js +58 -13
  13. package/dist/index.js.map +1 -1
  14. package/dist/integrations/github.js +1 -1
  15. package/dist/main.js +26 -7
  16. package/dist/main.js.map +1 -1
  17. package/dist/sandboxes/docker.js +2 -2
  18. package/dist/templates/parallel-planner/block-scope.sh +6 -0
  19. package/dist/templates/parallel-planner/handoff.sh +7 -2
  20. package/dist/templates/parallel-planner/main.mts +25 -14
  21. package/dist/templates/parallel-planner/setup.sh +8 -1
  22. package/dist/templates/parallel-planner/triage-prompt.md +3 -1
  23. package/dist/templates/parallel-planner-with-review/block-scope.sh +6 -0
  24. package/dist/templates/parallel-planner-with-review/handoff.sh +7 -2
  25. package/dist/templates/parallel-planner-with-review/main.mts +26 -15
  26. package/dist/templates/parallel-planner-with-review/setup.sh +8 -1
  27. package/dist/templates/parallel-planner-with-review/triage-prompt.md +3 -1
  28. package/dist/templates/sequential-reviewer/block-scope.sh +6 -0
  29. package/dist/templates/sequential-reviewer/handoff.sh +7 -2
  30. package/dist/templates/sequential-reviewer/main.mts +21 -14
  31. package/dist/templates/sequential-reviewer/setup.sh +8 -1
  32. package/dist/templates/sequential-reviewer/triage-prompt.md +3 -1
  33. package/dist/templates/shared/block-scope.sh +6 -0
  34. package/dist/templates/shared/handoff.sh +7 -2
  35. package/dist/templates/shared/setup.sh +8 -1
  36. package/dist/templates/shared/triage-prompt.md +3 -1
  37. package/dist/templates/simple-loop/block-scope.sh +6 -0
  38. package/dist/templates/simple-loop/handoff.sh +7 -2
  39. package/dist/templates/simple-loop/main.mts +20 -13
  40. package/dist/templates/simple-loop/setup.sh +8 -1
  41. package/dist/templates/simple-loop/triage-prompt.md +3 -1
  42. package/dist/workflow.d.ts +1 -1
  43. package/dist/workflow.js +2 -2
  44. package/package.json +1 -1
  45. package/dist/chunk-57EEKW3R.js.map +0 -1
  46. package/dist/chunk-HXSZM52J.js.map +0 -1
@@ -1,4 +1,4 @@
1
- export { defaultImageName, docker } from '../chunk-JZUBT4WG.js';
2
- import '../chunk-57EEKW3R.js';
1
+ export { defaultImageName, docker } from '../chunk-KSTSHZNH.js';
2
+ import '../chunk-BAP7BWQ2.js';
3
3
  //# sourceMappingURL=docker.js.map
4
4
  //# sourceMappingURL=docker.js.map
@@ -108,6 +108,10 @@ Work was not handed off. To retry, resolve the problem, remove shipyard:blocked,
108
108
  fi
109
109
 
110
110
  if [[ "$branch" == "shipyard/spec-$root" ]]; then
111
+ if ! remove_issue_status "$root" ready-for-human; then
112
+ echo "Warning: could not clear spec triage status" >&2
113
+ status_synced=false
114
+ fi
111
115
  if ! remove_issue_status "$root" shipyard:outstanding-tasks; then
112
116
  echo "Warning: could not clear spec status" >&2
113
117
  status_synced=false
@@ -122,9 +126,11 @@ else
122
126
  fi
123
127
  if [[ "$branch" == "shipyard/spec-$root" ]]; then
124
128
  for affected_id in "${affected_ids[@]:1}"; do
129
+ remove_issue_status "$affected_id" ready-for-human || status=75
125
130
  remove_issue_status "$affected_id" shipyard:pending || status=75
126
131
  done
127
132
  else
133
+ remove_issue_status "$root" ready-for-human || status=75
128
134
  remove_issue_status "$root" shipyard:pending || status=75
129
135
  fi
130
136
 
@@ -103,6 +103,7 @@ for ((i=1; i<${#scope_ids[@]}; i++)); do
103
103
  echo "Could not mark ticket #${scope_ids[i]} complete; retry issue completion when GitHub is available" >&2
104
104
  exit 75
105
105
  fi
106
+ remove_issue_label "${scope_ids[i]}" ready-for-human
106
107
  remove_issue_label "${scope_ids[i]}" shipyard:blocked
107
108
  remove_issue_label "${scope_ids[i]}" shipyard:pending
108
109
  done
@@ -128,7 +129,11 @@ if ! gh label create "$status_label" --repo "$repo" --color "$status_color" --de
128
129
  echo "Warning: could not create $status_label" >&2
129
130
  status_synced=false
130
131
  fi
131
- if ! gh issue edit "$issue" --repo "$repo" --add-label "$status_label"; then
132
+ if gh issue edit "$issue" --repo "$repo" --add-label "$status_label"; then
133
+ if [[ "$status_label" == shipyard:complete ]]; then
134
+ remove_issue_label "$issue" ready-for-human
135
+ fi
136
+ else
132
137
  if [[ "$branch" == "shipyard/issue-$issue" ]]; then
133
138
  echo "Could not mark ticket #$issue complete; retry issue completion when GitHub is available" >&2
134
139
  exit 75
@@ -170,6 +175,6 @@ if [[ "$status_synced" == true ]]; then
170
175
  fi
171
176
  done
172
177
  else
173
- echo "Warning: status labels need reconciliation; retaining ticket activation" >&2
178
+ echo "Warning: status labels need reconciliation; add shipyard to the ticket to retry" >&2
174
179
  fi
175
180
  printf '%s\n' "$url"
@@ -27,24 +27,27 @@ const roleModels = {
27
27
  routine: readRoleModel("routine"),
28
28
  strong: readRoleModel("strong"),
29
29
  };
30
- const CODEX_REASONING_EFFORTS = shipyard.CODEX_REASONING_EFFORTS;
31
- type CodexReasoningEffort = shipyard.CodexReasoningEffort;
32
- const readCodexReasoningEffort = (
30
+ const REASONING_EFFORTS = shipyard.REASONING_EFFORTS;
31
+ type ReasoningEffort = shipyard.ReasoningEffort;
32
+ const readRoleReasoningEffort = (
33
33
  role: ModelRole,
34
- ): CodexReasoningEffort | undefined => {
35
- if (!CODEX_PROVIDER) return undefined;
36
- const envName = `SHIPYARD_CODEX_${role.toUpperCase()}_REASONING_EFFORT`;
37
- const effort = process.env[envName]?.trim();
34
+ ): ReasoningEffort | undefined => {
35
+ const envName = `SHIPYARD_${role.toUpperCase()}_REASONING_EFFORT`;
36
+ const legacyName = `SHIPYARD_CODEX_${role.toUpperCase()}_REASONING_EFFORT`;
37
+ const sharedEffort = process.env[envName]?.trim();
38
+ const effort =
39
+ sharedEffort ||
40
+ (CODEX_PROVIDER ? process.env[legacyName]?.trim() : undefined);
38
41
  if (!effort) return undefined;
39
- if (!(CODEX_REASONING_EFFORTS as readonly string[]).includes(effort))
42
+ if (!(REASONING_EFFORTS as readonly string[]).includes(effort))
40
43
  throw new Error(
41
- `${envName} must be one of ${CODEX_REASONING_EFFORTS.join(", ")}; received "${effort}"`,
44
+ `${sharedEffort ? envName : legacyName} must be one of ${REASONING_EFFORTS.join(", ")}; received "${effort}"`,
42
45
  );
43
- return effort as CodexReasoningEffort;
46
+ return effort as ReasoningEffort;
44
47
  };
45
48
  const roleEfforts = {
46
- routine: readCodexReasoningEffort("routine"),
47
- strong: readCodexReasoningEffort("strong"),
49
+ routine: readRoleReasoningEffort("routine"),
50
+ strong: readRoleReasoningEffort("strong"),
48
51
  };
49
52
  const readCodexRoleModel = (role: ModelRole, defaultModel: AgentModel) => {
50
53
  if (!CODEX_PROVIDER || typeof defaultModel === "string") return defaultModel;
@@ -166,6 +169,7 @@ const runWorker = async (scope: Scope, ticket: Ticket) => {
166
169
  baseBranch: scope.branch,
167
170
  sandbox: sandboxProvider,
168
171
  hooks,
172
+ copyToWorktree: [".shipyard/setup.sh"],
169
173
  });
170
174
  try {
171
175
  await sandbox.run({
@@ -173,7 +177,7 @@ const runWorker = async (scope: Scope, ticket: Ticket) => {
173
177
  maxIterations: 1,
174
178
  agent: roleAgent("routine", shipyard.CODEX_MODELS.routine),
175
179
  promptFile: "./.shipyard/triage-prompt.md",
176
- promptArgs: { TASK_ID: ticket.id },
180
+ promptArgs: { TASK_ID: ticket.id, BASE_BRANCH: targetBranch },
177
181
  });
178
182
  verifyTriage(ticket.id);
179
183
  const implementation = await sandbox.run({
@@ -212,6 +216,7 @@ for (let iteration = 0; iteration < 10; iteration++) {
212
216
  const plan = await shipyard.run({
213
217
  hooks,
214
218
  sandbox: sandboxProvider,
219
+ copyToWorktree: [".shipyard/setup.sh"],
215
220
  name: "planner",
216
221
  branchStrategy: { type: "branch", branch: plannerBranch },
217
222
  maxIterations: 1,
@@ -257,6 +262,8 @@ for (let iteration = 0; iteration < 10; iteration++) {
257
262
  repository,
258
263
  "--add-label",
259
264
  "shipyard:pending",
265
+ "--remove-label",
266
+ "shipyard",
260
267
  ]);
261
268
  if (
262
269
  scope.branch !==
@@ -271,6 +278,7 @@ for (let iteration = 0; iteration < 10; iteration++) {
271
278
  branch: scope.branch,
272
279
  sandbox: sandboxProvider,
273
280
  hooks,
281
+ copyToWorktree: [".shipyard/setup.sh"],
274
282
  });
275
283
  await closeClean(seed);
276
284
  const tickets = scope.tickets ?? [];
@@ -305,6 +313,7 @@ for (let iteration = 0; iteration < 10; iteration++) {
305
313
  branch: scope.branch,
306
314
  sandbox: sandboxProvider,
307
315
  hooks,
316
+ copyToWorktree: [".shipyard/setup.sh"],
308
317
  });
309
318
  try {
310
319
  for (const [index, outcome] of settled.entries()) {
@@ -430,6 +439,7 @@ for (let iteration = 0; iteration < 10; iteration++) {
430
439
  branch: scope.branch,
431
440
  sandbox: sandboxProvider,
432
441
  hooks,
442
+ copyToWorktree: [".shipyard/setup.sh"],
433
443
  });
434
444
  let handoffEvidence: string;
435
445
  try {
@@ -439,7 +449,7 @@ for (let iteration = 0; iteration < 10; iteration++) {
439
449
  maxIterations: 1,
440
450
  agent: roleAgent("routine", shipyard.CODEX_MODELS.routine),
441
451
  promptFile: "./.shipyard/triage-prompt.md",
442
- promptArgs: { TASK_ID: id },
452
+ promptArgs: { TASK_ID: id, BASE_BRANCH: targetBranch },
443
453
  });
444
454
  verifyTriage(id);
445
455
  const standalone = await integration.run({
@@ -477,6 +487,7 @@ for (let iteration = 0; iteration < 10; iteration++) {
477
487
  const publication = await shipyard.createSandbox({
478
488
  branch: scope.branch,
479
489
  sandbox: sandboxProvider,
490
+ copyToWorktree: [".shipyard/handoff.sh"],
480
491
  });
481
492
  try {
482
493
  const scopeIds = [
@@ -18,7 +18,14 @@ if [[ -f package.json ]]; then
18
18
  else manager=npm; fi
19
19
  fi
20
20
  case "$manager" in
21
- npm) npm install ;;
21
+ npm)
22
+ if [[ -f package-lock.json ]]; then
23
+ if ! npm ci; then
24
+ echo "npm ci failed; installing from package.json without changing package-lock.json" >&2
25
+ npm install --no-package-lock
26
+ fi
27
+ else npm install --no-package-lock
28
+ fi ;;
22
29
  pnpm) corepack pnpm install ;;
23
30
  yarn) corepack yarn install ;;
24
31
  bun)
@@ -1,5 +1,7 @@
1
1
  Triage GitHub issue #{{TASK_ID}} before Shipyard implementation.
2
2
 
3
- Follow `/triage` and the repository's `docs/agents/triage.md` label mapping. Read the full issue and comments, verify its claim against the codebase, then apply the appropriate category and state label and post the brief or triage notes required by the skill. The maintainer has authorized Shipyard to apply an evidence-based recommendation during this unattended intake. If a decision requires maintainer judgment, use `needs-triage` and record the question. Do not implement the issue or start a PR in this phase.
3
+ Follow `/triage` and the repository's triage label mapping. Read the full issue and comments. Verify the reported behavior against `origin/{{BASE_BRANCH}}`, the integration baseline. The checked-out task branch may contain unfinished changes from an earlier attempt. Do not close the issue as already complete based on the task branch or comments about that candidate.
4
+
5
+ Apply the appropriate category and state label and post the brief or triage notes required by the skill. The maintainer has authorized Shipyard to apply an evidence-based recommendation during this unattended intake. If a decision requires maintainer judgment, use `needs-triage` and record the question. Do not implement the issue or start a PR in this phase.
4
6
 
5
7
  Report the applied state and evidence. Shipyard will independently re-read GitHub labels and implement only an open issue carrying both `shipyard` and the sole triage state `ready-for-agent`.
@@ -108,6 +108,10 @@ Work was not handed off. To retry, resolve the problem, remove shipyard:blocked,
108
108
  fi
109
109
 
110
110
  if [[ "$branch" == "shipyard/spec-$root" ]]; then
111
+ if ! remove_issue_status "$root" ready-for-human; then
112
+ echo "Warning: could not clear spec triage status" >&2
113
+ status_synced=false
114
+ fi
111
115
  if ! remove_issue_status "$root" shipyard:outstanding-tasks; then
112
116
  echo "Warning: could not clear spec status" >&2
113
117
  status_synced=false
@@ -122,9 +126,11 @@ else
122
126
  fi
123
127
  if [[ "$branch" == "shipyard/spec-$root" ]]; then
124
128
  for affected_id in "${affected_ids[@]:1}"; do
129
+ remove_issue_status "$affected_id" ready-for-human || status=75
125
130
  remove_issue_status "$affected_id" shipyard:pending || status=75
126
131
  done
127
132
  else
133
+ remove_issue_status "$root" ready-for-human || status=75
128
134
  remove_issue_status "$root" shipyard:pending || status=75
129
135
  fi
130
136
 
@@ -103,6 +103,7 @@ for ((i=1; i<${#scope_ids[@]}; i++)); do
103
103
  echo "Could not mark ticket #${scope_ids[i]} complete; retry issue completion when GitHub is available" >&2
104
104
  exit 75
105
105
  fi
106
+ remove_issue_label "${scope_ids[i]}" ready-for-human
106
107
  remove_issue_label "${scope_ids[i]}" shipyard:blocked
107
108
  remove_issue_label "${scope_ids[i]}" shipyard:pending
108
109
  done
@@ -128,7 +129,11 @@ if ! gh label create "$status_label" --repo "$repo" --color "$status_color" --de
128
129
  echo "Warning: could not create $status_label" >&2
129
130
  status_synced=false
130
131
  fi
131
- if ! gh issue edit "$issue" --repo "$repo" --add-label "$status_label"; then
132
+ if gh issue edit "$issue" --repo "$repo" --add-label "$status_label"; then
133
+ if [[ "$status_label" == shipyard:complete ]]; then
134
+ remove_issue_label "$issue" ready-for-human
135
+ fi
136
+ else
132
137
  if [[ "$branch" == "shipyard/issue-$issue" ]]; then
133
138
  echo "Could not mark ticket #$issue complete; retry issue completion when GitHub is available" >&2
134
139
  exit 75
@@ -170,6 +175,6 @@ if [[ "$status_synced" == true ]]; then
170
175
  fi
171
176
  done
172
177
  else
173
- echo "Warning: status labels need reconciliation; retaining ticket activation" >&2
178
+ echo "Warning: status labels need reconciliation; add shipyard to the ticket to retry" >&2
174
179
  fi
175
180
  printf '%s\n' "$url"
@@ -27,24 +27,27 @@ const roleModels = {
27
27
  routine: readRoleModel("routine"),
28
28
  strong: readRoleModel("strong"),
29
29
  };
30
- const CODEX_REASONING_EFFORTS = shipyard.CODEX_REASONING_EFFORTS;
31
- type CodexReasoningEffort = shipyard.CodexReasoningEffort;
32
- const readCodexReasoningEffort = (
30
+ const REASONING_EFFORTS = shipyard.REASONING_EFFORTS;
31
+ type ReasoningEffort = shipyard.ReasoningEffort;
32
+ const readRoleReasoningEffort = (
33
33
  role: ModelRole,
34
- ): CodexReasoningEffort | undefined => {
35
- if (!CODEX_PROVIDER) return undefined;
36
- const envName = `SHIPYARD_CODEX_${role.toUpperCase()}_REASONING_EFFORT`;
37
- const effort = process.env[envName]?.trim();
34
+ ): ReasoningEffort | undefined => {
35
+ const envName = `SHIPYARD_${role.toUpperCase()}_REASONING_EFFORT`;
36
+ const legacyName = `SHIPYARD_CODEX_${role.toUpperCase()}_REASONING_EFFORT`;
37
+ const sharedEffort = process.env[envName]?.trim();
38
+ const effort =
39
+ sharedEffort ||
40
+ (CODEX_PROVIDER ? process.env[legacyName]?.trim() : undefined);
38
41
  if (!effort) return undefined;
39
- if (!(CODEX_REASONING_EFFORTS as readonly string[]).includes(effort))
42
+ if (!(REASONING_EFFORTS as readonly string[]).includes(effort))
40
43
  throw new Error(
41
- `${envName} must be one of ${CODEX_REASONING_EFFORTS.join(", ")}; received "${effort}"`,
44
+ `${sharedEffort ? envName : legacyName} must be one of ${REASONING_EFFORTS.join(", ")}; received "${effort}"`,
42
45
  );
43
- return effort as CodexReasoningEffort;
46
+ return effort as ReasoningEffort;
44
47
  };
45
48
  const roleEfforts = {
46
- routine: readCodexReasoningEffort("routine"),
47
- strong: readCodexReasoningEffort("strong"),
49
+ routine: readRoleReasoningEffort("routine"),
50
+ strong: readRoleReasoningEffort("strong"),
48
51
  };
49
52
  const readCodexRoleModel = (role: ModelRole, defaultModel: AgentModel) => {
50
53
  if (!CODEX_PROVIDER || typeof defaultModel === "string") return defaultModel;
@@ -144,7 +147,7 @@ const approved = (
144
147
  throw new Error(
145
148
  `${stage} has unresolved review findings: ${result.stdout.trim().slice(-1200)}`,
146
149
  );
147
- return complete(result, stage);
150
+ return `Review: APPROVED\n${complete(result, stage)}`;
148
151
  };
149
152
  class TicketFailures extends Error {
150
153
  constructor(readonly failures: Array<{ id: string; reason: string }>) {
@@ -176,6 +179,7 @@ const runWorker = async (scope: Scope, ticket: Ticket) => {
176
179
  baseBranch: scope.branch,
177
180
  sandbox: sandboxProvider,
178
181
  hooks,
182
+ copyToWorktree: [".shipyard/setup.sh"],
179
183
  });
180
184
  try {
181
185
  await sandbox.run({
@@ -183,7 +187,7 @@ const runWorker = async (scope: Scope, ticket: Ticket) => {
183
187
  maxIterations: 1,
184
188
  agent: roleAgent("routine", shipyard.CODEX_MODELS.routine),
185
189
  promptFile: "./.shipyard/triage-prompt.md",
186
- promptArgs: { TASK_ID: ticket.id },
190
+ promptArgs: { TASK_ID: ticket.id, BASE_BRANCH: targetBranch },
187
191
  });
188
192
  verifyTriage(ticket.id);
189
193
  const implementation = await sandbox.run({
@@ -235,6 +239,7 @@ for (let iteration = 0; iteration < 10; iteration++) {
235
239
  const plan = await shipyard.run({
236
240
  hooks,
237
241
  sandbox: sandboxProvider,
242
+ copyToWorktree: [".shipyard/setup.sh"],
238
243
  name: "planner",
239
244
  branchStrategy: { type: "branch", branch: plannerBranch },
240
245
  maxIterations: 1,
@@ -280,6 +285,8 @@ for (let iteration = 0; iteration < 10; iteration++) {
280
285
  repository,
281
286
  "--add-label",
282
287
  "shipyard:pending",
288
+ "--remove-label",
289
+ "shipyard",
283
290
  ]);
284
291
  if (
285
292
  scope.branch !==
@@ -294,6 +301,7 @@ for (let iteration = 0; iteration < 10; iteration++) {
294
301
  branch: scope.branch,
295
302
  sandbox: sandboxProvider,
296
303
  hooks,
304
+ copyToWorktree: [".shipyard/setup.sh"],
297
305
  });
298
306
  await closeClean(seed);
299
307
  const tickets = scope.tickets ?? [];
@@ -328,6 +336,7 @@ for (let iteration = 0; iteration < 10; iteration++) {
328
336
  branch: scope.branch,
329
337
  sandbox: sandboxProvider,
330
338
  hooks,
339
+ copyToWorktree: [".shipyard/setup.sh"],
331
340
  });
332
341
  try {
333
342
  for (const [index, outcome] of settled.entries()) {
@@ -453,6 +462,7 @@ for (let iteration = 0; iteration < 10; iteration++) {
453
462
  branch: scope.branch,
454
463
  sandbox: sandboxProvider,
455
464
  hooks,
465
+ copyToWorktree: [".shipyard/setup.sh"],
456
466
  });
457
467
  let handoffEvidence: string;
458
468
  try {
@@ -462,7 +472,7 @@ for (let iteration = 0; iteration < 10; iteration++) {
462
472
  maxIterations: 1,
463
473
  agent: roleAgent("routine", shipyard.CODEX_MODELS.routine),
464
474
  promptFile: "./.shipyard/triage-prompt.md",
465
- promptArgs: { TASK_ID: id },
475
+ promptArgs: { TASK_ID: id, BASE_BRANCH: targetBranch },
466
476
  });
467
477
  verifyTriage(id);
468
478
  const standalone = await integration.run({
@@ -528,6 +538,7 @@ for (let iteration = 0; iteration < 10; iteration++) {
528
538
  const publication = await shipyard.createSandbox({
529
539
  branch: scope.branch,
530
540
  sandbox: sandboxProvider,
541
+ copyToWorktree: [".shipyard/handoff.sh"],
531
542
  });
532
543
  try {
533
544
  const scopeIds = [
@@ -18,7 +18,14 @@ if [[ -f package.json ]]; then
18
18
  else manager=npm; fi
19
19
  fi
20
20
  case "$manager" in
21
- npm) npm install ;;
21
+ npm)
22
+ if [[ -f package-lock.json ]]; then
23
+ if ! npm ci; then
24
+ echo "npm ci failed; installing from package.json without changing package-lock.json" >&2
25
+ npm install --no-package-lock
26
+ fi
27
+ else npm install --no-package-lock
28
+ fi ;;
22
29
  pnpm) corepack pnpm install ;;
23
30
  yarn) corepack yarn install ;;
24
31
  bun)
@@ -1,5 +1,7 @@
1
1
  Triage GitHub issue #{{TASK_ID}} before Shipyard implementation.
2
2
 
3
- Follow `/triage` and the repository's `docs/agents/triage.md` label mapping. Read the full issue and comments, verify its claim against the codebase, then apply the appropriate category and state label and post the brief or triage notes required by the skill. The maintainer has authorized Shipyard to apply an evidence-based recommendation during this unattended intake. If a decision requires maintainer judgment, use `needs-triage` and record the question. Do not implement the issue or start a PR in this phase.
3
+ Follow `/triage` and the repository's triage label mapping. Read the full issue and comments. Verify the reported behavior against `origin/{{BASE_BRANCH}}`, the integration baseline. The checked-out task branch may contain unfinished changes from an earlier attempt. Do not close the issue as already complete based on the task branch or comments about that candidate.
4
+
5
+ Apply the appropriate category and state label and post the brief or triage notes required by the skill. The maintainer has authorized Shipyard to apply an evidence-based recommendation during this unattended intake. If a decision requires maintainer judgment, use `needs-triage` and record the question. Do not implement the issue or start a PR in this phase.
4
6
 
5
7
  Report the applied state and evidence. Shipyard will independently re-read GitHub labels and implement only an open issue carrying both `shipyard` and the sole triage state `ready-for-agent`.
@@ -108,6 +108,10 @@ Work was not handed off. To retry, resolve the problem, remove shipyard:blocked,
108
108
  fi
109
109
 
110
110
  if [[ "$branch" == "shipyard/spec-$root" ]]; then
111
+ if ! remove_issue_status "$root" ready-for-human; then
112
+ echo "Warning: could not clear spec triage status" >&2
113
+ status_synced=false
114
+ fi
111
115
  if ! remove_issue_status "$root" shipyard:outstanding-tasks; then
112
116
  echo "Warning: could not clear spec status" >&2
113
117
  status_synced=false
@@ -122,9 +126,11 @@ else
122
126
  fi
123
127
  if [[ "$branch" == "shipyard/spec-$root" ]]; then
124
128
  for affected_id in "${affected_ids[@]:1}"; do
129
+ remove_issue_status "$affected_id" ready-for-human || status=75
125
130
  remove_issue_status "$affected_id" shipyard:pending || status=75
126
131
  done
127
132
  else
133
+ remove_issue_status "$root" ready-for-human || status=75
128
134
  remove_issue_status "$root" shipyard:pending || status=75
129
135
  fi
130
136
 
@@ -103,6 +103,7 @@ for ((i=1; i<${#scope_ids[@]}; i++)); do
103
103
  echo "Could not mark ticket #${scope_ids[i]} complete; retry issue completion when GitHub is available" >&2
104
104
  exit 75
105
105
  fi
106
+ remove_issue_label "${scope_ids[i]}" ready-for-human
106
107
  remove_issue_label "${scope_ids[i]}" shipyard:blocked
107
108
  remove_issue_label "${scope_ids[i]}" shipyard:pending
108
109
  done
@@ -128,7 +129,11 @@ if ! gh label create "$status_label" --repo "$repo" --color "$status_color" --de
128
129
  echo "Warning: could not create $status_label" >&2
129
130
  status_synced=false
130
131
  fi
131
- if ! gh issue edit "$issue" --repo "$repo" --add-label "$status_label"; then
132
+ if gh issue edit "$issue" --repo "$repo" --add-label "$status_label"; then
133
+ if [[ "$status_label" == shipyard:complete ]]; then
134
+ remove_issue_label "$issue" ready-for-human
135
+ fi
136
+ else
132
137
  if [[ "$branch" == "shipyard/issue-$issue" ]]; then
133
138
  echo "Could not mark ticket #$issue complete; retry issue completion when GitHub is available" >&2
134
139
  exit 75
@@ -170,6 +175,6 @@ if [[ "$status_synced" == true ]]; then
170
175
  fi
171
176
  done
172
177
  else
173
- echo "Warning: status labels need reconciliation; retaining ticket activation" >&2
178
+ echo "Warning: status labels need reconciliation; add shipyard to the ticket to retry" >&2
174
179
  fi
175
180
  printf '%s\n' "$url"
@@ -22,24 +22,27 @@ const roleModels = {
22
22
  routine: readRoleModel("routine"),
23
23
  strong: readRoleModel("strong"),
24
24
  };
25
- const CODEX_REASONING_EFFORTS = shipyard.CODEX_REASONING_EFFORTS;
26
- type CodexReasoningEffort = shipyard.CodexReasoningEffort;
27
- const readCodexReasoningEffort = (
25
+ const REASONING_EFFORTS = shipyard.REASONING_EFFORTS;
26
+ type ReasoningEffort = shipyard.ReasoningEffort;
27
+ const readRoleReasoningEffort = (
28
28
  role: ModelRole,
29
- ): CodexReasoningEffort | undefined => {
30
- if (!CODEX_PROVIDER) return undefined;
31
- const envName = `SHIPYARD_CODEX_${role.toUpperCase()}_REASONING_EFFORT`;
32
- const effort = process.env[envName]?.trim();
29
+ ): ReasoningEffort | undefined => {
30
+ const envName = `SHIPYARD_${role.toUpperCase()}_REASONING_EFFORT`;
31
+ const legacyName = `SHIPYARD_CODEX_${role.toUpperCase()}_REASONING_EFFORT`;
32
+ const sharedEffort = process.env[envName]?.trim();
33
+ const effort =
34
+ sharedEffort ||
35
+ (CODEX_PROVIDER ? process.env[legacyName]?.trim() : undefined);
33
36
  if (!effort) return undefined;
34
- if (!(CODEX_REASONING_EFFORTS as readonly string[]).includes(effort))
37
+ if (!(REASONING_EFFORTS as readonly string[]).includes(effort))
35
38
  throw new Error(
36
- `${envName} must be one of ${CODEX_REASONING_EFFORTS.join(", ")}; received "${effort}"`,
39
+ `${sharedEffort ? envName : legacyName} must be one of ${REASONING_EFFORTS.join(", ")}; received "${effort}"`,
37
40
  );
38
- return effort as CodexReasoningEffort;
41
+ return effort as ReasoningEffort;
39
42
  };
40
43
  const roleEfforts = {
41
- routine: readCodexReasoningEffort("routine"),
42
- strong: readCodexReasoningEffort("strong"),
44
+ routine: readRoleReasoningEffort("routine"),
45
+ strong: readRoleReasoningEffort("strong"),
43
46
  };
44
47
  const readCodexRoleModel = (role: ModelRole, defaultModel: AgentModel) => {
45
48
  if (!CODEX_PROVIDER || typeof defaultModel === "string") return defaultModel;
@@ -176,11 +179,14 @@ for (let iteration = 0; iteration < MAX_ITERATIONS; iteration++) {
176
179
  repository,
177
180
  "--add-label",
178
181
  "shipyard:pending",
182
+ "--remove-label",
183
+ "shipyard",
179
184
  ]);
180
185
  const sandbox = await shipyard.createSandbox({
181
186
  branch: issue.branch,
182
187
  sandbox: sandboxProvider,
183
188
  hooks,
189
+ copyToWorktree: [".shipyard/setup.sh"],
184
190
  });
185
191
  let evidence: string;
186
192
  try {
@@ -192,7 +198,7 @@ for (let iteration = 0; iteration < MAX_ITERATIONS; iteration++) {
192
198
  agent: roleAgent("routine", shipyard.CODEX_MODELS.routine),
193
199
  maxIterations: 1,
194
200
  promptFile: "./.shipyard/triage-prompt.md",
195
- promptArgs: { TASK_ID: ticketId },
201
+ promptArgs: { TASK_ID: ticketId, BASE_BRANCH: targetBranch },
196
202
  });
197
203
  verifyTriage(ticketId);
198
204
  }
@@ -237,7 +243,7 @@ for (let iteration = 0; iteration < MAX_ITERATIONS; iteration++) {
237
243
  `Issue #${issue.id} has unresolved review findings: ${review.stdout.trim().slice(-1200)}`,
238
244
  );
239
245
  }
240
- evidence = `${implementationEvidence}\n\n${reviewEvidence}`;
246
+ evidence = `${implementationEvidence}\n\nReview: APPROVED\n${reviewEvidence}`;
241
247
  } finally {
242
248
  await closeClean(sandbox);
243
249
  }
@@ -245,6 +251,7 @@ for (let iteration = 0; iteration < MAX_ITERATIONS; iteration++) {
245
251
  const publication = await shipyard.createSandbox({
246
252
  branch: issue.branch,
247
253
  sandbox: sandboxProvider,
254
+ copyToWorktree: [".shipyard/handoff.sh"],
248
255
  });
249
256
  try {
250
257
  publicationUncertain = true;
@@ -18,7 +18,14 @@ if [[ -f package.json ]]; then
18
18
  else manager=npm; fi
19
19
  fi
20
20
  case "$manager" in
21
- npm) npm install ;;
21
+ npm)
22
+ if [[ -f package-lock.json ]]; then
23
+ if ! npm ci; then
24
+ echo "npm ci failed; installing from package.json without changing package-lock.json" >&2
25
+ npm install --no-package-lock
26
+ fi
27
+ else npm install --no-package-lock
28
+ fi ;;
22
29
  pnpm) corepack pnpm install ;;
23
30
  yarn) corepack yarn install ;;
24
31
  bun)
@@ -1,5 +1,7 @@
1
1
  Triage GitHub issue #{{TASK_ID}} before Shipyard implementation.
2
2
 
3
- Follow `/triage` and the repository's `docs/agents/triage.md` label mapping. Read the full issue and comments, verify its claim against the codebase, then apply the appropriate category and state label and post the brief or triage notes required by the skill. The maintainer has authorized Shipyard to apply an evidence-based recommendation during this unattended intake. If a decision requires maintainer judgment, use `needs-triage` and record the question. Do not implement the issue or start a PR in this phase.
3
+ Follow `/triage` and the repository's triage label mapping. Read the full issue and comments. Verify the reported behavior against `origin/{{BASE_BRANCH}}`, the integration baseline. The checked-out task branch may contain unfinished changes from an earlier attempt. Do not close the issue as already complete based on the task branch or comments about that candidate.
4
+
5
+ Apply the appropriate category and state label and post the brief or triage notes required by the skill. The maintainer has authorized Shipyard to apply an evidence-based recommendation during this unattended intake. If a decision requires maintainer judgment, use `needs-triage` and record the question. Do not implement the issue or start a PR in this phase.
4
6
 
5
7
  Report the applied state and evidence. Shipyard will independently re-read GitHub labels and implement only an open issue carrying both `shipyard` and the sole triage state `ready-for-agent`.
@@ -108,6 +108,10 @@ Work was not handed off. To retry, resolve the problem, remove shipyard:blocked,
108
108
  fi
109
109
 
110
110
  if [[ "$branch" == "shipyard/spec-$root" ]]; then
111
+ if ! remove_issue_status "$root" ready-for-human; then
112
+ echo "Warning: could not clear spec triage status" >&2
113
+ status_synced=false
114
+ fi
111
115
  if ! remove_issue_status "$root" shipyard:outstanding-tasks; then
112
116
  echo "Warning: could not clear spec status" >&2
113
117
  status_synced=false
@@ -122,9 +126,11 @@ else
122
126
  fi
123
127
  if [[ "$branch" == "shipyard/spec-$root" ]]; then
124
128
  for affected_id in "${affected_ids[@]:1}"; do
129
+ remove_issue_status "$affected_id" ready-for-human || status=75
125
130
  remove_issue_status "$affected_id" shipyard:pending || status=75
126
131
  done
127
132
  else
133
+ remove_issue_status "$root" ready-for-human || status=75
128
134
  remove_issue_status "$root" shipyard:pending || status=75
129
135
  fi
130
136
 
@@ -103,6 +103,7 @@ for ((i=1; i<${#scope_ids[@]}; i++)); do
103
103
  echo "Could not mark ticket #${scope_ids[i]} complete; retry issue completion when GitHub is available" >&2
104
104
  exit 75
105
105
  fi
106
+ remove_issue_label "${scope_ids[i]}" ready-for-human
106
107
  remove_issue_label "${scope_ids[i]}" shipyard:blocked
107
108
  remove_issue_label "${scope_ids[i]}" shipyard:pending
108
109
  done
@@ -128,7 +129,11 @@ if ! gh label create "$status_label" --repo "$repo" --color "$status_color" --de
128
129
  echo "Warning: could not create $status_label" >&2
129
130
  status_synced=false
130
131
  fi
131
- if ! gh issue edit "$issue" --repo "$repo" --add-label "$status_label"; then
132
+ if gh issue edit "$issue" --repo "$repo" --add-label "$status_label"; then
133
+ if [[ "$status_label" == shipyard:complete ]]; then
134
+ remove_issue_label "$issue" ready-for-human
135
+ fi
136
+ else
132
137
  if [[ "$branch" == "shipyard/issue-$issue" ]]; then
133
138
  echo "Could not mark ticket #$issue complete; retry issue completion when GitHub is available" >&2
134
139
  exit 75
@@ -170,6 +175,6 @@ if [[ "$status_synced" == true ]]; then
170
175
  fi
171
176
  done
172
177
  else
173
- echo "Warning: status labels need reconciliation; retaining ticket activation" >&2
178
+ echo "Warning: status labels need reconciliation; add shipyard to the ticket to retry" >&2
174
179
  fi
175
180
  printf '%s\n' "$url"
@@ -18,7 +18,14 @@ if [[ -f package.json ]]; then
18
18
  else manager=npm; fi
19
19
  fi
20
20
  case "$manager" in
21
- npm) npm install ;;
21
+ npm)
22
+ if [[ -f package-lock.json ]]; then
23
+ if ! npm ci; then
24
+ echo "npm ci failed; installing from package.json without changing package-lock.json" >&2
25
+ npm install --no-package-lock
26
+ fi
27
+ else npm install --no-package-lock
28
+ fi ;;
22
29
  pnpm) corepack pnpm install ;;
23
30
  yarn) corepack yarn install ;;
24
31
  bun)
@@ -1,5 +1,7 @@
1
1
  Triage GitHub issue #{{TASK_ID}} before Shipyard implementation.
2
2
 
3
- Follow `/triage` and the repository's `docs/agents/triage.md` label mapping. Read the full issue and comments, verify its claim against the codebase, then apply the appropriate category and state label and post the brief or triage notes required by the skill. The maintainer has authorized Shipyard to apply an evidence-based recommendation during this unattended intake. If a decision requires maintainer judgment, use `needs-triage` and record the question. Do not implement the issue or start a PR in this phase.
3
+ Follow `/triage` and the repository's triage label mapping. Read the full issue and comments. Verify the reported behavior against `origin/{{BASE_BRANCH}}`, the integration baseline. The checked-out task branch may contain unfinished changes from an earlier attempt. Do not close the issue as already complete based on the task branch or comments about that candidate.
4
+
5
+ Apply the appropriate category and state label and post the brief or triage notes required by the skill. The maintainer has authorized Shipyard to apply an evidence-based recommendation during this unattended intake. If a decision requires maintainer judgment, use `needs-triage` and record the question. Do not implement the issue or start a PR in this phase.
4
6
 
5
7
  Report the applied state and evidence. Shipyard will independently re-read GitHub labels and implement only an open issue carrying both `shipyard` and the sole triage state `ready-for-agent`.