@plainconceptsplatform/workflows 1.6.16 → 1.6.23
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/loops/actions/verify-route-matrix/verify-route-matrix.sh +103 -0
- package/loops/templates/opencode/opencode.ci.json +117 -0
- package/loops/workflows/agent-apply-review.md +12 -0
- package/loops/workflows/agent-audit.md +8 -0
- package/loops/workflows/agent-implement.md +57 -4
- package/loops/workflows/agent-merge-gate.md +8 -0
- package/loops/workflows/agent-refine.md +12 -0
- package/loops/workflows/agent-release.md +8 -0
- package/loops/workflows/agent-triage.md +12 -0
- package/loops/workflows/shared/opencode-ci.md +46 -1
- package/loops/workflows/work-router.yml +22 -2
- package/package.json +1 -1
|
@@ -1788,6 +1788,63 @@ fi
|
|
|
1788
1788
|
# leaving a verdict or an attempt is a loop: the train cannot tell a gate that declined from a gate
|
|
1789
1789
|
# that has not run. Refusals are for what the train never hands over (a fork, a stranger, a pull
|
|
1790
1790
|
# request that is not open), and the train filters those itself.
|
|
1791
|
+
# The implement run is mostly model turns and repeated verification: Pliny-Bot #694 reached its
|
|
1792
|
+
# full verification at minute forty-one after six scoped runs, and #751 spent fifteen of twenty-two
|
|
1793
|
+
# minutes in twelve tiny worker tasks. These sentences are what keep a run near twenty minutes.
|
|
1794
|
+
if worker_installed implement; then
|
|
1795
|
+
if grep -qF 'Run them once, on the final tree.' "$IMPLEMENT_WORKER_MD" && grep -qF 'on both paths, before the commands above.' "$IMPLEMENT_WORKER_MD" && grep -qF 'Ask it for few, coarse tasks' "$IMPLEMENT_WORKER_MD" && grep -qF 'WORK IN FEW TURNS' "$IMPLEMENT_WORKER_MD"; then
|
|
1796
|
+
PASS=$((PASS + 1))
|
|
1797
|
+
else
|
|
1798
|
+
FAIL=$((FAIL + 1))
|
|
1799
|
+
echo "FAIL: the implement prompt must verify once on the final tree, ask for coarse tasks and work in few turns" >&2
|
|
1800
|
+
fi
|
|
1801
|
+
fi
|
|
1802
|
+
# gh-aw leaves OPENCODE_MODEL empty, so every lead ran on opencode.ci.json's one model. Each worker
|
|
1803
|
+
# names its own in AGENT_LEAD_MODEL, the merge step writes it onto ci-workflow-agent, and the model
|
|
1804
|
+
# it names, like every tier model in harness.json, has to be declared in opencode.ci.json or the
|
|
1805
|
+
# run dies at its first turn on "model not found".
|
|
1806
|
+
LEAD_OK=1
|
|
1807
|
+
# A consumer has it at its root; this package keeps the template beside the workflows.
|
|
1808
|
+
CI_JSON="${HERE}/../../../opencode.ci.json"
|
|
1809
|
+
[ -f "$CI_JSON" ] || CI_JSON="${HERE}/../../templates/opencode/opencode.ci.json"
|
|
1810
|
+
for route in refine implement triage apply-review merge-gate audit release; do
|
|
1811
|
+
worker_md="${WORKFLOWS_DIR}/agent-${route}.md"
|
|
1812
|
+
[ -f "$worker_md" ] || continue
|
|
1813
|
+
lead=$(sed -n 's/^ AGENT_LEAD_MODEL: "\(.*\)"$/\1/p' "$worker_md")
|
|
1814
|
+
if [ -z "$lead" ]; then
|
|
1815
|
+
LEAD_OK=0; echo "FAIL: agent-${route} names no AGENT_LEAD_MODEL, so its session runs on the cheap default" >&2; continue
|
|
1816
|
+
fi
|
|
1817
|
+
if [ -f "$CI_JSON" ] && ! jq -e --arg p "${lead%%/*}" --arg m "${lead#*/}" '.providers[$p].models[$m]' "$CI_JSON" > /dev/null; then
|
|
1818
|
+
LEAD_OK=0; echo "FAIL: agent-${route} leads on ${lead}, which opencode.ci.json does not declare" >&2
|
|
1819
|
+
fi
|
|
1820
|
+
done
|
|
1821
|
+
if [ -f "${WORKFLOWS_DIR}/agent-refine.md" ] && grep -q '^ AGENT_LEAD_MODEL: "forge/carina' "${WORKFLOWS_DIR}/agent-refine.md"; then
|
|
1822
|
+
LEAD_OK=0; echo "FAIL: refine must never lead on carina" >&2
|
|
1823
|
+
fi
|
|
1824
|
+
if ! grep -qF 'jq --arg model "$AGENT_LEAD_MODEL"' "${WORKFLOWS_DIR}/shared/opencode-ci.md"; then
|
|
1825
|
+
LEAD_OK=0; echo "FAIL: the CI config merge must write AGENT_LEAD_MODEL onto ci-workflow-agent" >&2
|
|
1826
|
+
fi
|
|
1827
|
+
if worker_installed implement && ! grep -qF 'subagent_type: planner' "$IMPLEMENT_WORKER_MD"; then
|
|
1828
|
+
LEAD_OK=0; echo "FAIL: the implement prompt must run propose and archive in the planner agent" >&2
|
|
1829
|
+
fi
|
|
1830
|
+
if [ "$LEAD_OK" -eq 1 ]; then PASS=$((PASS + 1)); else FAIL=$((FAIL + 1)); fi
|
|
1831
|
+
# The harness plugins only load with .opencode/node_modules installed; without it every CI run
|
|
1832
|
+
# spawned untiered workers on the session's model. And the tier model a worker pins has to be one
|
|
1833
|
+
# opencode.ci.json declares, under the provider name harness.json uses.
|
|
1834
|
+
PLUGINS_OK=1
|
|
1835
|
+
if ! grep -qF "(cd .opencode && npm install" "${WORKFLOWS_DIR}/shared/opencode-ci.md"; then
|
|
1836
|
+
PLUGINS_OK=0; echo "FAIL: the CI setup must install .opencode's dependencies or the harness plugins never load" >&2
|
|
1837
|
+
fi
|
|
1838
|
+
for route in refine implement triage apply-review merge-gate audit release; do
|
|
1839
|
+
worker_md="${WORKFLOWS_DIR}/agent-${route}.md"
|
|
1840
|
+
[ -f "$worker_md" ] || continue
|
|
1841
|
+
tier=$(sed -n 's/^ AGENT_TIER_MODEL: "\(.*\)"$/\1/p' "$worker_md")
|
|
1842
|
+
[ -n "$tier" ] || continue
|
|
1843
|
+
if [ -f "$CI_JSON" ] && ! jq -e --arg p "${tier%%/*}" --arg m "${tier#*/}" '.providers[$p].models[$m]' "$CI_JSON" > /dev/null; then
|
|
1844
|
+
PLUGINS_OK=0; echo "FAIL: agent-${route} pins its tiers to ${tier}, which opencode.ci.json does not declare" >&2
|
|
1845
|
+
fi
|
|
1846
|
+
done
|
|
1847
|
+
if [ "$PLUGINS_OK" -eq 1 ]; then PASS=$((PASS + 1)); else FAIL=$((FAIL + 1)); fi
|
|
1791
1848
|
# The shape of the tests reaches the agent only through these two prompts (check-diff measures size,
|
|
1792
1849
|
# never shape), so losing the sentence silently brings the pyramid back.
|
|
1793
1850
|
if worker_installed implement; then
|
|
@@ -2695,6 +2752,52 @@ if [ -f "$HK_YML" ] && [ -f "$TRAIN_SH_HB" ]; then
|
|
|
2695
2752
|
echo "FAIL: the merge train has no heartbeat from housekeeping, or does not cancel a repair gate whose CI turned green" >&2
|
|
2696
2753
|
fi
|
|
2697
2754
|
fi
|
|
2755
|
+
# The implement queue was the issue's key modulo the number of queues, so three independent issues whose
|
|
2756
|
+
# numbers were multiples of three (Pliny-Bot #726, #750 and #694 under #690) shared queue zero and ran in
|
|
2757
|
+
# series behind a three-hour run. The queue is now the key's place among the keys of the issues waiting for
|
|
2758
|
+
# an implement, and the children of one refine still share a key.
|
|
2759
|
+
if grep -qF "implement-queue-" "$ROUTER_YML" 2>/dev/null; then
|
|
2760
|
+
if grep -qF 'place=$index' "$ROUTER_YML" && grep -qF 'queue=$(( place % queues ))' "$ROUTER_YML" && ! grep -qF 'queue=$(( key % queues ))' "$ROUTER_YML"; then
|
|
2761
|
+
PASS=$((PASS + 1))
|
|
2762
|
+
else
|
|
2763
|
+
FAIL=$((FAIL + 1))
|
|
2764
|
+
echo "FAIL: the implement queue is chosen by the issue number modulo the queue count, which puts independent issues in one queue" >&2
|
|
2765
|
+
fi
|
|
2766
|
+
fi
|
|
2767
|
+
# The runner VM kills any job that outlives RUNNER_JOB_TIMEOUT, whatever the workflow allows. The default
|
|
2768
|
+
# of 90 minutes killed three Pliny-Bot implements that the workflow had given 180: no log, no pull request.
|
|
2769
|
+
# The cap in cloud-init has to stay above the longest agent job, or the workflow's own timeout is decoration.
|
|
2770
|
+
CLOUD_INIT_YML="${HERE}/../../../runners/cloud-init.yaml"
|
|
2771
|
+
if [ -f "$CLOUD_INIT_YML" ] && [ -f "${WORKFLOWS_DIR}/agent-implement.md" ]; then
|
|
2772
|
+
cap="$(sed -n 's/^ *RUNNER_JOB_TIMEOUT=\([0-9][0-9]*\)$/\1/p' "$CLOUD_INIT_YML" | tail -1)"
|
|
2773
|
+
longest="$(grep -hoE '^timeout-minutes: [0-9]+' "${WORKFLOWS_DIR}"/agent-*.md | grep -oE '[0-9]+' | sort -n | tail -1)"
|
|
2774
|
+
if [ -n "$cap" ] && [ -n "$longest" ] && [ "$cap" -gt $((longest * 60)) ]; then
|
|
2775
|
+
PASS=$((PASS + 1))
|
|
2776
|
+
else
|
|
2777
|
+
FAIL=$((FAIL + 1))
|
|
2778
|
+
echo "FAIL: the runner job cap (${cap:-unset} s in cloud-init.yaml) is not above the longest agent timeout (${longest:-?} min); a long agent job would be killed by the VM" >&2
|
|
2779
|
+
fi
|
|
2780
|
+
fi
|
|
2781
|
+
# decide-agent-retry reads why an agent job died from the job's annotations, and the annotations API needs
|
|
2782
|
+
# `checks: read`. Without it the read fails quietly (`2>/dev/null || true`), a runner shut down mid-job
|
|
2783
|
+
# (exit code 143) reads as no cause, and the issue is parked for a person instead of being run again:
|
|
2784
|
+
# Pliny-Bot #750 died at 44 minutes with exactly that annotation and was flagged for review.
|
|
2785
|
+
RETRY_PERM_OK=1
|
|
2786
|
+
for worker in "${WORKFLOWS_DIR}"/agent-*.md; do
|
|
2787
|
+
[ -f "$worker" ] || continue
|
|
2788
|
+
grep -q "decide-agent-retry" "$worker" || continue
|
|
2789
|
+
missing="$(awk '
|
|
2790
|
+
function flush() { if (job != "" && uses && !checks) printf "%s ", job }
|
|
2791
|
+
/^ [a-z_]+:$/ { flush(); job = $1; uses = 0; checks = 0; next }
|
|
2792
|
+
/decide-agent-retry/ { uses = 1 }
|
|
2793
|
+
/^ checks: read$/ { checks = 1 }
|
|
2794
|
+
END { flush() }' "$worker")"
|
|
2795
|
+
if [ -n "${missing// /}" ]; then
|
|
2796
|
+
RETRY_PERM_OK=0
|
|
2797
|
+
echo "FAIL: ${worker##*/}: job(s) ${missing}use decide-agent-retry without checks: read, so a runner shutdown reads as no cause" >&2
|
|
2798
|
+
fi
|
|
2799
|
+
done
|
|
2800
|
+
if [ "$RETRY_PERM_OK" -eq 1 ]; then PASS=$((PASS + 1)); else FAIL=$((FAIL + 1)); fi
|
|
2698
2801
|
# The implement prompt tells the agent it is already on its branch and to skip the pc-plan-goal
|
|
2699
2802
|
# branching phase. That is only true if a step before the agent made the branch, and nothing in the
|
|
2700
2803
|
# sandbox can do it later: no git credentials, and the prompt forbids the commands. Without the
|
|
@@ -44,7 +44,61 @@
|
|
|
44
44
|
"apiKey": "{env:OPENAI_API_KEY}"
|
|
45
45
|
},
|
|
46
46
|
"models": {
|
|
47
|
+
"carina-60b": {
|
|
48
|
+
"name": "Carina 60B",
|
|
49
|
+
"capabilities": {
|
|
50
|
+
"tools": true,
|
|
51
|
+
"input": [
|
|
52
|
+
"text",
|
|
53
|
+
"image"
|
|
54
|
+
],
|
|
55
|
+
"output": [
|
|
56
|
+
"text"
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"limit": {
|
|
60
|
+
"context": 1048576,
|
|
61
|
+
"input": 1048576,
|
|
62
|
+
"output": 131072
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"quasar-438b": {
|
|
66
|
+
"name": "Quasar 438B",
|
|
67
|
+
"capabilities": {
|
|
68
|
+
"tools": true,
|
|
69
|
+
"input": [
|
|
70
|
+
"text",
|
|
71
|
+
"image"
|
|
72
|
+
],
|
|
73
|
+
"output": [
|
|
74
|
+
"text"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"limit": {
|
|
78
|
+
"context": 1048576,
|
|
79
|
+
"input": 1048576,
|
|
80
|
+
"output": 131072
|
|
81
|
+
}
|
|
82
|
+
},
|
|
47
83
|
"glm-5-2": {
|
|
84
|
+
"name": "GLM 5.2",
|
|
85
|
+
"capabilities": {
|
|
86
|
+
"tools": true,
|
|
87
|
+
"input": [
|
|
88
|
+
"text",
|
|
89
|
+
"image"
|
|
90
|
+
],
|
|
91
|
+
"output": [
|
|
92
|
+
"text"
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
"limit": {
|
|
96
|
+
"context": 1048576,
|
|
97
|
+
"input": 1048576,
|
|
98
|
+
"output": 131072
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"glm-5-3": {
|
|
48
102
|
"name": "GLM 5.3",
|
|
49
103
|
"capabilities": {
|
|
50
104
|
"tools": true,
|
|
@@ -61,8 +115,53 @@
|
|
|
61
115
|
"input": 1048576,
|
|
62
116
|
"output": 131072
|
|
63
117
|
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"plainconcepts": {
|
|
122
|
+
"package": "aisdk:@ai-sdk/openai-compatible",
|
|
123
|
+
"settings": {
|
|
124
|
+
"baseURL": "{env:OPENAI_BASE_URL}",
|
|
125
|
+
"apiKey": "{env:OPENAI_API_KEY}"
|
|
126
|
+
},
|
|
127
|
+
"models": {
|
|
128
|
+
"carina-60b": {
|
|
129
|
+
"name": "Carina 60B",
|
|
130
|
+
"capabilities": {
|
|
131
|
+
"tools": true,
|
|
132
|
+
"input": [
|
|
133
|
+
"text",
|
|
134
|
+
"image"
|
|
135
|
+
],
|
|
136
|
+
"output": [
|
|
137
|
+
"text"
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
"limit": {
|
|
141
|
+
"context": 1048576,
|
|
142
|
+
"input": 1048576,
|
|
143
|
+
"output": 131072
|
|
144
|
+
}
|
|
64
145
|
},
|
|
65
146
|
"quasar-438b": {
|
|
147
|
+
"name": "Quasar 438B",
|
|
148
|
+
"capabilities": {
|
|
149
|
+
"tools": true,
|
|
150
|
+
"input": [
|
|
151
|
+
"text",
|
|
152
|
+
"image"
|
|
153
|
+
],
|
|
154
|
+
"output": [
|
|
155
|
+
"text"
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
"limit": {
|
|
159
|
+
"context": 1048576,
|
|
160
|
+
"input": 1048576,
|
|
161
|
+
"output": 131072
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
"glm-5-2": {
|
|
66
165
|
"name": "GLM 5.2",
|
|
67
166
|
"capabilities": {
|
|
68
167
|
"tools": true,
|
|
@@ -79,6 +178,24 @@
|
|
|
79
178
|
"input": 1048576,
|
|
80
179
|
"output": 131072
|
|
81
180
|
}
|
|
181
|
+
},
|
|
182
|
+
"glm-5-3": {
|
|
183
|
+
"name": "GLM 5.3",
|
|
184
|
+
"capabilities": {
|
|
185
|
+
"tools": true,
|
|
186
|
+
"input": [
|
|
187
|
+
"text",
|
|
188
|
+
"image"
|
|
189
|
+
],
|
|
190
|
+
"output": [
|
|
191
|
+
"text"
|
|
192
|
+
]
|
|
193
|
+
},
|
|
194
|
+
"limit": {
|
|
195
|
+
"context": 1048576,
|
|
196
|
+
"input": 1048576,
|
|
197
|
+
"output": 131072
|
|
198
|
+
}
|
|
82
199
|
}
|
|
83
200
|
}
|
|
84
201
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
# Managed by @plainconceptsplatform/workflows. Source: loops/workflows/agent-apply-review.md. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
3
3
|
env:
|
|
4
|
+
# The model this worker's own session runs on. opencode.ci.json's `model` is the cheap default
|
|
5
|
+
# every agent without its own model falls back to; the lead is where a wrong call costs a run,
|
|
6
|
+
# so it names its model here. `model:` below only satisfies the compiler. A repository may
|
|
7
|
+
# change it to any model its opencode.ci.json declares.
|
|
8
|
+
AGENT_LEAD_MODEL: "forge/glm-5-2"
|
|
9
|
+
# The model every harness tier (plan, build, fast) runs on in CI, planner included. Unset,
|
|
10
|
+
# the tiers take harness.json, whose models the AWF proxy may not price.
|
|
11
|
+
AGENT_TIER_MODEL: "plainconcepts/glm-5-2"
|
|
4
12
|
# Printed by the prompt as the verification block. Consumers set their own commands.
|
|
5
13
|
VERIFY_COMMANDS: ""
|
|
6
14
|
REPO_RULES: "Apply only actionable outstanding reviewer feedback to the selected bot pull request. Make minimal changes that address each comment. Preserve architecture and do not weaken tests. Run full verification after changes."
|
|
@@ -307,6 +315,10 @@ jobs:
|
|
|
307
315
|
issues: write
|
|
308
316
|
# the retry re-enters through the router, which is a workflow_dispatch
|
|
309
317
|
actions: write
|
|
318
|
+
# decide-agent-retry reads why the agent job died from its annotations, and the annotations API needs this scope.
|
|
319
|
+
# Without it the read fails quietly, a runner shut down mid-job (exit code 143) reads as no cause,
|
|
320
|
+
# and the issue is parked for a person instead of being run again (Pliny-Bot #750).
|
|
321
|
+
checks: read
|
|
310
322
|
steps:
|
|
311
323
|
- name: Checkout workflow actions
|
|
312
324
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
# Managed by @plainconceptsplatform/workflows. Source: loops/workflows/agent-audit.md. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
3
3
|
env:
|
|
4
|
+
# The model this worker's own session runs on. opencode.ci.json's `model` is the cheap default
|
|
5
|
+
# every agent without its own model falls back to; the lead is where a wrong call costs a run,
|
|
6
|
+
# so it names its model here. `model:` below only satisfies the compiler. A repository may
|
|
7
|
+
# change it to any model its opencode.ci.json declares.
|
|
8
|
+
AGENT_LEAD_MODEL: "forge/glm-5-2"
|
|
9
|
+
# The model every harness tier (plan, build, fast) runs on in CI, planner included. Unset,
|
|
10
|
+
# the tiers take harness.json, whose models the AWF proxy may not price.
|
|
11
|
+
AGENT_TIER_MODEL: "plainconcepts/glm-5-2"
|
|
4
12
|
REPO_RULES: "Read-only repository audit. Report only reproducible, actionable defects with evidence. Do not modify files, commit, push, or run write operations."
|
|
5
13
|
# Split out of REPO_RULES, which carried both the read-only discipline above and the list
|
|
6
14
|
# below. All four consuming repositories had customised the list and none could touch it
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
# Managed by @plainconceptsplatform/workflows. Source: loops/workflows/agent-implement.md. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
3
3
|
env:
|
|
4
|
+
# The model this worker's own session runs on. opencode.ci.json's `model` is the cheap default
|
|
5
|
+
# every agent without its own model falls back to; the lead is where a wrong call costs a run,
|
|
6
|
+
# so it names its model here. `model:` below only satisfies the compiler. A repository may
|
|
7
|
+
# change it to any model its opencode.ci.json declares.
|
|
8
|
+
AGENT_LEAD_MODEL: "forge/glm-5-2"
|
|
9
|
+
# The model every harness tier (plan, build, fast) runs on in CI, planner included. Unset,
|
|
10
|
+
# the tiers take harness.json, whose models the AWF proxy may not price.
|
|
11
|
+
AGENT_TIER_MODEL: "plainconcepts/glm-5-2"
|
|
4
12
|
VERIFY_COMMANDS: ""
|
|
5
13
|
REPO_RULES: "Implement only the selected issue. Follow repository documentation and existing conventions. Do not weaken tests, lower coverage thresholds, or bypass checks."
|
|
6
14
|
# Split out of REPO_RULES because one field asked to carry architecture, testing, coverage and
|
|
@@ -247,6 +255,10 @@ jobs:
|
|
|
247
255
|
pull-requests: write
|
|
248
256
|
# reads the agent job's log and annotations, and re-enters through the router on a retry
|
|
249
257
|
actions: write
|
|
258
|
+
# decide-agent-retry reads why the agent job died from its annotations, and the annotations API needs this scope.
|
|
259
|
+
# Without it the read fails quietly, a runner shut down mid-job (exit code 143) reads as no cause,
|
|
260
|
+
# and the issue is parked for a person instead of being run again (Pliny-Bot #750).
|
|
261
|
+
checks: read
|
|
250
262
|
steps:
|
|
251
263
|
- name: Checkout workflow actions
|
|
252
264
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
@@ -509,6 +521,10 @@ jobs:
|
|
|
509
521
|
issues: write
|
|
510
522
|
# the retry re-enters through the router, which is a workflow_dispatch
|
|
511
523
|
actions: write
|
|
524
|
+
# decide-agent-retry reads why the agent job died from its annotations, and the annotations API needs this scope.
|
|
525
|
+
# Without it the read fails quietly, a runner shut down mid-job (exit code 143) reads as no cause,
|
|
526
|
+
# and the issue is parked for a person instead of being run again (Pliny-Bot #750).
|
|
527
|
+
checks: read
|
|
512
528
|
steps:
|
|
513
529
|
- name: Checkout workflow actions
|
|
514
530
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
@@ -732,18 +748,33 @@ timeout-minutes: 180
|
|
|
732
748
|
wrote instead:
|
|
733
749
|
|
|
734
750
|
1. `pc-plan-goal`, then Phase 0 as it describes.
|
|
751
|
+
|
|
752
|
+
**Propose and archive run in the `planner` agent, not in this session.** Spawn it with the
|
|
753
|
+
task tool (`subagent_type: planner`) and a prompt that names the skill to load
|
|
754
|
+
(`pc-plan-propose` or `pc-plan-archive`), the issue context path above, the branch, and that it
|
|
755
|
+
runs in autonomous mode with no one to ask. `planner` runs on the repository's plan model, which
|
|
756
|
+
is stronger than this session's, and these two steps decide the whole change. Wait for it in the
|
|
757
|
+
same turn, then continue with the next phase yourself. Only when no `planner` agent exists
|
|
758
|
+
(agent-harness older than 3.4.0) load the skill here instead.
|
|
759
|
+
|
|
735
760
|
2. `pc-plan-propose`, even when the issue is refined. It is what turns the issue into
|
|
736
761
|
`openspec/changes/<id>/tasks.md` with every task annotated
|
|
737
762
|
`<!-- agent: <engineer>.<tier>, depends_on: [...], touches: [...] -->`. **You never write
|
|
738
763
|
`proposal.md` or `tasks.md` yourself.** A hand-written `tasks.md` carries no annotations,
|
|
739
764
|
and without them `pc-plan-apply` has no workers to spawn, so the whole change is done by
|
|
740
765
|
you alone, in sequence, on one model. That is the failure this step exists to prevent.
|
|
766
|
+
Ask it for few, coarse tasks: one task per area of files, the tests for that code in the
|
|
767
|
+
same task, and no task whose only job is to repair what an earlier task broke. Five tasks
|
|
768
|
+
in three waves cover most issues. Every task is a fresh worker that rereads the code it
|
|
769
|
+
needs before it edits anything, one to three minutes each: Pliny-Bot #751 split a small
|
|
770
|
+
web feature into twelve tasks and spent fifteen of its twenty-two minutes in them, and
|
|
771
|
+
#696 ended with a twelve-minute task that fixed the test doubles the earlier tasks broke.
|
|
741
772
|
3. `pc-plan-apply`. The tasks are implemented by the subagents it spawns, in waves, each on
|
|
742
773
|
the model tier its annotation names. **In this phase you do not edit source files
|
|
743
774
|
yourself.** If it stops because a worker cannot be resolved, report that with
|
|
744
775
|
`report_incomplete`, naming the task and the missing worker; do not do the task yourself
|
|
745
776
|
to get past it.
|
|
746
|
-
4. `pc-repo-verify`, then `pc-plan-archive`, as `pc-plan-goal` directs.
|
|
777
|
+
4. `pc-repo-verify`, then `pc-plan-archive` in `planner`, as `pc-plan-goal` directs.
|
|
747
778
|
|
|
748
779
|
Before the first edit to a source file, check that
|
|
749
780
|
`openspec/changes/<id>/tasks.md` exists and that its tasks carry the annotation above. If it
|
|
@@ -767,6 +798,12 @@ timeout-minutes: 180
|
|
|
767
798
|
Adhere to ${{ env.REPO_RULES }}, ${{ env.ARCHITECTURE_RULES }} and
|
|
768
799
|
${{ env.TESTING_RULES }}.
|
|
769
800
|
|
|
801
|
+
**WORK IN FEW TURNS**, on both paths. Every turn is a model call of three to twenty
|
|
802
|
+
seconds, and the turns, not the commands, are most of a run. Read a file whole, or in
|
|
803
|
+
ranges of a few hundred lines, once; never eight or fifteen lines at a time. Put
|
|
804
|
+
independent reads and searches in the same turn. Leave whitespace and line endings to the
|
|
805
|
+
formatter instead of inspecting them with `hexdump` or `cat -A`.
|
|
806
|
+
|
|
770
807
|
**DECISIVE IMPLEMENTATION**, on both paths. When a design choice is ambiguous, pick the most
|
|
771
808
|
standard interpretation and implement it immediately. Do not deliberate between options for
|
|
772
809
|
more than one turn. Do not ask clarifying questions the issue author expects you to use good
|
|
@@ -781,6 +818,21 @@ timeout-minutes: 180
|
|
|
781
818
|
|
|
782
819
|
Never open a pull request that does not pass them.
|
|
783
820
|
|
|
821
|
+
**Run them once, on the final tree.** They are slow: Pliny-Bot's `pnpm verify` is about
|
|
822
|
+
twenty minutes of work on this runner. `pc-plan-apply` runs them in its verify step, and
|
|
823
|
+
that run is this one: do not run them again when nothing changed since. Order the end of
|
|
824
|
+
the run so the last run is the only one: first `check-diff` below, which takes seconds, and
|
|
825
|
+
fix what it lists; then these commands. While you work, check with the narrowest command
|
|
826
|
+
that covers what you changed (build the project you edited, run the test files you
|
|
827
|
+
edited, typecheck the web app), not with these. Pliny-Bot #694 ran three typechecks and
|
|
828
|
+
three test runs of its own before it reached the full verification at minute forty-one.
|
|
829
|
+
|
|
830
|
+
A test that cannot start in this sandbox (it needs Docker, a database or a host the
|
|
831
|
+
firewall blocks) and fails the same way on the default branch is not yours to fix: say so
|
|
832
|
+
in the pull request and move on, CI runs it with the service. Pliny-Bot #696 spent its
|
|
833
|
+
turns on 365 integration tests that could not start here because Testcontainers found an
|
|
834
|
+
empty Docker configuration.
|
|
835
|
+
|
|
784
836
|
**Run every command in the foreground and wait for it in the same turn. Never move a command
|
|
785
837
|
to the background and never end a turn to wait for it.** This run is not interactive: nothing
|
|
786
838
|
resumes you. A turn that ends with text and no tool call ends the whole run, and the work is
|
|
@@ -803,7 +855,7 @@ timeout-minutes: 180
|
|
|
803
855
|
|
|
804
856
|
When `.opencode/scripts/check-diff.mjs` exists, also run
|
|
805
857
|
`node .opencode/scripts/check-diff.mjs`
|
|
806
|
-
on both paths. It must exit 0. It holds the comments and tests you add to the size of the
|
|
858
|
+
on both paths, before the commands above. It must exit 0. It holds the comments and tests you add to the size of the
|
|
807
859
|
change and makes you clean the files you touched; fix every line it lists. The merge gate
|
|
808
860
|
runs it again and does not merge while it fails.
|
|
809
861
|
|
|
@@ -812,8 +864,9 @@ timeout-minutes: 180
|
|
|
812
864
|
unit test only for logic with real branching. A bug fix gets one regression test. Do not
|
|
813
865
|
write tests of constants, configured defaults, mocks of your own code or markup attributes.
|
|
814
866
|
|
|
815
|
-
Anything you change to satisfy `check-diff` is a change: run
|
|
816
|
-
|
|
867
|
+
Anything you change to satisfy `check-diff` is a change: run it again, and run the
|
|
868
|
+
verification commands only after it passes, so they run on the tree you ship. Open the
|
|
869
|
+
pull request only from a tree where both pass. Do not delete
|
|
817
870
|
a test that covers a branch you added to fit the budget; the complexity gate in CI measures
|
|
818
871
|
that coverage.
|
|
819
872
|
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
# Managed by @plainconceptsplatform/workflows. Source: loops/workflows/agent-merge-gate.md. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
3
3
|
env:
|
|
4
|
+
# The model this worker's own session runs on. opencode.ci.json's `model` is the cheap default
|
|
5
|
+
# every agent without its own model falls back to; the lead is where a wrong call costs a run,
|
|
6
|
+
# so it names its model here. `model:` below only satisfies the compiler. A repository may
|
|
7
|
+
# change it to any model its opencode.ci.json declares.
|
|
8
|
+
AGENT_LEAD_MODEL: "forge/glm-5-2"
|
|
9
|
+
# The model every harness tier (plan, build, fast) runs on in CI, planner included. Unset,
|
|
10
|
+
# the tiers take harness.json, whose models the AWF proxy may not price.
|
|
11
|
+
AGENT_TIER_MODEL: "plainconcepts/glm-5-2"
|
|
4
12
|
VERIFY_COMMANDS: ""
|
|
5
13
|
REPO_RULES: "Review the selected pull request for defects and report what you verified. Do not decide the outcome: the workflow computes it from your report and from facts it measured before you ran."
|
|
6
14
|
# Where to look first, not what to escalate on. This list used to be the check that decided
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
# Managed by @plainconceptsplatform/workflows. Source: loops/workflows/agent-refine.md. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
3
3
|
env:
|
|
4
|
+
# The model this worker's own session runs on. opencode.ci.json's `model` is the cheap default
|
|
5
|
+
# every agent without its own model falls back to; the lead is where a wrong call costs a run,
|
|
6
|
+
# so it names its model here. `model:` below only satisfies the compiler. A repository may
|
|
7
|
+
# change it to any model its opencode.ci.json declares.
|
|
8
|
+
AGENT_LEAD_MODEL: "forge/glm-5-2"
|
|
9
|
+
# The model every harness tier (plan, build, fast) runs on in CI, planner included. Unset,
|
|
10
|
+
# the tiers take harness.json, whose models the AWF proxy may not price.
|
|
11
|
+
AGENT_TIER_MODEL: "plainconcepts/glm-5-2"
|
|
4
12
|
REPO_RULES: "Refine only the selected issue into a grounded, implementation-ready user story. Read repository documentation for domain context. Write acceptance criteria that match existing patterns. Where a story or its open questions decide how business data (named people, roles, addresses) is stored, resolve it toward the repository's convention for that data -- a database entity or a settings entry -- and record that choice in the acceptance criteria; do not carry a person's name or email address into the story as something the code reads from a constant. Do not implement code. Do not use web search or web fetch tools. The issue body already contains the research that motivated it; derive your domain context from the repository's own code and documentation only."
|
|
5
13
|
# The estimate decides whether a story gets split, and the prompt tells the agent these bands
|
|
6
14
|
# come from this repository's own merged pull requests. They have to actually come from it, or
|
|
@@ -672,6 +680,10 @@ jobs:
|
|
|
672
680
|
issues: write
|
|
673
681
|
# the retry re-enters through the router, which is a workflow_dispatch
|
|
674
682
|
actions: write
|
|
683
|
+
# decide-agent-retry reads why the agent job died from its annotations, and the annotations API needs this scope.
|
|
684
|
+
# Without it the read fails quietly, a runner shut down mid-job (exit code 143) reads as no cause,
|
|
685
|
+
# and the issue is parked for a person instead of being run again (Pliny-Bot #750).
|
|
686
|
+
checks: read
|
|
675
687
|
steps:
|
|
676
688
|
- name: Checkout workflow actions
|
|
677
689
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
# Managed by @plainconceptsplatform/workflows. Source: loops/workflows/agent-release.md. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
3
3
|
env:
|
|
4
|
+
# The model this worker's own session runs on. opencode.ci.json's `model` is the cheap default
|
|
5
|
+
# every agent without its own model falls back to; the lead is where a wrong call costs a run,
|
|
6
|
+
# so it names its model here. `model:` below only satisfies the compiler. A repository may
|
|
7
|
+
# change it to any model its opencode.ci.json declares.
|
|
8
|
+
AGENT_LEAD_MODEL: "forge/glm-5-2"
|
|
9
|
+
# The model every harness tier (plan, build, fast) runs on in CI, planner included. Unset,
|
|
10
|
+
# the tiers take harness.json, whose models the AWF proxy may not price.
|
|
11
|
+
AGENT_TIER_MODEL: "plainconcepts/glm-5-2"
|
|
4
12
|
GIT_AUTHOR_NAME: "github-actions[bot]"
|
|
5
13
|
GIT_AUTHOR_EMAIL: "github-actions[bot]@users.noreply.github.com"
|
|
6
14
|
GIT_COMMITTER_NAME: "github-actions[bot]"
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
# Managed by @plainconceptsplatform/workflows. Source: loops/workflows/agent-triage.md. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
3
3
|
env:
|
|
4
|
+
# The model this worker's own session runs on. opencode.ci.json's `model` is the cheap default
|
|
5
|
+
# every agent without its own model falls back to; the lead is where a wrong call costs a run,
|
|
6
|
+
# so it names its model here. `model:` below only satisfies the compiler. A repository may
|
|
7
|
+
# change it to any model its opencode.ci.json declares.
|
|
8
|
+
AGENT_LEAD_MODEL: "forge/glm-5-2"
|
|
9
|
+
# The model every harness tier (plan, build, fast) runs on in CI, planner included. Unset,
|
|
10
|
+
# the tiers take harness.json, whose models the AWF proxy may not price.
|
|
11
|
+
AGENT_TIER_MODEL: "plainconcepts/glm-5-2"
|
|
4
12
|
REPO_RULES: "Triage issues opened by outside collaborators. Assess template completeness, security risk, change size, danger level, duplicates, clarity, reproducibility, acceptance criteria, and cross-cutting impact. When the request names specific people, email addresses, or business entities as the thing the code acts on, judge where that data is meant to live: business data that belongs in the database or a settings page is a design decision, not a wording detail, and it must not be left for the implementer to decide by hardcoding it. Do not implement code. Do not modify the issue body."
|
|
5
13
|
# What a product owner may ask for, and what has to become a maintainer-owned technical
|
|
6
14
|
# proposal instead. This is business policy, so it belongs to the repository rather than to
|
|
@@ -314,6 +322,10 @@ jobs:
|
|
|
314
322
|
issues: write
|
|
315
323
|
# the retry re-enters through the router, which is a workflow_dispatch
|
|
316
324
|
actions: write
|
|
325
|
+
# decide-agent-retry reads why the agent job died from its annotations, and the annotations API needs this scope.
|
|
326
|
+
# Without it the read fails quietly, a runner shut down mid-job (exit code 143) reads as no cause,
|
|
327
|
+
# and the issue is parked for a person instead of being run again (Pliny-Bot #750).
|
|
328
|
+
checks: read
|
|
317
329
|
steps:
|
|
318
330
|
- name: Checkout workflow actions
|
|
319
331
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
@@ -165,6 +165,45 @@ pre-agent-steps:
|
|
|
165
165
|
- name: Install workspace dependencies
|
|
166
166
|
run: pnpm install --frozen-lockfile
|
|
167
167
|
|
|
168
|
+
- name: Cache the agent-harness plugins' dependencies
|
|
169
|
+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
170
|
+
with:
|
|
171
|
+
path: .opencode/node_modules
|
|
172
|
+
key: opencode-plugins-${{ runner.os }}-${{ hashFiles('.opencode/package.json') }}
|
|
173
|
+
|
|
174
|
+
- name: Install the agent-harness plugins' dependencies
|
|
175
|
+
# The harness plugins under .opencode/plugins import @opencode/plugin, which lives in
|
|
176
|
+
# .opencode/node_modules, and that directory is gitignored. Without it OpenCode logs
|
|
177
|
+
# "failed to load plugin ... Cannot find package '@opencode/plugin'" and goes on: no tier
|
|
178
|
+
# workers, no planner, no concurrency cap. Every CI run until 1.6.23 ran that way, so
|
|
179
|
+
# pc-plan-apply spawned the untiered base engineers on the session's model.
|
|
180
|
+
#
|
|
181
|
+
# Two of the three run here. pc-system-reminders gates shell and edits on skills a CI
|
|
182
|
+
# worker's prompt does not load, and has never run in CI; it stays off until it does.
|
|
183
|
+
#
|
|
184
|
+
# The tier models come from AGENT_TIER_MODEL when a worker sets it. The AWF proxy refuses
|
|
185
|
+
# a model it has no AI credits price for, and harness.json names carina-60b and
|
|
186
|
+
# quasar-438b, which it has none for (Atlas #2016), so CI pins every tier to one priced
|
|
187
|
+
# model through the gitignored per-user override the plugin reads first.
|
|
188
|
+
run: |
|
|
189
|
+
set -euo pipefail
|
|
190
|
+
if [ ! -f .opencode/package.json ]; then
|
|
191
|
+
echo "No .opencode/package.json: agent-harness is not installed, nothing to load"; exit 0
|
|
192
|
+
fi
|
|
193
|
+
if [ ! -d .opencode/node_modules/@opencode/plugin ]; then
|
|
194
|
+
(cd .opencode && npm install --no-audit --no-fund --no-package-lock --ignore-scripts --loglevel=error)
|
|
195
|
+
fi
|
|
196
|
+
if [ -f .opencode/plugins/pc-system-reminders.js ]; then
|
|
197
|
+
rm .opencode/plugins/pc-system-reminders.js
|
|
198
|
+
# Tracked, so hide the deletion: an agent that sees it in `git status` restores it.
|
|
199
|
+
git update-index --skip-worktree .opencode/plugins/pc-system-reminders.js 2>/dev/null || true
|
|
200
|
+
fi
|
|
201
|
+
if [ -n "${AGENT_TIER_MODEL:-}" ]; then
|
|
202
|
+
jq -n --arg m "$AGENT_TIER_MODEL" '{models: {plan: $m, build: $m, fast: $m}}' > .opencode/harness.user.json
|
|
203
|
+
fi
|
|
204
|
+
echo "Harness plugins: $(ls .opencode/plugins | tr '\n' ' ')"
|
|
205
|
+
echo "Tier models: $(jq -c '.models' .opencode/harness.user.json 2>/dev/null || jq -c '.models' .opencode/harness.json)"
|
|
206
|
+
|
|
168
207
|
- name: Merge the CI-only OpenCode provider into opencode.jsonc
|
|
169
208
|
run: |
|
|
170
209
|
set -euo pipefail
|
|
@@ -195,6 +234,12 @@ pre-agent-steps:
|
|
|
195
234
|
merged=$(jq -S . "$FRAGMENT")
|
|
196
235
|
fi
|
|
197
236
|
|
|
237
|
+
# The worker's lead model, from its env. gh-aw leaves OPENCODE_MODEL empty, so without this
|
|
238
|
+
# every worker's session would run on opencode.ci.json's default.
|
|
239
|
+
if [ -n "${AGENT_LEAD_MODEL:-}" ]; then
|
|
240
|
+
merged=$(printf '%s' "$merged" | jq --arg model "$AGENT_LEAD_MODEL" '.agents["ci-workflow-agent"].model = $model')
|
|
241
|
+
fi
|
|
242
|
+
|
|
198
243
|
printf '%s\n' "$merged" > "$CONFIG"
|
|
199
244
|
|
|
200
245
|
# A tracked config now differs from HEAD, so the agent sees it in `git diff`, takes it for
|
|
@@ -210,5 +255,5 @@ pre-agent-steps:
|
|
|
210
255
|
# `$existing * $base`. Base wins on conflicting keys, but it defines neither `model`
|
|
211
256
|
# nor this provider, so both survive and `awf-proxy` is added alongside.
|
|
212
257
|
echo "Wrote $CONFIG from $FRAGMENT:"
|
|
213
|
-
jq -r '" model: \(.model // "unset")", " plugins: \(.plugins // [] | join(", "))", " providers: \(.providers // {} | keys | join(", "))"' "$CONFIG"
|
|
258
|
+
jq -r '" model: \(.model // "unset")", " lead: \(.agents["ci-workflow-agent"].model // "the default")", " plugins: \(.plugins // [] | join(", "))", " providers: \(.providers // {} | keys | join(", "))"' "$CONFIG"
|
|
214
259
|
---
|
|
@@ -551,9 +551,29 @@ jobs:
|
|
|
551
551
|
body=$(gh issue view "$ISSUE_NUMBER" --repo "$REPO" --json body --jq '.body // ""' 2>/dev/null || echo '')
|
|
552
552
|
parent=$(printf '%s' "$body" | grep -oE '<!-- split-parent: [0-9]+ -->' | head -1 | grep -oE '[0-9]+' || true)
|
|
553
553
|
key="${parent:-$ISSUE_NUMBER}"
|
|
554
|
-
|
|
554
|
+
|
|
555
|
+
# The queue is the key's place among the keys of every issue waiting for an implement, not
|
|
556
|
+
# the key modulo the number of queues. Three independent issues whose numbers were all
|
|
557
|
+
# multiples of three (Pliny-Bot #726, #750 and #694, whose parent is #690) landed in queue
|
|
558
|
+
# zero together and ran one after another behind a three-hour run while two queues sat
|
|
559
|
+
# empty. A place among the waiting keys puts independent issues in different queues, and
|
|
560
|
+
# the children of one refine, which share a key, still share one.
|
|
561
|
+
waiting=$(
|
|
562
|
+
{ printf '%s\n' "$key"
|
|
563
|
+
gh issue list --repo "$REPO" --label "implement" --state open --limit 100 --json number,body \
|
|
564
|
+
--jq '.[] | ((.body // "") | capture("<!-- split-parent: (?<p>[0-9]+) -->").p) // (.number | tostring)' \
|
|
565
|
+
2>/dev/null || true
|
|
566
|
+
} | grep -E '^[0-9]+$' | sort -un
|
|
567
|
+
)
|
|
568
|
+
place=0
|
|
569
|
+
index=0
|
|
570
|
+
for candidate in $waiting; do
|
|
571
|
+
[ "$candidate" = "$key" ] && place=$index
|
|
572
|
+
index=$((index + 1))
|
|
573
|
+
done
|
|
574
|
+
queue=$(( place % queues ))
|
|
555
575
|
echo "queue=$queue" >> "$GITHUB_OUTPUT"
|
|
556
|
-
echo "#$ISSUE_NUMBER waits in implement queue $queue of $queues (keyed on #$key)."
|
|
576
|
+
echo "#$ISSUE_NUMBER waits in implement queue $queue of $queues (keyed on #$key, place $place among $index waiting)."
|
|
557
577
|
- name: Resolve the feature branch from the stack markers
|
|
558
578
|
id: stack
|
|
559
579
|
env:
|