@plainconceptsplatform/workflows 1.14.1 → 1.16.1

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 (41) hide show
  1. package/dist/workflow-catalog.d.ts +1 -1
  2. package/dist/workflow-catalog.js +0 -2
  3. package/loops/actions/add-issue-labels/action.yml +50 -50
  4. package/loops/actions/agent-output.cjs +17 -17
  5. package/loops/actions/apply-agent-bundle/action.yml +24 -24
  6. package/loops/actions/apply-agent-comments/action.yml +42 -42
  7. package/loops/actions/apply-agent-labels/action.yml +55 -55
  8. package/loops/actions/assess-blast-radius/assess-blast-radius.sh +0 -0
  9. package/loops/actions/assess-blast-radius/migration-is-additive.sh +0 -0
  10. package/loops/actions/cleanup-artifacts/action.yml +91 -91
  11. package/loops/actions/close-agent-issues/action.yml +43 -43
  12. package/loops/actions/create-issue-comment/action.yml +29 -29
  13. package/loops/actions/decide-agent-retry/classify-failure.sh +0 -0
  14. package/loops/actions/download-agent-output/action.yml +53 -53
  15. package/loops/actions/link-pr-to-issue/action.yml +40 -40
  16. package/loops/actions/list-open-issues/action.yml +33 -33
  17. package/loops/actions/load-issue-context/action.yml +45 -45
  18. package/loops/actions/merge-agent-pr/action.yml +49 -49
  19. package/loops/actions/merge-train/merge-train.sh +0 -0
  20. package/loops/actions/rebase-stale-patch/rebase-stale-patch.sh +0 -0
  21. package/loops/actions/remove-issue-labels/action.yml +37 -37
  22. package/loops/actions/update-agent-issues/action.yml +58 -58
  23. package/loops/actions/validate-merge-gate-output/validate-merge-gate-output.sh +0 -0
  24. package/loops/actions/validate-refine-output/validate-refine-output.sh +13 -1
  25. package/loops/actions/validate-review-output/action.yml +35 -35
  26. package/loops/actions/validate-triage-output/action.yml +36 -36
  27. package/loops/actions/verify-composite-actions/action.yml +9 -9
  28. package/loops/actions/verify-refine-output/action.yml +9 -9
  29. package/loops/actions/verify-refine-output/verify-refine-output.sh +4 -0
  30. package/loops/actions/verify-route-matrix/action.yml +9 -9
  31. package/loops/actions/verify-route-matrix/verify-route-matrix.sh +32 -46
  32. package/loops/templates/agentics/agentics-maintenance.yml +121 -121
  33. package/loops/templates/issues/bug_report.yml +109 -109
  34. package/loops/templates/issues/feature_request.yml +75 -75
  35. package/loops/templates/quality-gates/scripts/dast-wait-stable.sh +0 -0
  36. package/loops/workflows/agent-refine.md +29 -118
  37. package/loops/workflows/shared/platform-defaults.md +53 -53
  38. package/package.json +1 -1
  39. package/loops/actions/report-workflow-errors/action.yml +0 -538
  40. package/loops/actions/report-workflow-errors/report-policy.cjs +0 -194
  41. 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' }}