@snappedly-tools/shipyard 0.5.0 → 0.6.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/README.md +56 -8
- package/dist/index.js +27 -9
- package/dist/index.js.map +1 -1
- package/dist/main.js +51 -15
- package/dist/main.js.map +1 -1
- package/dist/templates/parallel-planner/block-scope.sh +160 -0
- package/dist/templates/parallel-planner/conflict-prompt.md +22 -0
- package/dist/templates/parallel-planner/handoff.sh +175 -0
- package/dist/templates/parallel-planner/implement-prompt.md +10 -56
- package/dist/templates/parallel-planner/main.mts +434 -188
- package/dist/templates/parallel-planner/merge-prompt.md +9 -21
- package/dist/templates/parallel-planner/plan-prompt.md +5 -31
- package/dist/templates/parallel-planner/select-issues.mjs +788 -0
- package/dist/templates/parallel-planner/setup.sh +51 -0
- package/dist/templates/parallel-planner/spec-wave-prompt.md +16 -0
- package/dist/templates/parallel-planner/template.json +1 -1
- package/dist/templates/parallel-planner/triage-prompt.md +5 -0
- package/dist/templates/parallel-planner/verify-triage.sh +19 -0
- package/dist/templates/parallel-planner-with-review/block-scope.sh +160 -0
- package/dist/templates/parallel-planner-with-review/conflict-prompt.md +22 -0
- package/dist/templates/parallel-planner-with-review/handoff.sh +175 -0
- package/dist/templates/parallel-planner-with-review/implement-prompt.md +10 -56
- package/dist/templates/parallel-planner-with-review/main.mts +473 -205
- package/dist/templates/parallel-planner-with-review/merge-prompt.md +9 -21
- package/dist/templates/parallel-planner-with-review/plan-prompt.md +5 -31
- package/dist/templates/parallel-planner-with-review/review-prompt.md +11 -49
- package/dist/templates/parallel-planner-with-review/select-issues.mjs +788 -0
- package/dist/templates/parallel-planner-with-review/setup.sh +51 -0
- package/dist/templates/parallel-planner-with-review/spec-wave-prompt.md +16 -0
- package/dist/templates/parallel-planner-with-review/template.json +1 -1
- package/dist/templates/parallel-planner-with-review/triage-prompt.md +5 -0
- package/dist/templates/parallel-planner-with-review/verify-triage.sh +19 -0
- package/dist/templates/sequential-reviewer/block-scope.sh +160 -0
- package/dist/templates/sequential-reviewer/handoff.sh +175 -0
- package/dist/templates/sequential-reviewer/implement-prompt.md +12 -46
- package/dist/templates/sequential-reviewer/main.mts +199 -103
- package/dist/templates/sequential-reviewer/review-prompt.md +11 -49
- package/dist/templates/sequential-reviewer/select-issues.mjs +788 -0
- package/dist/templates/sequential-reviewer/setup.sh +51 -0
- package/dist/templates/sequential-reviewer/template.json +1 -1
- package/dist/templates/sequential-reviewer/triage-prompt.md +5 -0
- package/dist/templates/sequential-reviewer/verify-triage.sh +19 -0
- package/dist/templates/shared/block-scope.sh +160 -0
- package/dist/templates/shared/handoff.sh +175 -0
- package/dist/templates/shared/select-issues.mjs +788 -0
- package/dist/templates/shared/setup.sh +51 -0
- package/dist/templates/shared/triage-prompt.md +5 -0
- package/dist/templates/shared/verify-triage.sh +19 -0
- package/dist/templates/simple-loop/block-scope.sh +160 -0
- package/dist/templates/simple-loop/handoff.sh +175 -0
- package/dist/templates/simple-loop/main.mts +190 -45
- package/dist/templates/simple-loop/prompt.md +12 -46
- package/dist/templates/simple-loop/select-issues.mjs +788 -0
- package/dist/templates/simple-loop/setup.sh +51 -0
- package/dist/templates/simple-loop/template.json +1 -1
- package/dist/templates/simple-loop/triage-prompt.md +5 -0
- package/dist/templates/simple-loop/verify-triage.sh +19 -0
- package/package.json +1 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
root=${1:?scope root required}
|
|
5
|
+
failed=${2:?failed issue required}
|
|
6
|
+
repo=${3:?GitHub repository required}
|
|
7
|
+
scope=${4:?scope issue numbers required}
|
|
8
|
+
branch=${5:-shipyard/issue-$root}
|
|
9
|
+
[[ "$root" =~ ^[0-9]+$ && "$failed" =~ ^[0-9]+$ ]] || { echo "Invalid issue number" >&2; exit 1; }
|
|
10
|
+
[[ "$repo" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] || { echo "Invalid GitHub repository" >&2; exit 1; }
|
|
11
|
+
[[ "$scope" =~ ^[0-9]+(,[0-9]+)*$ && ( "$scope" == "$root" || "$scope" == "$root,"* ) && ",$scope," == *",$failed,"* ]] || {
|
|
12
|
+
echo "Invalid blocked issue scope" >&2; exit 1;
|
|
13
|
+
}
|
|
14
|
+
[[ "$branch" == "shipyard/issue-$root" || "$branch" == "shipyard/spec-$root" ]] || { echo "Invalid scope branch" >&2; exit 1; }
|
|
15
|
+
reason=$(cat)
|
|
16
|
+
[[ -n "$reason" ]] || { echo "Missing failure reason" >&2; exit 1; }
|
|
17
|
+
reason_hash=$(printf '%s' "$reason" | git hash-object --stdin)
|
|
18
|
+
pending_dir=$(git rev-parse --git-path shipyard-pending)
|
|
19
|
+
mkdir -p "$pending_dir"
|
|
20
|
+
umask 077
|
|
21
|
+
pending_file="$pending_dir/$root-$failed.pending"
|
|
22
|
+
trap 'rm -f "${pending_tmp:-}"' EXIT
|
|
23
|
+
confirmed=""
|
|
24
|
+
if [[ -f "$pending_file" ]]; then
|
|
25
|
+
IFS=$'\t' read -r old_root old_failed old_repo old_scope old_branch old_confirmed < "$pending_file"
|
|
26
|
+
if [[ "$old_root" == "$root" && "$old_failed" == "$failed" && "$old_repo" == "$repo" && "$old_scope" == "$scope" && "$old_branch" == "$branch" ]]; then
|
|
27
|
+
confirmed=${old_confirmed:-}
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
30
|
+
write_pending() {
|
|
31
|
+
pending_tmp=$(mktemp "$pending_file.XXXXXX")
|
|
32
|
+
printf '%s\t%s\t%s\t%s\t%s\t%s\n%s' "$root" "$failed" "$repo" "$scope" "$branch" "$confirmed" "$reason" > "$pending_tmp"
|
|
33
|
+
mv "$pending_tmp" "$pending_file"
|
|
34
|
+
}
|
|
35
|
+
confirm_blocked() {
|
|
36
|
+
if [[ ",$confirmed," != *",$1,"* ]]; then
|
|
37
|
+
confirmed=${confirmed:+$confirmed,}$1
|
|
38
|
+
write_pending
|
|
39
|
+
fi
|
|
40
|
+
}
|
|
41
|
+
write_pending
|
|
42
|
+
|
|
43
|
+
remove_issue_status() {
|
|
44
|
+
local labels
|
|
45
|
+
if gh issue edit "$1" --repo "$repo" --remove-label "$2"; then return 0; fi
|
|
46
|
+
labels=$(gh issue view "$1" --repo "$repo" --json labels --jq '.labels[].name') || return 75
|
|
47
|
+
if grep -Fxq "$2" <<< "$labels"; then
|
|
48
|
+
echo "Could not remove $2 from issue #$1" >&2
|
|
49
|
+
return 75
|
|
50
|
+
fi
|
|
51
|
+
}
|
|
52
|
+
remove_pr_status() {
|
|
53
|
+
local labels
|
|
54
|
+
if gh pr edit "$1" --repo "$repo" --remove-label "$2"; then return 0; fi
|
|
55
|
+
labels=$(gh pr view "$1" --repo "$repo" --json labels --jq '.labels[].name') || return 75
|
|
56
|
+
if grep -Fxq "$2" <<< "$labels"; then
|
|
57
|
+
echo "Could not remove $2 from PR #$1" >&2
|
|
58
|
+
return 75
|
|
59
|
+
fi
|
|
60
|
+
}
|
|
61
|
+
comment_once() {
|
|
62
|
+
local comments
|
|
63
|
+
if comments=$(gh issue view "$1" --repo "$repo" --json comments --jq '.comments[].body'); then
|
|
64
|
+
if grep -Fq "$3" <<< "$comments"; then return 0; fi
|
|
65
|
+
fi
|
|
66
|
+
gh issue comment "$1" --repo "$repo" --body "$2"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
gh label create shipyard:blocked --repo "$repo" --color B60205 --description 'Shipyard work needs intervention' --force
|
|
70
|
+
status_synced=true
|
|
71
|
+
IFS=',' read -ra affected_ids <<< "$scope"
|
|
72
|
+
if [[ "$branch" == "shipyard/spec-$root" ]]; then
|
|
73
|
+
for affected_id in "${affected_ids[@]:1}"; do
|
|
74
|
+
gh issue edit "$affected_id" --repo "$repo" --add-label shipyard:blocked
|
|
75
|
+
confirm_blocked "$affected_id"
|
|
76
|
+
done
|
|
77
|
+
if ! gh issue edit "$root" --repo "$repo" --add-label shipyard:blocked; then
|
|
78
|
+
echo "Warning: could not update spec #$root status" >&2
|
|
79
|
+
status_synced=false
|
|
80
|
+
fi
|
|
81
|
+
else
|
|
82
|
+
gh issue edit "$root" --repo "$repo" --add-label shipyard:blocked
|
|
83
|
+
confirm_blocked "$root"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
status=0
|
|
87
|
+
marker="shipyard:blocked:$root:$failed:$reason_hash"
|
|
88
|
+
comment_once "$failed" "Shipyard could not complete this issue. Reason: $reason
|
|
89
|
+
|
|
90
|
+
Work was not handed off. To retry, resolve the problem, remove shipyard:blocked, and add shipyard to the affected ticket.
|
|
91
|
+
|
|
92
|
+
<!-- $marker:failed -->" "$marker:failed" || status=75
|
|
93
|
+
if [[ "$root" != "$failed" ]]; then
|
|
94
|
+
comment_once "$root" "Shipyard paused this spec because linked ticket #$failed failed. Reason: $reason
|
|
95
|
+
|
|
96
|
+
Work was not handed off. To retry, resolve the problem, remove shipyard:blocked, and add shipyard to each unfinished ticket.
|
|
97
|
+
|
|
98
|
+
<!-- $marker:parent -->" "$marker:parent" || status=75
|
|
99
|
+
fi
|
|
100
|
+
if [[ "$branch" == "shipyard/spec-$root" && "$failed" == "$root" ]]; then
|
|
101
|
+
for affected_id in "${affected_ids[@]:1}"; do
|
|
102
|
+
comment_once "$affected_id" "Shipyard could not complete this ticket during the spec attempt. Reason: $reason
|
|
103
|
+
|
|
104
|
+
Work was not handed off. To retry, resolve the problem, remove shipyard:blocked, and add shipyard to the affected ticket.
|
|
105
|
+
|
|
106
|
+
<!-- $marker:ticket:$affected_id -->" "$marker:ticket:$affected_id" || status=75
|
|
107
|
+
done
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
if [[ "$branch" == "shipyard/spec-$root" ]]; then
|
|
111
|
+
if ! remove_issue_status "$root" shipyard:outstanding-tasks; then
|
|
112
|
+
echo "Warning: could not clear spec status" >&2
|
|
113
|
+
status_synced=false
|
|
114
|
+
fi
|
|
115
|
+
if ! remove_issue_status "$root" shipyard:complete; then
|
|
116
|
+
echo "Warning: could not clear spec status" >&2
|
|
117
|
+
status_synced=false
|
|
118
|
+
fi
|
|
119
|
+
else
|
|
120
|
+
remove_issue_status "$root" shipyard:outstanding-tasks || status=75
|
|
121
|
+
remove_issue_status "$root" shipyard:complete || status=75
|
|
122
|
+
fi
|
|
123
|
+
if [[ "$branch" == "shipyard/spec-$root" ]]; then
|
|
124
|
+
for affected_id in "${affected_ids[@]:1}"; do
|
|
125
|
+
remove_issue_status "$affected_id" shipyard:pending || status=75
|
|
126
|
+
done
|
|
127
|
+
else
|
|
128
|
+
remove_issue_status "$root" shipyard:pending || status=75
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
if ! pr_number=$(gh pr list --repo "$repo" --head "$branch" --state open --json number --jq '.[0].number // empty'); then
|
|
132
|
+
echo "Warning: could not inspect PR status" >&2
|
|
133
|
+
status_synced=false
|
|
134
|
+
pr_number=""
|
|
135
|
+
fi
|
|
136
|
+
if [[ -n "$pr_number" ]]; then
|
|
137
|
+
if ! gh pr edit "$pr_number" --repo "$repo" --add-label shipyard:blocked; then
|
|
138
|
+
echo "Warning: could not update PR status" >&2
|
|
139
|
+
status_synced=false
|
|
140
|
+
fi
|
|
141
|
+
if ! remove_pr_status "$pr_number" shipyard:outstanding-tasks; then
|
|
142
|
+
echo "Warning: could not clear PR status" >&2
|
|
143
|
+
status_synced=false
|
|
144
|
+
fi
|
|
145
|
+
if ! remove_pr_status "$pr_number" shipyard:complete; then
|
|
146
|
+
echo "Warning: could not clear PR status" >&2
|
|
147
|
+
status_synced=false
|
|
148
|
+
fi
|
|
149
|
+
fi
|
|
150
|
+
if [[ "$status" -eq 0 && "$status_synced" == true ]]; then
|
|
151
|
+
IFS=',' read -ra scope_ids <<< "$scope"
|
|
152
|
+
for scope_id in "${scope_ids[@]}"; do
|
|
153
|
+
labels=$(gh issue view "$scope_id" --repo "$repo" --json labels --jq '.labels[].name')
|
|
154
|
+
if grep -Fxq shipyard <<< "$labels"; then
|
|
155
|
+
gh issue edit "$scope_id" --repo "$repo" --remove-label shipyard
|
|
156
|
+
fi
|
|
157
|
+
done
|
|
158
|
+
rm -f "$pending_file"
|
|
159
|
+
fi
|
|
160
|
+
exit "$status"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Resolve a spec ticket integration conflict
|
|
2
|
+
|
|
3
|
+
Ticket #{{TASK_ID}} has a cherry-pick conflict while integrating commits `{{COMMITS}}` into spec branch `{{BRANCH}}`.
|
|
4
|
+
|
|
5
|
+
Follow `/implement-spec` as the assigned integrator. Read the parent and every scoped ticket, inspect `git status` and the conflicted files, and resolve the conflict while preserving both the ticket's requirements and changes already integrated into the spec branch. Continue the cherry-pick; do not abort or skip the ticket. If the cherry-pick includes multiple commits, continue until the sequence finishes. Run affected checks, commit the resolution, and leave the worktree clean. The selected workflow will then run its normal whole-spec integration and review stages.
|
|
6
|
+
|
|
7
|
+
The Git failure was:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
{{CONFLICT}}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
If the conflict cannot be resolved or checked, explain why and stop without a completion marker. After resolution, report `Checks: <commands and results>` and the integrated ticket:
|
|
14
|
+
|
|
15
|
+
<handoff>...</handoff>
|
|
16
|
+
<promise>COMPLETE</promise>
|
|
17
|
+
|
|
18
|
+
## Resolved spec scope
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{{SCOPE}}
|
|
22
|
+
```
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
issue=${1:?issue number required}
|
|
5
|
+
branch=${2:?branch required}
|
|
6
|
+
base=${3:?target branch required}
|
|
7
|
+
repo=${4:?GitHub repository required}
|
|
8
|
+
scope=${5:-$issue}
|
|
9
|
+
outstanding=${6:--}
|
|
10
|
+
previously_completed=${7:--}
|
|
11
|
+
[[ "$issue" =~ ^[0-9]+$ && ( "$branch" == "shipyard/issue-$issue" || "$branch" == "shipyard/spec-$issue" ) ]] || {
|
|
12
|
+
echo "Invalid issue or branch for PR handoff" >&2; exit 1;
|
|
13
|
+
}
|
|
14
|
+
[[ "$base" =~ ^[A-Za-z0-9._/-]+$ ]] || {
|
|
15
|
+
echo "Invalid target branch for PR handoff" >&2; exit 1;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
[[ "$scope" =~ ^[0-9]+(,[0-9]+)*$ && ( "$scope" == "$issue" || "$scope" == "$issue,"* ) ]] || { echo "Invalid PR scope" >&2; exit 1; }
|
|
19
|
+
[[ "$outstanding" == - || "$outstanding" =~ ^[0-9]+(,[0-9]+)*$ ]] || { echo "Invalid outstanding tickets" >&2; exit 1; }
|
|
20
|
+
[[ "$previously_completed" == - || "$previously_completed" =~ ^[0-9]+(,[0-9]+)*$ ]] || { echo "Invalid completed tickets" >&2; exit 1; }
|
|
21
|
+
evidence=$(cat)
|
|
22
|
+
[[ -n "$evidence" ]] || { echo "Missing verification evidence" >&2; exit 1; }
|
|
23
|
+
grep -Eiq '(^|[[:space:]])Checks:' <<< "$evidence" || { echo "Missing check evidence" >&2; exit 1; }
|
|
24
|
+
grep -Eiq '(^|[[:space:]])Review:' <<< "$evidence" || { echo "Missing review evidence" >&2; exit 1; }
|
|
25
|
+
[[ -z $(git status --porcelain) ]] || {
|
|
26
|
+
echo "Uncommitted changes prevent PR handoff" >&2; exit 1;
|
|
27
|
+
}
|
|
28
|
+
base_ref=$(git rev-parse --verify "refs/remotes/origin/$base^{commit}") || {
|
|
29
|
+
echo "Target branch is unavailable in the sandbox" >&2; exit 1;
|
|
30
|
+
}
|
|
31
|
+
branch_base=$base_ref
|
|
32
|
+
if ! git merge-base --is-ancestor "$base_ref" HEAD; then
|
|
33
|
+
branch_base=$(git merge-base "$base_ref" HEAD) || {
|
|
34
|
+
echo "PR branch has no common base with the target branch" >&2; exit 1;
|
|
35
|
+
}
|
|
36
|
+
fi
|
|
37
|
+
[[ -n $(git log "$branch_base"..HEAD --format=%H) ]] || {
|
|
38
|
+
echo "No verified commit to publish" >&2; exit 1;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[[ "$repo" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]] || { echo "Invalid GitHub repository" >&2; exit 1; }
|
|
42
|
+
remove_issue_label() {
|
|
43
|
+
local labels
|
|
44
|
+
if gh issue edit "$1" --repo "$repo" --remove-label "$2"; then return 0; fi
|
|
45
|
+
labels=$(gh issue view "$1" --repo "$repo" --json labels --jq '.labels[].name') || return 75
|
|
46
|
+
if grep -Fxq "$2" <<< "$labels"; then
|
|
47
|
+
echo "Could not remove $2 from issue #$1; retry handoff when GitHub is available" >&2
|
|
48
|
+
return 75
|
|
49
|
+
fi
|
|
50
|
+
}
|
|
51
|
+
remove_pr_label() {
|
|
52
|
+
local labels
|
|
53
|
+
if gh pr edit "$1" --repo "$repo" --remove-label "$2"; then return 0; fi
|
|
54
|
+
labels=$(gh pr view "$1" --repo "$repo" --json labels --jq '.labels[].name') || return 75
|
|
55
|
+
if grep -Fxq "$2" <<< "$labels"; then
|
|
56
|
+
echo "Could not remove $2 from PR #$1; retry handoff when GitHub is available" >&2
|
|
57
|
+
return 75
|
|
58
|
+
fi
|
|
59
|
+
}
|
|
60
|
+
title=$(gh issue view "$issue" --repo "$repo" --json title --jq .title)
|
|
61
|
+
body=$(mktemp)
|
|
62
|
+
trap 'rm -f "$body"' EXIT
|
|
63
|
+
links=""
|
|
64
|
+
implemented=""
|
|
65
|
+
IFS=',' read -ra scope_ids <<< "$scope"
|
|
66
|
+
for scope_id in "${scope_ids[@]}"; do links+="#$scope_id "; done
|
|
67
|
+
for ((i=1; i<${#scope_ids[@]}; i++)); do implemented+="#${scope_ids[i]} "; done
|
|
68
|
+
if [[ "$previously_completed" != - ]]; then
|
|
69
|
+
IFS=',' read -ra completed_ids <<< "$previously_completed"
|
|
70
|
+
for completed_id in "${completed_ids[@]}"; do links+="#$completed_id "; implemented+="#$completed_id "; done
|
|
71
|
+
fi
|
|
72
|
+
if [[ "$outstanding" != - ]]; then
|
|
73
|
+
IFS=',' read -ra outstanding_ids <<< "$outstanding"
|
|
74
|
+
for outstanding_id in "${outstanding_ids[@]}"; do links+="#$outstanding_id "; done
|
|
75
|
+
fi
|
|
76
|
+
printf 'Source issues: %s\nImplemented tickets: %s\n\n## Verification and review\n\n%s\n\nHuman review and merge required.\n\n<!-- shipyard:verified-handoff -->\n' "$links" "$implemented" "$evidence" > "$body"
|
|
77
|
+
|
|
78
|
+
# Keep Git credentials inside the disposable sandbox, not in the host worktree.
|
|
79
|
+
git remote set-url origin "https://github.com/$repo.git"
|
|
80
|
+
git -c credential.helper='!gh auth git-credential' push -u origin "HEAD:refs/heads/$branch"
|
|
81
|
+
number=$(gh pr list --repo "$repo" --head "$branch" --state open --json number --jq '.[0].number // empty')
|
|
82
|
+
if [[ -z "$number" ]]; then
|
|
83
|
+
gh pr create --repo "$repo" --head "$branch" --base "$base" --title "$title" --body-file "$body" --draft
|
|
84
|
+
number=$(gh pr list --repo "$repo" --head "$branch" --state open --json number --jq '.[0].number // empty')
|
|
85
|
+
fi
|
|
86
|
+
[[ -n "$number" ]] || { echo "PR publication did not return an open PR" >&2; exit 1; }
|
|
87
|
+
gh pr edit "$number" --repo "$repo" --body-file "$body"
|
|
88
|
+
draft=$(gh pr view "$number" --repo "$repo" --json isDraft --jq .isDraft)
|
|
89
|
+
url=$(gh pr view "$number" --repo "$repo" --json url --jq .url)
|
|
90
|
+
[[ -n "$url" ]] || { echo "PR publication did not return a URL" >&2; exit 1; }
|
|
91
|
+
if [[ "$draft" == true ]]; then gh pr ready "$number" --repo "$repo" || true; fi
|
|
92
|
+
if ! current_draft=$(gh pr view "$number" --repo "$repo" --json isDraft --jq .isDraft) ||
|
|
93
|
+
! current_state=$(gh pr view "$number" --repo "$repo" --json state --jq .state); then
|
|
94
|
+
echo "Could not confirm PR readiness; retry publication when GitHub is available" >&2
|
|
95
|
+
exit 75
|
|
96
|
+
fi
|
|
97
|
+
if [[ "$current_draft" != false || "$current_state" != OPEN ]]; then
|
|
98
|
+
echo "PR did not become ready for human review" >&2
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
for ((i=1; i<${#scope_ids[@]}; i++)); do
|
|
102
|
+
if ! gh issue edit "${scope_ids[i]}" --repo "$repo" --add-label shipyard:complete; then
|
|
103
|
+
echo "Could not mark ticket #${scope_ids[i]} complete; retry issue completion when GitHub is available" >&2
|
|
104
|
+
exit 75
|
|
105
|
+
fi
|
|
106
|
+
remove_issue_label "${scope_ids[i]}" shipyard:blocked
|
|
107
|
+
remove_issue_label "${scope_ids[i]}" shipyard:pending
|
|
108
|
+
done
|
|
109
|
+
status_label=shipyard:complete
|
|
110
|
+
status_color=0E8A16
|
|
111
|
+
status_description='Shipyard work ready for human review'
|
|
112
|
+
if [[ "$outstanding" != - ]]; then
|
|
113
|
+
status_label=shipyard:outstanding-tasks
|
|
114
|
+
status_color=FBCA04
|
|
115
|
+
status_description='Spec has uncompleted tickets'
|
|
116
|
+
for outstanding_id in "${outstanding_ids[@]}"; do
|
|
117
|
+
labels=$(gh issue view "$outstanding_id" --repo "$repo" --json labels --jq '.labels[].name') || exit 75
|
|
118
|
+
if grep -Fxq shipyard:blocked <<< "$labels" && ! grep -Fxq shipyard:complete <<< "$labels"; then
|
|
119
|
+
status_label=shipyard:blocked
|
|
120
|
+
status_color=B60205
|
|
121
|
+
status_description='Shipyard work needs intervention'
|
|
122
|
+
break
|
|
123
|
+
fi
|
|
124
|
+
done
|
|
125
|
+
fi
|
|
126
|
+
status_synced=true
|
|
127
|
+
if ! gh label create "$status_label" --repo "$repo" --color "$status_color" --description "$status_description" --force; then
|
|
128
|
+
echo "Warning: could not create $status_label" >&2
|
|
129
|
+
status_synced=false
|
|
130
|
+
fi
|
|
131
|
+
if ! gh issue edit "$issue" --repo "$repo" --add-label "$status_label"; then
|
|
132
|
+
if [[ "$branch" == "shipyard/issue-$issue" ]]; then
|
|
133
|
+
echo "Could not mark ticket #$issue complete; retry issue completion when GitHub is available" >&2
|
|
134
|
+
exit 75
|
|
135
|
+
fi
|
|
136
|
+
echo "Warning: could not update spec #$issue status" >&2
|
|
137
|
+
status_synced=false
|
|
138
|
+
fi
|
|
139
|
+
for stale_label in shipyard:complete shipyard:outstanding-tasks shipyard:blocked; do
|
|
140
|
+
if [[ "$stale_label" != "$status_label" ]]; then
|
|
141
|
+
if ! remove_issue_label "$issue" "$stale_label"; then
|
|
142
|
+
if [[ "$branch" == "shipyard/issue-$issue" ]]; then exit 75; fi
|
|
143
|
+
echo "Warning: could not clear $stale_label from spec #$issue" >&2
|
|
144
|
+
status_synced=false
|
|
145
|
+
fi
|
|
146
|
+
fi
|
|
147
|
+
done
|
|
148
|
+
if [[ "$branch" == "shipyard/issue-$issue" ]]; then remove_issue_label "$issue" shipyard:pending; fi
|
|
149
|
+
if ! gh pr edit "$number" --repo "$repo" --add-label "$status_label"; then
|
|
150
|
+
echo "Warning: could not update PR #$number status" >&2
|
|
151
|
+
status_synced=false
|
|
152
|
+
fi
|
|
153
|
+
for stale_label in shipyard:complete shipyard:outstanding-tasks shipyard:blocked; do
|
|
154
|
+
if [[ "$stale_label" != "$status_label" ]]; then
|
|
155
|
+
if ! remove_pr_label "$number" "$stale_label"; then
|
|
156
|
+
echo "Warning: could not clear $stale_label from PR #$number" >&2
|
|
157
|
+
status_synced=false
|
|
158
|
+
fi
|
|
159
|
+
fi
|
|
160
|
+
done
|
|
161
|
+
if [[ "$status_synced" == true ]]; then
|
|
162
|
+
for scope_id in "${scope_ids[@]}"; do
|
|
163
|
+
if ! labels=$(gh issue view "$scope_id" --repo "$repo" --json labels --jq '.labels[].name'); then
|
|
164
|
+
echo "Warning: could not inspect activation on issue #$scope_id; the next invocation will retry cleanup" >&2
|
|
165
|
+
continue
|
|
166
|
+
fi
|
|
167
|
+
if grep -Fxq shipyard <<< "$labels"; then
|
|
168
|
+
gh issue edit "$scope_id" --repo "$repo" --remove-label shipyard ||
|
|
169
|
+
echo "Warning: could not remove activation from issue #$scope_id; the next invocation will retry cleanup" >&2
|
|
170
|
+
fi
|
|
171
|
+
done
|
|
172
|
+
else
|
|
173
|
+
echo "Warning: status labels need reconciliation; retaining ticket activation" >&2
|
|
174
|
+
fi
|
|
175
|
+
printf '%s\n' "$url"
|
|
@@ -1,62 +1,16 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Assigned executable ticket #{{TASK_ID}}: {{ISSUE_TITLE}}
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Work only on branch `{{BRANCH}}`. Read `gh issue view {{TASK_ID}} --comments`, `AGENTS.md`, `CONTEXT.md`, `docs/agents/workflow.md`, and relevant ADRs.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Follow `/implement` as a ticket worker assigned by the existing parallel planner. Use `/tdd` for changed logic and local `/code-cleanup`; run focused tests, typechecking, and scoped formatting. Reinstall dependencies for the candidate with `bash .shipyard/setup.sh` after manifest changes. Commit and self-check. Return to the planner workflow: do not launch `/implement-spec`, another orchestration or review chain, merge, create a PR, or close this issue. The selected template's later stages own those steps.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
If incomplete, explain the blocker without a completion marker. Once the ticket has a verified commit, give scope, `Checks: <commands and results>`, and limitations:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
<handoff>...</handoff>
|
|
10
|
+
<promise>COMPLETE</promise>
|
|
10
11
|
|
|
11
|
-
#
|
|
12
|
+
For a child assignment, parent/spec scope is below. Read the parent and sibling tickets for context, but implement only assigned ticket #{{TASK_ID}}. The whole-spec integration stage owns `/implement-spec`.
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
!`git log -n 10 --format="%H%n%ad%n%B---" --date=short`
|
|
18
|
-
|
|
19
|
-
</recent-commits>
|
|
20
|
-
|
|
21
|
-
# EXPLORATION
|
|
22
|
-
|
|
23
|
-
Explore the repo and fill your context window with relevant information that will allow you to complete the task.
|
|
24
|
-
|
|
25
|
-
Pay extra attention to test files that touch the relevant parts of the code.
|
|
26
|
-
|
|
27
|
-
# EXECUTION
|
|
28
|
-
|
|
29
|
-
If applicable, use RGR to complete the task.
|
|
30
|
-
|
|
31
|
-
1. RED: write one test
|
|
32
|
-
2. GREEN: write the implementation to pass that test
|
|
33
|
-
3. REPEAT until done
|
|
34
|
-
4. REFACTOR the code
|
|
35
|
-
|
|
36
|
-
# FEEDBACK LOOPS
|
|
37
|
-
|
|
38
|
-
Before committing, read the repository's configured feedback-loop contract and run every applicable check for this change. Use the configured static check and focused behavior tests when they exist; include formatting, build, or broader checks when the contract or change requires them. Fix failures before committing.
|
|
39
|
-
|
|
40
|
-
# COMMIT
|
|
41
|
-
|
|
42
|
-
Make a git commit. The commit message must:
|
|
43
|
-
|
|
44
|
-
1. Start with `RALPH:` prefix
|
|
45
|
-
2. Include task completed + PRD reference
|
|
46
|
-
3. Key decisions made
|
|
47
|
-
4. Files changed
|
|
48
|
-
5. Blockers or notes for next iteration
|
|
49
|
-
|
|
50
|
-
Keep it concise.
|
|
51
|
-
|
|
52
|
-
# THE ISSUE
|
|
53
|
-
|
|
54
|
-
If the task is not complete, leave a comment on the issue with what was done.
|
|
55
|
-
|
|
56
|
-
Do not close the issue - this will be done later.
|
|
57
|
-
|
|
58
|
-
Once complete, output <promise>COMPLETE</promise>.
|
|
59
|
-
|
|
60
|
-
# FINAL RULES
|
|
61
|
-
|
|
62
|
-
ONLY WORK ON A SINGLE TASK.
|
|
14
|
+
```json
|
|
15
|
+
{{SCOPE}}
|
|
16
|
+
```
|