thumbgate 1.37.2 → 1.37.3
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/.agents/skills/article-syndication-pipeline/SKILL.md +37 -0
- package/.agents/skills/ci-gha-buildkite-patterns-not-clone/SKILL.md +72 -0
- package/.agents/skills/comet-session-logger/SKILL.md +59 -0
- package/.agents/skills/datadog-llm-obs-compare-not-clone/SKILL.md +75 -0
- package/.agents/skills/deeppattern-discipline-honesty-not-clone/SKILL.md +73 -0
- package/.agents/skills/thumbgate-board-loop/SKILL.md +72 -0
- package/.agents/skills/thumbgate-daily-discoveries/SKILL.md +57 -0
- package/.agents/skills/typesafe-typed-questions-not-clone/SKILL.md +7 -3
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/forge/forge.yaml +3 -3
- package/adapters/future-agi/.mcp.json +1 -1
- package/adapters/future-agi/config.toml +1 -1
- package/adapters/future-agi/opencode.json +1 -1
- package/adapters/herdr/herdr-plugin.toml +2 -2
- package/adapters/mcp/server-stdio.js +1 -1
- package/adapters/opencode/opencode.json +1 -1
- package/bin/cli.js +132 -4
- package/config/gate-templates.json +48 -0
- package/package.json +36 -9
- package/public/blog/2026-09-23-datadog-llm-obs-four-practices.html +42 -0
- package/public/index.html +2 -2
- package/public/install.html +7 -7
- package/public/numbers.html +2 -2
- package/scripts/ci-gha-buildkite-patterns.js +393 -0
- package/scripts/cli-schema.js +51 -1
- package/scripts/deeppattern-discipline-honesty.js +431 -0
- package/scripts/install-thumbgate-daily-publish-launchd.sh +83 -0
- package/scripts/llm-obs-honesty.js +478 -0
- package/scripts/thumbgate-board-loop.js +432 -0
- package/scripts/thumbgate-daily-discoveries-publish.js +413 -0
- package/scripts/thumbgate-daily-discoveries-publish.sh +18 -0
- package/scripts/typesafe-typed-questions.js +332 -7
- package/server.json +2 -2
- package/skills/article-syndication-pipeline/SKILL.md +37 -0
- package/skills/comet-session-logger/SKILL.md +59 -0
- package/skills/thumbgate-daily-discoveries/SKILL.md +57 -0
- package/src/index.js +10 -0
- package/src/integrations/ideabrowser-connector.js +190 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Buildkite pipeline FORMAT steal onto GitHub Actions — not a clone.
|
|
6
|
+
*
|
|
7
|
+
* Sources (public docs, 2026-09):
|
|
8
|
+
* https://buildkite.com/docs/pipelines/best-practices/pipeline-design-and-structure
|
|
9
|
+
* https://buildkite.com/docs/pipelines/configure/annotations
|
|
10
|
+
* https://buildkite.com/docs/pipelines/configure/dependencies
|
|
11
|
+
*
|
|
12
|
+
* Transfers onto existing GHA rails:
|
|
13
|
+
* 1. first-fail step identity — name the first failed job/step before logs
|
|
14
|
+
* 2. fail-fast / cancel-in-progress — PR concurrency; never cancel main
|
|
15
|
+
* 3. depends_on honesty — `needs:` is wait; continue-on-error only on non-required
|
|
16
|
+
* 4. skip conditions — path/event filters must not skip merge_group required checks
|
|
17
|
+
* 5. annotations — GITHUB_STEP_SUMMARY, not Buildkite annotation APIs
|
|
18
|
+
*
|
|
19
|
+
* Does NOT add Buildkite Pipelines, agents, Test Engine, auto-quarantine,
|
|
20
|
+
* Package Registries, or a second required CI vendor.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const fs = require('node:fs');
|
|
24
|
+
const path = require('node:path');
|
|
25
|
+
|
|
26
|
+
const SOURCE_DESIGN = 'https://buildkite.com/docs/pipelines/best-practices/pipeline-design-and-structure';
|
|
27
|
+
const SOURCE_ANNOTATIONS = 'https://buildkite.com/docs/pipelines/configure/annotations';
|
|
28
|
+
|
|
29
|
+
const REQUIRED_GHA_CONTEXTS = Object.freeze([
|
|
30
|
+
'test',
|
|
31
|
+
'audit',
|
|
32
|
+
'Verify changeset',
|
|
33
|
+
'Analyze JavaScript',
|
|
34
|
+
'CodeQL',
|
|
35
|
+
'Socket Security: Project Report',
|
|
36
|
+
'Socket Security: Pull Request Alerts',
|
|
37
|
+
'GitGuardian Security Checks',
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const CLONE_RE = /\b(buildkite\.yml|buildkite-agent|test engine auto-?quarantine|pipelines saas|hosted mac agents|buildkite mcp)\b/i;
|
|
41
|
+
const MIGRATE_RE = /\b(migrate (this )?(repo )?to buildkite|replace github actions with buildkite|add buildkite as (a )?required check)\b/i;
|
|
42
|
+
|
|
43
|
+
const RAIL_MAP = Object.freeze([
|
|
44
|
+
{
|
|
45
|
+
buildkite: 'Step is the diagnosis unit; first failed step before logs',
|
|
46
|
+
githubActions: 'gh run view --json jobs → first failed job.steps[].name; never --log-failed first',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
buildkite: 'fail-fast cancels remaining jobs; fast checks before slow suites',
|
|
50
|
+
githubActions: 'concurrency cancel-in-progress on PRs; never cancel main; ci-scope deps/web/full',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
buildkite: 'depends_on / wait; continue_on_failure only for annotations after a fail',
|
|
54
|
+
githubActions: 'jobs.<id>.needs; continue-on-error only on non-required drift monitors',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
buildkite: 'skip / if / branches skip unnecessary work',
|
|
58
|
+
githubActions: 'paths: + ci-scope; merge_group and main stay full required checks',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
buildkite: 'build annotations summarize failures',
|
|
62
|
+
githubActions: 'GITHUB_STEP_SUMMARY from --annotate; no Buildkite annotation API',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
buildkite: 'control plane vs compute (SaaS schedules, you run agents)',
|
|
66
|
+
githubActions: 'GitHub is the control plane; ubuntu-latest is compute; do not add Buildkite agents',
|
|
67
|
+
},
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
function parseArgs(argv = process.argv.slice(2)) {
|
|
71
|
+
const options = {
|
|
72
|
+
json: false,
|
|
73
|
+
strict: false,
|
|
74
|
+
mapOnly: false,
|
|
75
|
+
annotate: false,
|
|
76
|
+
cloneBuildkite: false,
|
|
77
|
+
migrate: false,
|
|
78
|
+
rerunQueued: false,
|
|
79
|
+
quarantine: false,
|
|
80
|
+
jobsJson: '',
|
|
81
|
+
workflow: '',
|
|
82
|
+
required: REQUIRED_GHA_CONTEXTS.slice(),
|
|
83
|
+
};
|
|
84
|
+
for (const arg of argv) {
|
|
85
|
+
if (arg === '--json') options.json = true;
|
|
86
|
+
else if (arg === '--strict') options.strict = true;
|
|
87
|
+
else if (arg === '--map-only') options.mapOnly = true;
|
|
88
|
+
else if (arg === '--annotate' || arg === '--annotate-job') options.annotate = true;
|
|
89
|
+
else if (arg === '--clone-buildkite') options.cloneBuildkite = true;
|
|
90
|
+
else if (arg === '--migrate' || arg === '--migrate-to-buildkite') options.migrate = true;
|
|
91
|
+
else if (arg === '--rerun-queued') options.rerunQueued = true;
|
|
92
|
+
else if (arg === '--quarantine') options.quarantine = true;
|
|
93
|
+
else if (arg.startsWith('--jobs-json=')) options.jobsJson = arg.slice('--jobs-json='.length);
|
|
94
|
+
else if (arg.startsWith('--workflow=')) options.workflow = arg.slice('--workflow='.length);
|
|
95
|
+
else if (arg.startsWith('--required=')) {
|
|
96
|
+
options.required = arg.slice('--required='.length).split(',').map((s) => s.trim()).filter(Boolean);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return options;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function loadJson(filePath) {
|
|
103
|
+
if (!filePath) return null;
|
|
104
|
+
const abs = path.resolve(filePath);
|
|
105
|
+
return JSON.parse(fs.readFileSync(abs, 'utf8'));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function firstFailedStep(jobs = []) {
|
|
109
|
+
const list = Array.isArray(jobs) ? jobs : [];
|
|
110
|
+
for (const job of list) {
|
|
111
|
+
const jobConclusion = String(job.conclusion || job.status || '').toLowerCase();
|
|
112
|
+
const steps = Array.isArray(job.steps) ? job.steps : [];
|
|
113
|
+
for (const step of steps) {
|
|
114
|
+
const stepConclusion = String(step.conclusion || '').toLowerCase();
|
|
115
|
+
if (stepConclusion === 'failure' || stepConclusion === 'timed_out' || stepConclusion === 'cancelled') {
|
|
116
|
+
return {
|
|
117
|
+
job: job.name || job.id || 'unknown-job',
|
|
118
|
+
step: step.name || 'unknown-step',
|
|
119
|
+
conclusion: step.conclusion,
|
|
120
|
+
number: step.number == null ? null : step.number,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (jobConclusion === 'failure' || jobConclusion === 'timed_out') {
|
|
125
|
+
return {
|
|
126
|
+
job: job.name || job.id || 'unknown-job',
|
|
127
|
+
step: null,
|
|
128
|
+
conclusion: job.conclusion,
|
|
129
|
+
number: null,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function missingRequiredContexts(jobs = [], required = REQUIRED_GHA_CONTEXTS) {
|
|
137
|
+
const names = new Set();
|
|
138
|
+
for (const job of Array.isArray(jobs) ? jobs : []) {
|
|
139
|
+
const n = String(job.name || '');
|
|
140
|
+
if (n) names.add(n);
|
|
141
|
+
const ctx = String(job.context || '');
|
|
142
|
+
if (ctx) names.add(ctx);
|
|
143
|
+
}
|
|
144
|
+
return required.filter((need) => {
|
|
145
|
+
const needle = need.toLowerCase();
|
|
146
|
+
for (const have of names) {
|
|
147
|
+
if (have.toLowerCase() === needle || have.toLowerCase().startsWith(needle.toLowerCase())) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function auditWorkflowText(yaml = '') {
|
|
156
|
+
const text = String(yaml || '');
|
|
157
|
+
const findings = [];
|
|
158
|
+
if (!/^\s*concurrency:\s*$/m.test(text) && !/\nconcurrency:\s*\n/.test(text)) {
|
|
159
|
+
findings.push({
|
|
160
|
+
id: 'missing_concurrency',
|
|
161
|
+
message: 'Workflow has no concurrency group (Buildkite concurrency_group analog).',
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (/cancel-in-progress:\s*true\s*$/m.test(text) && !/refs\/heads\/main/.test(text)) {
|
|
165
|
+
findings.push({
|
|
166
|
+
id: 'cancel_main',
|
|
167
|
+
message: 'cancel-in-progress: true without excluding main — Buildkite fail-fast must not cancel the trunk build.',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (CLONE_RE.test(text) || /uses:\s*buildkite\//i.test(text)) {
|
|
171
|
+
findings.push({
|
|
172
|
+
id: 'clone_buildkite',
|
|
173
|
+
message: 'Workflow vendors Buildkite agents/actions. Keep GitHub Actions native.',
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return findings;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function auditWorkflowFile(filePath) {
|
|
180
|
+
if (!filePath) return [];
|
|
181
|
+
const abs = path.resolve(filePath);
|
|
182
|
+
if (!fs.existsSync(abs)) {
|
|
183
|
+
return [{ id: 'workflow_missing', message: `Workflow not found: ${filePath}` }];
|
|
184
|
+
}
|
|
185
|
+
return auditWorkflowText(fs.readFileSync(abs, 'utf8'));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function renderAnnotation(firstFail, extraLines = []) {
|
|
189
|
+
const lines = [
|
|
190
|
+
'## First failed step (Buildkite FORMAT on GitHub Actions)',
|
|
191
|
+
'',
|
|
192
|
+
'Do not dump logs first. Name the red step, then re-run that command locally.',
|
|
193
|
+
'Do not `gh run rerun` a queued job. Do not add Buildkite as a required check.',
|
|
194
|
+
'',
|
|
195
|
+
];
|
|
196
|
+
if (firstFail) {
|
|
197
|
+
lines.push(`- Job: \`${firstFail.job}\``);
|
|
198
|
+
if (firstFail.step) lines.push(`- Step: \`${firstFail.step}\``);
|
|
199
|
+
if (firstFail.conclusion) lines.push(`- Conclusion: \`${firstFail.conclusion}\``);
|
|
200
|
+
} else {
|
|
201
|
+
lines.push('- No failed step in the provided jobs JSON (scroll the Actions UI for the first red step).');
|
|
202
|
+
}
|
|
203
|
+
for (const line of extraLines) lines.push(line);
|
|
204
|
+
lines.push('');
|
|
205
|
+
return `${lines.join('\n')}\n`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function writeStepSummary(markdown) {
|
|
209
|
+
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
210
|
+
if (!summaryPath) return false;
|
|
211
|
+
fs.appendFileSync(summaryPath, markdown);
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function buildCiGhaBuildkitePatternsReport(args = {}) {
|
|
216
|
+
if (args.mapOnly) {
|
|
217
|
+
return {
|
|
218
|
+
name: 'thumbgate-ci-gha-buildkite-patterns',
|
|
219
|
+
ok: true,
|
|
220
|
+
status: 'ready',
|
|
221
|
+
source: { design: SOURCE_DESIGN, annotations: SOURCE_ANNOTATIONS },
|
|
222
|
+
clone: false,
|
|
223
|
+
vendor: 'github-actions',
|
|
224
|
+
requiredContexts: REQUIRED_GHA_CONTEXTS,
|
|
225
|
+
firstFail: null,
|
|
226
|
+
missingRequired: [],
|
|
227
|
+
findings: [],
|
|
228
|
+
railMap: RAIL_MAP,
|
|
229
|
+
annotationWritten: false,
|
|
230
|
+
note: 'FORMAT steal only. Not affiliated with Buildkite. Public repo stays on GHA.',
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
const findings = [];
|
|
234
|
+
const jobsPayload = args.jobsJson ? loadJson(args.jobsJson) : null;
|
|
235
|
+
const jobs = Array.isArray(jobsPayload)
|
|
236
|
+
? jobsPayload
|
|
237
|
+
: (jobsPayload && Array.isArray(jobsPayload.jobs) ? jobsPayload.jobs : []);
|
|
238
|
+
|
|
239
|
+
if (args.cloneBuildkite) {
|
|
240
|
+
findings.push({
|
|
241
|
+
id: 'clone_buildkite',
|
|
242
|
+
message: 'Refuse Buildkite Pipelines / agents / Test Engine SKU. Steal FORMAT onto GitHub Actions only.',
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (args.migrate || MIGRATE_RE.test(String(args.claim || ''))) {
|
|
246
|
+
findings.push({
|
|
247
|
+
id: 'migrate_vendor',
|
|
248
|
+
message: 'This public repo stays on GitHub-hosted ubuntu-latest. Do not migrate to Buildkite.',
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
if (args.rerunQueued) {
|
|
252
|
+
findings.push({
|
|
253
|
+
id: 'rerun_queued',
|
|
254
|
+
message: 'Never gh run rerun a queued job (no-op / burns minutes). Wait for Actions to drain.',
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
if (args.quarantine) {
|
|
258
|
+
findings.push({
|
|
259
|
+
id: 'auto_quarantine',
|
|
260
|
+
message: 'Refuse Test Engine auto-quarantine. First fail is a real regression until proven otherwise.',
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const firstFail = jobs.length ? firstFailedStep(jobs) : null;
|
|
265
|
+
const missing = jobs.length ? missingRequiredContexts(jobs, args.required || REQUIRED_GHA_CONTEXTS) : [];
|
|
266
|
+
if (missing.length) {
|
|
267
|
+
findings.push({
|
|
268
|
+
id: 'missing_required',
|
|
269
|
+
message: `Required GHA contexts absent: ${missing.join(', ')}. Check githubstatus.com Actions before blaming code.`,
|
|
270
|
+
missing,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const workflowFindings = args.workflow ? auditWorkflowFile(args.workflow) : [];
|
|
275
|
+
findings.push(...workflowFindings);
|
|
276
|
+
|
|
277
|
+
let annotationWritten = false;
|
|
278
|
+
let annotation = '';
|
|
279
|
+
if (args.annotate) {
|
|
280
|
+
annotation = renderAnnotation(firstFail);
|
|
281
|
+
annotationWritten = writeStepSummary(annotation);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const ok = findings.length === 0;
|
|
285
|
+
return {
|
|
286
|
+
name: 'thumbgate-ci-gha-buildkite-patterns',
|
|
287
|
+
ok,
|
|
288
|
+
status: ok ? 'ready' : 'fail',
|
|
289
|
+
source: { design: SOURCE_DESIGN, annotations: SOURCE_ANNOTATIONS },
|
|
290
|
+
clone: false,
|
|
291
|
+
vendor: 'github-actions',
|
|
292
|
+
requiredContexts: args.required || REQUIRED_GHA_CONTEXTS,
|
|
293
|
+
firstFail,
|
|
294
|
+
missingRequired: missing,
|
|
295
|
+
findings,
|
|
296
|
+
railMap: RAIL_MAP,
|
|
297
|
+
annotationWritten,
|
|
298
|
+
annotation: args.annotate ? annotation : undefined,
|
|
299
|
+
note: 'FORMAT steal only. Not affiliated with Buildkite. Public repo stays on GHA.',
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function formatCiGhaBuildkitePatternsReport(report) {
|
|
304
|
+
const lines = [];
|
|
305
|
+
lines.push(`# ${report.name} status=${report.status}`);
|
|
306
|
+
lines.push(`vendor: ${report.vendor} clone: ${report.clone}`);
|
|
307
|
+
if (report.firstFail) {
|
|
308
|
+
lines.push(`first_fail: job=${report.firstFail.job} step=${report.firstFail.step || '(job)'} conclusion=${report.firstFail.conclusion}`);
|
|
309
|
+
} else {
|
|
310
|
+
lines.push('first_fail: (none)');
|
|
311
|
+
}
|
|
312
|
+
if (report.missingRequired && report.missingRequired.length) {
|
|
313
|
+
lines.push(`missing_required: ${report.missingRequired.join(', ')}`);
|
|
314
|
+
}
|
|
315
|
+
if (report.findings.length) {
|
|
316
|
+
lines.push('findings:');
|
|
317
|
+
for (const f of report.findings) lines.push(`- ${f.id}: ${f.message}`);
|
|
318
|
+
} else {
|
|
319
|
+
lines.push('findings: (none)');
|
|
320
|
+
}
|
|
321
|
+
lines.push('rail_map:');
|
|
322
|
+
for (const row of report.railMap) {
|
|
323
|
+
lines.push(`- BK: ${row.buildkite}`);
|
|
324
|
+
lines.push(` GHA: ${row.githubActions}`);
|
|
325
|
+
}
|
|
326
|
+
lines.push(report.note);
|
|
327
|
+
return `${lines.join('\n')}\n`;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function printHelp() {
|
|
331
|
+
process.stdout.write(`Usage: node scripts/ci-gha-buildkite-patterns.js [flags]
|
|
332
|
+
|
|
333
|
+
Buildkite pipeline FORMAT on GitHub Actions. Does not add Buildkite.
|
|
334
|
+
|
|
335
|
+
--json JSON report
|
|
336
|
+
--map-only Rail map only (always ok)
|
|
337
|
+
--jobs-json=path Parse gh run view --json jobs (or {jobs:[...]})
|
|
338
|
+
--workflow=path Audit a workflow YAML
|
|
339
|
+
--annotate Append first-fail block to GITHUB_STEP_SUMMARY
|
|
340
|
+
--strict Exit 1 unless ready
|
|
341
|
+
--clone-buildkite Refuse SKU clone
|
|
342
|
+
--migrate Refuse vendor migration
|
|
343
|
+
--rerun-queued Refuse gh run rerun of queued jobs
|
|
344
|
+
--quarantine Refuse Test Engine auto-quarantine
|
|
345
|
+
`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function runCli(argv = process.argv.slice(2)) {
|
|
349
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
350
|
+
printHelp();
|
|
351
|
+
return 0;
|
|
352
|
+
}
|
|
353
|
+
const args = parseArgs(argv);
|
|
354
|
+
const report = args.mapOnly
|
|
355
|
+
? {
|
|
356
|
+
name: 'thumbgate-ci-gha-buildkite-patterns',
|
|
357
|
+
ok: true,
|
|
358
|
+
status: 'ready',
|
|
359
|
+
source: { design: SOURCE_DESIGN, annotations: SOURCE_ANNOTATIONS },
|
|
360
|
+
clone: false,
|
|
361
|
+
vendor: 'github-actions',
|
|
362
|
+
requiredContexts: REQUIRED_GHA_CONTEXTS,
|
|
363
|
+
firstFail: null,
|
|
364
|
+
missingRequired: [],
|
|
365
|
+
findings: [],
|
|
366
|
+
railMap: RAIL_MAP,
|
|
367
|
+
annotationWritten: false,
|
|
368
|
+
note: 'FORMAT steal only. Not affiliated with Buildkite. Public repo stays on GHA.',
|
|
369
|
+
}
|
|
370
|
+
: buildCiGhaBuildkitePatternsReport(args);
|
|
371
|
+
if (args.json) process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
372
|
+
else process.stdout.write(formatCiGhaBuildkitePatternsReport(report));
|
|
373
|
+
if (args.strict && report.status !== 'ready') return 1;
|
|
374
|
+
if (report.status === 'fail') return 1;
|
|
375
|
+
return 0;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
module.exports = {
|
|
379
|
+
REQUIRED_GHA_CONTEXTS,
|
|
380
|
+
RAIL_MAP,
|
|
381
|
+
parseArgs,
|
|
382
|
+
firstFailedStep,
|
|
383
|
+
missingRequiredContexts,
|
|
384
|
+
auditWorkflowText,
|
|
385
|
+
renderAnnotation,
|
|
386
|
+
buildCiGhaBuildkitePatternsReport,
|
|
387
|
+
formatCiGhaBuildkitePatternsReport,
|
|
388
|
+
runCli,
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
if (path.resolve(process.argv[1] || '') === path.resolve(__filename)) {
|
|
392
|
+
process.exitCode = runCli();
|
|
393
|
+
}
|
package/scripts/cli-schema.js
CHANGED
|
@@ -475,6 +475,21 @@ const CLI_COMMANDS = [
|
|
|
475
475
|
],
|
|
476
476
|
}),
|
|
477
477
|
|
|
478
|
+
|
|
479
|
+
discoveryCommand({
|
|
480
|
+
name: 'board-loop',
|
|
481
|
+
aliases: ['thumbgate-board-loop', 'pr-issue-loop'],
|
|
482
|
+
description: 'Classify+drain Issues/PR wall (BEHIND Dependabot update-branch; never approve)',
|
|
483
|
+
flags: [
|
|
484
|
+
jsonFlag(),
|
|
485
|
+
{ name: 'apply', type: 'boolean', description: 'Perform update-branch / pr:manage / comments (default classify-only)' },
|
|
486
|
+
{ name: 'max-update-branch', type: 'number', description: 'Max BEHIND PRs to update-branch per tick (default 1)' },
|
|
487
|
+
{ name: 'max-pr-manage', type: 'number', description: 'Max READY PRs to submit via pr:manage (default 1)' },
|
|
488
|
+
{ name: 'max-comments', type: 'number', description: 'Max needs-rebase / issue comments (default 4)' },
|
|
489
|
+
{ name: 'skip-pr', type: 'number', description: 'Skip a PR number (e.g. the one just merged)' },
|
|
490
|
+
],
|
|
491
|
+
}),
|
|
492
|
+
|
|
478
493
|
discoveryCommand({
|
|
479
494
|
name: 'colab-compute-honesty',
|
|
480
495
|
aliases: ['colab-honesty', 'compute-unit-honesty'],
|
|
@@ -493,6 +508,39 @@ const CLI_COMMANDS = [
|
|
|
493
508
|
],
|
|
494
509
|
}),
|
|
495
510
|
|
|
511
|
+
discoveryCommand({
|
|
512
|
+
name: 'ci-gha-buildkite-patterns',
|
|
513
|
+
aliases: ['ci-buildkite-patterns', 'gha-buildkite-honesty', 'first-fail-gha'],
|
|
514
|
+
description: 'Buildkite pipeline FORMAT on GitHub Actions: first-fail step, PR fail-fast, needs:/skip/annotations (does not add Buildkite)',
|
|
515
|
+
flags: [
|
|
516
|
+
jsonFlag(),
|
|
517
|
+
{ name: 'strict', type: 'boolean', description: 'Exit non-zero on fail' },
|
|
518
|
+
{ name: 'map-only', type: 'boolean', description: 'Print Buildkite → GHA rail map' },
|
|
519
|
+
{ name: 'jobs-json', type: 'string', description: 'Path to gh run view --json jobs payload' },
|
|
520
|
+
{ name: 'workflow', type: 'string', description: 'Workflow YAML to audit' },
|
|
521
|
+
{ name: 'annotate', type: 'boolean', description: 'Append first-fail block to GITHUB_STEP_SUMMARY' },
|
|
522
|
+
{ name: 'clone-buildkite', type: 'boolean', description: 'Refuse Buildkite SKU clone' },
|
|
523
|
+
{ name: 'migrate', type: 'boolean', description: 'Refuse migrating this repo to Buildkite' },
|
|
524
|
+
],
|
|
525
|
+
}),
|
|
526
|
+
|
|
527
|
+
discoveryCommand({
|
|
528
|
+
name: 'deeppattern-discipline-honesty',
|
|
529
|
+
aliases: ['deeppattern-honesty', 'layer-check-honesty', 'evidence-closeout-honesty', 'aqg-de-honesty'],
|
|
530
|
+
description: 'DeepPattern FORMAT steal: layer-check + evidence-closeout (does not install AQG/Decision Engine)',
|
|
531
|
+
flags: [
|
|
532
|
+
jsonFlag(),
|
|
533
|
+
{ name: 'strict', type: 'boolean', description: 'Exit non-zero on fail' },
|
|
534
|
+
{ name: 'claim', type: 'string', description: 'Comparative / substitute claim to layer-check' },
|
|
535
|
+
{ name: 'subject-layer', type: 'string', description: 'Subject product layer (default L7)' },
|
|
536
|
+
{ name: 'closeout', type: 'string', description: 'Markdown closeout / PR body path' },
|
|
537
|
+
{ name: 'closeout-text', type: 'string', description: 'Inline closeout markdown' },
|
|
538
|
+
{ name: 'map-only', type: 'boolean', description: 'Print DeepPattern → ThumbGate rail map' },
|
|
539
|
+
{ name: 'claim-ready', type: 'boolean', description: 'Fail unless layer-check + closeout are clean' },
|
|
540
|
+
{ name: 'clone-deeppattern', type: 'boolean', description: 'Refuse AQG/DE SKU clone' },
|
|
541
|
+
],
|
|
542
|
+
}),
|
|
543
|
+
|
|
496
544
|
discoveryCommand({
|
|
497
545
|
name: 'typesafe-typed-questions',
|
|
498
546
|
aliases: ['typesafe-hook', 'typed-questions', 'jev-typed-questions'],
|
|
@@ -509,7 +557,9 @@ const CLI_COMMANDS = [
|
|
|
509
557
|
{ name: 'map-only', type: 'boolean', description: 'Print TypeSafe → ThumbGate rail map' },
|
|
510
558
|
{ name: 'claim-ready', type: 'boolean', description: 'Fail unless code owns the route' },
|
|
511
559
|
{ name: 'clone-jev', type: 'boolean', description: 'Refuse TypeSafe/Jev SKU clone' },
|
|
512
|
-
{ name: 'use-typesafe-api', type: 'boolean', description: 'Refuse live api.typesafe.ai as the gate' },
|
|
560
|
+
{ name: 'use-typesafe-api', type: 'boolean', description: 'Refuse live api.typesafe.ai as the PreToolUse gate' },
|
|
561
|
+
{ name: 'live', type: 'boolean', description: 'Shadow battery against System One in one batched POST; code still owns route' },
|
|
562
|
+
{ name: 'fan-out-questions', type: 'boolean', description: 'Refuse N single-question HTTP calls (parallel_questions cookbook)' },
|
|
513
563
|
{ name: 'llm-adjudicate', type: 'boolean', description: 'Refuse LLM adjudicator (#3690/#3687 parked)' },
|
|
514
564
|
{ name: 'model-emitted-verdict', type: 'string', description: 'Refuse a model-emitted pass/review/block' },
|
|
515
565
|
],
|