@plainconceptsplatform/workflows 1.14.1 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/workflow-catalog.d.ts +1 -1
- package/dist/workflow-catalog.js +0 -2
- package/loops/actions/add-issue-labels/action.yml +50 -50
- package/loops/actions/agent-output.cjs +17 -17
- package/loops/actions/apply-agent-bundle/action.yml +24 -24
- package/loops/actions/apply-agent-comments/action.yml +42 -42
- package/loops/actions/apply-agent-labels/action.yml +55 -55
- package/loops/actions/cleanup-artifacts/action.yml +91 -91
- package/loops/actions/close-agent-issues/action.yml +43 -43
- package/loops/actions/create-issue-comment/action.yml +29 -29
- package/loops/actions/download-agent-output/action.yml +53 -53
- package/loops/actions/link-pr-to-issue/action.yml +40 -40
- package/loops/actions/list-open-issues/action.yml +33 -33
- package/loops/actions/load-issue-context/action.yml +45 -45
- package/loops/actions/merge-agent-pr/action.yml +49 -49
- package/loops/actions/remove-issue-labels/action.yml +37 -37
- package/loops/actions/update-agent-issues/action.yml +58 -58
- package/loops/actions/validate-refine-output/validate-refine-output.sh +13 -1
- package/loops/actions/validate-review-output/action.yml +35 -35
- package/loops/actions/validate-triage-output/action.yml +36 -36
- package/loops/actions/verify-composite-actions/action.yml +9 -9
- package/loops/actions/verify-refine-output/action.yml +9 -9
- package/loops/actions/verify-refine-output/verify-refine-output.sh +4 -0
- package/loops/actions/verify-route-matrix/action.yml +9 -9
- package/loops/actions/verify-route-matrix/verify-route-matrix.sh +32 -46
- package/loops/templates/agentics/agentics-maintenance.yml +121 -121
- package/loops/templates/issues/bug_report.yml +109 -109
- package/loops/templates/issues/feature_request.yml +75 -75
- package/loops/workflows/agent-refine.md +29 -118
- package/loops/workflows/shared/platform-defaults.md +53 -53
- package/package.json +1 -1
- package/loops/actions/report-workflow-errors/action.yml +0 -538
- package/loops/actions/report-workflow-errors/report-policy.cjs +0 -194
- package/loops/templates/agentics/agentics-error-report.yml +0 -101
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
// Managed by @plainconceptsplatform/workflows. Source: loops/actions/report-workflow-errors/report-policy.cjs. Update with workflows update --force; consumer edits may be overwritten.
|
|
2
|
-
//
|
|
3
|
-
// What the error report decides, kept apart from what it reads and files so it can be run on its
|
|
4
|
-
// own. Pure: no network, no clock, nothing read from the repository. Everything it sees is a
|
|
5
|
-
// finding the report already built from package-owned names and enumerated values, so nothing
|
|
6
|
-
// here can widen what leaves a private repository.
|
|
7
|
-
//
|
|
8
|
-
// The report used to open one issue per job, step and classification, for every failure it saw.
|
|
9
|
-
// Read back over a month, 320 issues: 59% were cancelled jobs, 80% carried no diagnosis, 14% were
|
|
10
|
-
// a runner shut down under a job, 47% re-filed a signature seen before, every one was closed as
|
|
11
|
-
// not planned and none had a comment. What follows is the filter that would have kept about a
|
|
12
|
-
// hundred of them out of that pile and left the rest as a count in the job summary.
|
|
13
|
-
//
|
|
14
|
-
// The 11 that still got through in the week to 2026-11-03 were the next layer: 39 jobs a day failing
|
|
15
|
-
// gh-aw's check for an engine secret the repository never set, agent failures the worker had
|
|
16
|
-
// already sent back through the router and recovered, a shellcheck step failing on a pull request
|
|
17
|
-
// it was right to fail, and installs several minors behind reporting what a release had fixed.
|
|
18
|
-
|
|
19
|
-
'use strict';
|
|
20
|
-
|
|
21
|
-
// Causes that say something about the fleet, not about this package. They are counted in the job
|
|
22
|
-
// summary, where a trend is visible, and never become an issue: nobody can fix a runner that was
|
|
23
|
-
// taken away mid-job by editing a workflow. A job that hit its timeout is NOT here, because that
|
|
24
|
-
// can be the package's own work taking too long.
|
|
25
|
-
const INFRA = new Set([
|
|
26
|
-
'runner-never-assigned',
|
|
27
|
-
'runner-lost-mid-job',
|
|
28
|
-
'gh-api-rate-limit',
|
|
29
|
-
'model-quota',
|
|
30
|
-
'model-gateway-unreachable',
|
|
31
|
-
'out-of-disk',
|
|
32
|
-
'out-of-memory',
|
|
33
|
-
]);
|
|
34
|
-
|
|
35
|
-
// Causes that belong to the repository that installed the package: a secret it never set. They are
|
|
36
|
-
// warned about in that repository's own job summary, where the person who can set it will look,
|
|
37
|
-
// and never filed upstream, where nobody can.
|
|
38
|
-
const CONSUMER = new Set([
|
|
39
|
-
'missing-secret',
|
|
40
|
-
]);
|
|
41
|
-
|
|
42
|
-
// Causes that are a defect in something this package ships.
|
|
43
|
-
const PACKAGE_BUG = new Set([
|
|
44
|
-
'jq-error',
|
|
45
|
-
'yaml-or-expression-invalid',
|
|
46
|
-
'lock-out-of-date',
|
|
47
|
-
'action-not-found',
|
|
48
|
-
'agent-produced-no-output',
|
|
49
|
-
'gh-api-403',
|
|
50
|
-
'gh-api-404',
|
|
51
|
-
'gh-api-409-conflict',
|
|
52
|
-
]);
|
|
53
|
-
|
|
54
|
-
/** infra, consumer, package-bug, or unknown (a cause nobody classified yet, worth a look when it repeats). */
|
|
55
|
-
function severityOf(patternId) {
|
|
56
|
-
if (INFRA.has(patternId)) return 'infra';
|
|
57
|
-
if (CONSUMER.has(patternId)) return 'consumer';
|
|
58
|
-
if (PACKAGE_BUG.has(patternId)) return 'package-bug';
|
|
59
|
-
return 'unknown';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Some steps say what failed by their name alone, and their log says it in words the catalogue
|
|
64
|
-
* cannot safely match: gh-aw's secret check names the variable, and a pattern for that name would
|
|
65
|
-
* put an engine name in a file that asserts it never reaches for a model. Step names are the
|
|
66
|
-
* package's own (or gh-aw's, compiled into the package's lock files), so they are safe to read.
|
|
67
|
-
*/
|
|
68
|
-
function causeFromStep(step) {
|
|
69
|
-
if (/^Validate .* secrets?$/i.test(step ?? '')) return 'missing-secret';
|
|
70
|
-
if (/^Check workflow lock file$/i.test(step ?? '')) return 'lock-out-of-date';
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* A worker that hits a short run, a runner shutdown or a cut-off sends the work back through the
|
|
76
|
-
* router (decide-agent-retry) and the next attempt usually lands. The failed agent job stays red in
|
|
77
|
-
* the run that retried it, and used to be filed as if nothing had caught it. A run whose retry step
|
|
78
|
-
* succeeded has had its failure answered; the attempt that finally parks does not retry, so a
|
|
79
|
-
* failure the retries never got past is still counted.
|
|
80
|
-
*/
|
|
81
|
-
function wasRetried(jobs) {
|
|
82
|
-
return (jobs ?? []).some((job) => (job.steps ?? []).some((step) =>
|
|
83
|
-
/^Send the (work|issue) back through the router$/.test(step.name ?? '') && step.conclusion === 'success'));
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* agentics-checks lints the repository's workflows and actions. On a pull request a red check is
|
|
88
|
-
* the check doing its job on somebody's change; only on the default branch, where the package's
|
|
89
|
-
* own files are what is there, can its failure be the package's.
|
|
90
|
-
*/
|
|
91
|
-
function isReviewCheck({ workflow, event }) {
|
|
92
|
-
return workflow === 'agentics-checks.yml' && (event === 'pull_request' || event === 'pull_request_target');
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Whether the reporting install is a minor or more behind the latest release. Its failure may be
|
|
97
|
-
* one the release already fixed, so it does not open an issue; it still appends to one that is
|
|
98
|
-
* open. Patch versions are left alone: the fleet is rolled out the same day and a patch behind is
|
|
99
|
-
* the normal state for hours after every release.
|
|
100
|
-
*/
|
|
101
|
-
function isOutdated(reporting, latest) {
|
|
102
|
-
const parse = (v) => /^v?(\d+)\.(\d+)\./.exec(v ?? '')?.slice(1, 3).map(Number);
|
|
103
|
-
const a = parse(reporting);
|
|
104
|
-
const b = parse(latest);
|
|
105
|
-
if (!a || !b) return false;
|
|
106
|
-
return a[0] < b[0] || (a[0] === b[0] && a[1] < b[1]);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* The newest release among the upstream tags. Tags, not GitHub releases or the npm dist-tag: the
|
|
111
|
-
* fleet is rolled out from tags, and both of the others have lagged them by several minors.
|
|
112
|
-
*/
|
|
113
|
-
function latestVersion(tags) {
|
|
114
|
-
let best = '';
|
|
115
|
-
for (const tag of tags ?? []) {
|
|
116
|
-
if (!/^v?\d+\.\d+\.\d+$/.test(tag)) continue;
|
|
117
|
-
const [a, b] = [tag, best].map((v) => v.replace(/^v/, '').split('.').map(Number));
|
|
118
|
-
if (!best || a[0] > b[0] || (a[0] === b[0] && (a[1] > b[1] || (a[1] === b[1] && a[2] > b[2])))) best = tag;
|
|
119
|
-
}
|
|
120
|
-
return best;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* One cause is one issue. A specific cause is the same defect whichever job of the router tripped
|
|
125
|
-
* on it, so the job and the step are not part of its identity: conclude, safe_outputs and reserve
|
|
126
|
-
* failing on one 403 are one finding, not three. A cause nobody could name is told apart by the
|
|
127
|
-
* step it failed in, because that is all there is to go on.
|
|
128
|
-
*/
|
|
129
|
-
function signatureOf({ workflow, step, conclusion, patternId }) {
|
|
130
|
-
if (patternId === 'unrecognised') return [workflow, step, conclusion, patternId].join('|');
|
|
131
|
-
return [workflow, conclusion, patternId].join('|');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** The text the upstream search matches on, so a repeat lands on the issue that already exists. */
|
|
135
|
-
function markerKey({ workflow, step, conclusion, patternId }) {
|
|
136
|
-
return signatureOf({ workflow, step, conclusion, patternId });
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Which findings become issues, and why the others do not.
|
|
141
|
-
* - infra causes are held: they are a rate, not a bug.
|
|
142
|
-
* - consumer causes are held: the repository that has them is the one that can fix them.
|
|
143
|
-
* - a finding seen fewer than `minOccurrences` times in the window is held: once is a blip.
|
|
144
|
-
* - an unrecognised cause needs `minUnknown`: with no diagnosis, twice in a day was mostly a
|
|
145
|
-
* provider hiccup nobody could act on.
|
|
146
|
-
* - at most `maxFindings` are filed per run, the most frequent first, so a bad night opens a
|
|
147
|
-
* handful of issues and not forty.
|
|
148
|
-
*/
|
|
149
|
-
function select(findings, { minOccurrences = 2, minUnknown = 3, maxFindings = 5 } = {}) {
|
|
150
|
-
const ranked = [...findings].sort((a, b) => b.count - a.count);
|
|
151
|
-
const file = [];
|
|
152
|
-
const held = [];
|
|
153
|
-
const neededFor = (finding) => (finding.severity === 'unknown' ? Math.max(minOccurrences, minUnknown) : minOccurrences);
|
|
154
|
-
for (const finding of ranked) {
|
|
155
|
-
if (finding.severity === 'infra') {
|
|
156
|
-
held.push({ finding, reason: 'infrastructure cause, counted not filed' });
|
|
157
|
-
} else if (finding.severity === 'consumer') {
|
|
158
|
-
held.push({ finding, reason: "this repository's own configuration, warned here not filed" });
|
|
159
|
-
} else if (finding.count < neededFor(finding)) {
|
|
160
|
-
const needed = neededFor(finding);
|
|
161
|
-
held.push({ finding, reason: `seen ${finding.count} time(s), under the ${needed} needed to file` });
|
|
162
|
-
} else if (file.length >= maxFindings) {
|
|
163
|
-
held.push({ finding, reason: `over the ${maxFindings} filed per run` });
|
|
164
|
-
} else {
|
|
165
|
-
file.push(finding);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return { file, held };
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* A signature closed recently stays quiet. Closing an issue is the answer to it; the report used
|
|
173
|
-
* to search open issues only, so every bulk close reopened the whole set the next morning.
|
|
174
|
-
*/
|
|
175
|
-
function isQuiet(closedAt, now, quietDays = 30) {
|
|
176
|
-
if (!closedAt) return false;
|
|
177
|
-
const closed = new Date(closedAt).getTime();
|
|
178
|
-
if (Number.isNaN(closed)) return false;
|
|
179
|
-
return now - closed < quietDays * 24 * 3600 * 1000;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* The package version out of the ownership header. The character class used to swallow the
|
|
184
|
-
* full stop that ends the sentence, so every report said `1.2.3.`.
|
|
185
|
-
*/
|
|
186
|
-
function parseVersion(header) {
|
|
187
|
-
const match = /@plainconceptsplatform\/workflows@([0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.]*[0-9A-Za-z])?)/.exec(header ?? '');
|
|
188
|
-
return match ? match[1] : 'unknown';
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
module.exports = {
|
|
192
|
-
INFRA, CONSUMER, PACKAGE_BUG, severityOf, causeFromStep, wasRetried, isReviewCheck, isOutdated, latestVersion,
|
|
193
|
-
signatureOf, markerKey, select, isQuiet, parseVersion,
|
|
194
|
-
};
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# Managed by @plainconceptsplatform/workflows. Source: loops/templates/agentics/agentics-error-report.yml. Update with `workflows update --force`; consumer edits may be overwritten.
|
|
2
|
-
name: "Agentics: Error Report"
|
|
3
|
-
|
|
4
|
-
# Once a day, look at how the workflows this package ships behaved in this repository and file
|
|
5
|
-
# what broke upstream, so the package gets fixed instead of every repository working around the
|
|
6
|
-
# same bug on its own.
|
|
7
|
-
#
|
|
8
|
-
# THIS REPOSITORY IS PRIVATE AND THE REPORT LEAVES IT. That is the whole design constraint. The
|
|
9
|
-
# job is deterministic no model runs, because a model summarising a log would paraphrase
|
|
10
|
-
# whatever the log happened to contain and it sends only:
|
|
11
|
-
#
|
|
12
|
-
# * the names this package gives its own workflows, jobs and steps
|
|
13
|
-
# * a conclusion, a runner label and a count
|
|
14
|
-
# * the id of a matched entry from a fixed catalogue of failure shapes
|
|
15
|
-
#
|
|
16
|
-
# It never sends a log line, a branch name, an issue title, a file path, a commit, a URL or a
|
|
17
|
-
# number that could be looked up. Before anything is filed, the finished text is run through a
|
|
18
|
-
# leak scanner; if the scanner matches, the report is withheld and the run goes red rather than
|
|
19
|
-
# filing it. See loops/actions/report-workflow-errors/action.yml.
|
|
20
|
-
#
|
|
21
|
-
# To turn it off: delete this file, or clear UPSTREAM_NAME below to compute the report into the
|
|
22
|
-
# job summary and file nothing.
|
|
23
|
-
|
|
24
|
-
run-name: "Error report: last ${{ github.event.inputs.lookback-hours || '24' }}h"
|
|
25
|
-
|
|
26
|
-
on:
|
|
27
|
-
schedule:
|
|
28
|
-
# One slot for every repository, unlike the audit cron. This job runs on a GitHub-hosted
|
|
29
|
-
# runner and never touches the shared agent fleet, and separate repositories do not queue
|
|
30
|
-
# against one another on hosted runners, so there is nothing to stagger.
|
|
31
|
-
- cron: "11 7 * * *"
|
|
32
|
-
workflow_dispatch:
|
|
33
|
-
inputs:
|
|
34
|
-
lookback-hours:
|
|
35
|
-
description: "How far back to look"
|
|
36
|
-
required: false
|
|
37
|
-
type: string
|
|
38
|
-
default: '24'
|
|
39
|
-
dry-run:
|
|
40
|
-
description: "Compute the report and file nothing"
|
|
41
|
-
required: false
|
|
42
|
-
type: boolean
|
|
43
|
-
default: false
|
|
44
|
-
|
|
45
|
-
permissions:
|
|
46
|
-
contents: read
|
|
47
|
-
|
|
48
|
-
env:
|
|
49
|
-
# Where the report goes. Owner and name are separate because the App token has to be scoped
|
|
50
|
-
# to the one repository, and GitHub expressions have no split(): there is no way to take the
|
|
51
|
-
# halves of "owner/name" apart inside a workflow. Clear UPSTREAM_NAME to keep the report
|
|
52
|
-
# local to the job summary.
|
|
53
|
-
UPSTREAM_OWNER: "PlainConceptsPlatform"
|
|
54
|
-
UPSTREAM_NAME: "Agentic-Workflows"
|
|
55
|
-
# How far back a scheduled run looks. Matches the cron so nothing is counted twice.
|
|
56
|
-
LOOKBACK_HOURS: "24"
|
|
57
|
-
|
|
58
|
-
jobs:
|
|
59
|
-
report:
|
|
60
|
-
# Fork of a consumer: the fleet is not theirs and neither is the report.
|
|
61
|
-
# AGENTIC_WORKFLOWS_DISABLED, a repository variable with any value, turns the automation off.
|
|
62
|
-
if: "!github.event.repository.fork && vars.AGENTIC_WORKFLOWS_DISABLED == ''"
|
|
63
|
-
runs-on: agents-arc
|
|
64
|
-
timeout-minutes: 20
|
|
65
|
-
concurrency:
|
|
66
|
-
group: agentics-error-report
|
|
67
|
-
cancel-in-progress: false
|
|
68
|
-
permissions:
|
|
69
|
-
# Reading this repository's own runs, jobs and logs. Read-only throughout: the job files
|
|
70
|
-
# upstream with a separate token and writes nothing here.
|
|
71
|
-
contents: read
|
|
72
|
-
actions: read
|
|
73
|
-
# The annotations GitHub writes on a job say why a runner went away; the log does not.
|
|
74
|
-
checks: read
|
|
75
|
-
steps:
|
|
76
|
-
- name: Checkout workflow actions
|
|
77
|
-
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
78
|
-
with:
|
|
79
|
-
persist-credentials: false
|
|
80
|
-
|
|
81
|
-
# A token for the upstream repository only. The App has to be installed there; where it is
|
|
82
|
-
# not, this step fails, the next one is skipped, and the report still lands in the job
|
|
83
|
-
# summary with a warning rather than a red run.
|
|
84
|
-
- name: Generate an upstream token
|
|
85
|
-
id: upstream-token
|
|
86
|
-
if: env.UPSTREAM_NAME != ''
|
|
87
|
-
continue-on-error: true
|
|
88
|
-
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
|
89
|
-
with:
|
|
90
|
-
app-id: ${{ secrets.BOT_APP_ID }}
|
|
91
|
-
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
|
92
|
-
owner: ${{ env.UPSTREAM_OWNER }}
|
|
93
|
-
repositories: ${{ env.UPSTREAM_NAME }}
|
|
94
|
-
|
|
95
|
-
- uses: ./.github/actions/report-workflow-errors
|
|
96
|
-
with:
|
|
97
|
-
token: ${{ github.token }}
|
|
98
|
-
upstream-token: ${{ steps.upstream-token.outputs.token }}
|
|
99
|
-
upstream-repo: ${{ env.UPSTREAM_OWNER }}/${{ env.UPSTREAM_NAME }}
|
|
100
|
-
lookback-hours: ${{ github.event.inputs.lookback-hours || env.LOOKBACK_HOURS }}
|
|
101
|
-
dry-run: ${{ github.event.inputs.dry-run || 'false' }}
|