@cobusgreyling/loop-cost 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +109 -0
- package/dist/estimator.d.ts +64 -0
- package/dist/estimator.js +135 -0
- package/package.json +51 -0
- package/registry.json +294 -0
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# loop-cost
|
|
2
|
+
|
|
3
|
+
Estimate daily token spend for [loop engineering](https://github.com/cobusgreyling/loop-engineering) patterns by cadence and readiness level (L1–L3).
|
|
4
|
+
|
|
5
|
+
Uses cost metadata from `patterns/registry.yaml`.
|
|
6
|
+
|
|
7
|
+
## Install & Run
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @cobusgreyling/loop-cost --pattern ci-sweeper --cadence 15m --level L2
|
|
11
|
+
npx @cobusgreyling/loop-cost --pattern daily-triage --level L1 --json
|
|
12
|
+
npx @cobusgreyling/loop-cost --list
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**From this repo:**
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cd tools/loop-cost
|
|
19
|
+
npm install
|
|
20
|
+
npm test
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Options
|
|
24
|
+
|
|
25
|
+
| Flag | Description |
|
|
26
|
+
|------|-------------|
|
|
27
|
+
| `--pattern` | Pattern id (see `--list`) |
|
|
28
|
+
| `--cadence` | Override cadence (e.g. `15m`, `1d`) |
|
|
29
|
+
| `--level` | `L1`, `L2`, or `L3` (default `L1`) |
|
|
30
|
+
| `--conservative` | Use slower cadence from ranges |
|
|
31
|
+
| `--json` | Machine-readable output |
|
|
32
|
+
|
|
33
|
+
## Scenarios
|
|
34
|
+
|
|
35
|
+
Each estimate includes:
|
|
36
|
+
|
|
37
|
+
- **Early-exit / no-op** — empty watchlist, minimal tokens
|
|
38
|
+
- **Full triage** — every run does a full scan
|
|
39
|
+
- **Action every run** — implementer + verifier every time (worst case)
|
|
40
|
+
- **Realistic blend** — level-based mix (documented in output)
|
|
41
|
+
|
|
42
|
+
Pair with `loop-budget.md` (scaffolded by `loop-init`) and `loop-audit` cost observability checks.
|
|
43
|
+
|
|
44
|
+
See [docs/operating-loops.md](../../docs/operating-loops.md).
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile, access } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import yaml from 'yaml';
|
|
6
|
+
import { estimateCost, formatEstimateHuman, } from './estimator.js';
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
9
|
+
function parseArgs(argv) {
|
|
10
|
+
let pattern = 'daily-triage';
|
|
11
|
+
let cadence;
|
|
12
|
+
let level = 'L1';
|
|
13
|
+
let conservative = false;
|
|
14
|
+
let json = false;
|
|
15
|
+
let list = false;
|
|
16
|
+
for (let i = 0; i < argv.length; i++) {
|
|
17
|
+
const a = argv[i];
|
|
18
|
+
if (a === '--pattern' || a === '-p')
|
|
19
|
+
pattern = argv[++i];
|
|
20
|
+
else if (a === '--cadence' || a === '-c')
|
|
21
|
+
cadence = argv[++i];
|
|
22
|
+
else if (a === '--level' || a === '-l')
|
|
23
|
+
level = argv[++i];
|
|
24
|
+
else if (a === '--conservative')
|
|
25
|
+
conservative = true;
|
|
26
|
+
else if (a === '--json')
|
|
27
|
+
json = true;
|
|
28
|
+
else if (a === '--list')
|
|
29
|
+
list = true;
|
|
30
|
+
else if (a === '--help' || a === '-h')
|
|
31
|
+
return { help: true };
|
|
32
|
+
}
|
|
33
|
+
return { help: false, pattern, cadence, level, conservative, json, list };
|
|
34
|
+
}
|
|
35
|
+
async function loadRegistry() {
|
|
36
|
+
const candidates = [
|
|
37
|
+
path.join(PACKAGE_ROOT, 'registry.json'),
|
|
38
|
+
path.resolve(PACKAGE_ROOT, '../../patterns/registry.yaml'),
|
|
39
|
+
];
|
|
40
|
+
for (const p of candidates) {
|
|
41
|
+
try {
|
|
42
|
+
await access(p);
|
|
43
|
+
const raw = await readFile(p, 'utf8');
|
|
44
|
+
if (p.endsWith('.json'))
|
|
45
|
+
return JSON.parse(raw);
|
|
46
|
+
return yaml.parse(raw);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
/* try next */
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
throw new Error('Pattern registry not found. Run from loop-engineering repo or install @cobusgreyling/loop-cost.');
|
|
53
|
+
}
|
|
54
|
+
async function main() {
|
|
55
|
+
const args = parseArgs(process.argv.slice(2));
|
|
56
|
+
if (args.help) {
|
|
57
|
+
console.log(`loop-cost — estimate daily token spend for loop patterns
|
|
58
|
+
|
|
59
|
+
Usage:
|
|
60
|
+
loop-cost --pattern <id> [options]
|
|
61
|
+
|
|
62
|
+
Options:
|
|
63
|
+
-p, --pattern <id> Pattern id (default: daily-triage)
|
|
64
|
+
-c, --cadence <spec> Override cadence (e.g. 15m, 1d, 5m-15m)
|
|
65
|
+
-l, --level <L1|L2|L3> Readiness level (default: L1)
|
|
66
|
+
--conservative Use slower cadence from ranges (e.g. 15m not 5m)
|
|
67
|
+
--json Machine-readable output
|
|
68
|
+
--list List pattern ids
|
|
69
|
+
-h, --help This help
|
|
70
|
+
|
|
71
|
+
Examples:
|
|
72
|
+
loop-cost --pattern ci-sweeper --cadence 15m --level L2
|
|
73
|
+
loop-cost --pattern daily-triage --level L1 --json
|
|
74
|
+
loop-cost --list
|
|
75
|
+
`);
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
78
|
+
const registry = await loadRegistry();
|
|
79
|
+
if (args.list) {
|
|
80
|
+
for (const p of registry.patterns) {
|
|
81
|
+
console.log(`${p.id}\t${p.token_cost}\t${p.cadence}`);
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const pattern = registry.patterns.find((p) => p.id === args.pattern);
|
|
86
|
+
if (!pattern) {
|
|
87
|
+
console.error(`Unknown pattern: ${args.pattern}. Use --list for ids.`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
if (!pattern.cost) {
|
|
91
|
+
console.error(`Pattern ${args.pattern} has no cost block in registry.`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
const result = estimateCost({
|
|
95
|
+
pattern,
|
|
96
|
+
cadence: args.cadence,
|
|
97
|
+
level: args.level,
|
|
98
|
+
conservative: args.conservative,
|
|
99
|
+
});
|
|
100
|
+
if (args.json)
|
|
101
|
+
console.log(JSON.stringify(result, null, 2));
|
|
102
|
+
else
|
|
103
|
+
console.log(formatEstimateHuman(result));
|
|
104
|
+
}
|
|
105
|
+
main().catch((err) => {
|
|
106
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
107
|
+
console.error('loop-cost failed:', msg);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export type ReadinessLevel = 'L1' | 'L2' | 'L3';
|
|
2
|
+
export interface PatternCost {
|
|
3
|
+
tokens_noop: number;
|
|
4
|
+
tokens_report: number;
|
|
5
|
+
tokens_action: number;
|
|
6
|
+
suggested_daily_cap: number;
|
|
7
|
+
early_exit_required: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface RegistryPattern {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
cadence: string;
|
|
13
|
+
token_cost: string;
|
|
14
|
+
cost: PatternCost;
|
|
15
|
+
}
|
|
16
|
+
export interface RegistryDoc {
|
|
17
|
+
patterns: RegistryPattern[];
|
|
18
|
+
}
|
|
19
|
+
export interface EstimateInput {
|
|
20
|
+
pattern: RegistryPattern;
|
|
21
|
+
cadence?: string;
|
|
22
|
+
level: ReadinessLevel;
|
|
23
|
+
conservative?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface EstimateResult {
|
|
26
|
+
patternId: string;
|
|
27
|
+
patternName: string;
|
|
28
|
+
cadence: string;
|
|
29
|
+
level: ReadinessLevel;
|
|
30
|
+
runsPerDay: number;
|
|
31
|
+
tokenCostTier: string;
|
|
32
|
+
suggestedDailyCap: number;
|
|
33
|
+
earlyExitRequired: boolean;
|
|
34
|
+
scenarios: {
|
|
35
|
+
noop: {
|
|
36
|
+
tokensPerRun: number;
|
|
37
|
+
tokensPerDay: number;
|
|
38
|
+
};
|
|
39
|
+
report: {
|
|
40
|
+
tokensPerRun: number;
|
|
41
|
+
tokensPerDay: number;
|
|
42
|
+
};
|
|
43
|
+
action: {
|
|
44
|
+
tokensPerRun: number;
|
|
45
|
+
tokensPerDay: number;
|
|
46
|
+
};
|
|
47
|
+
realistic: {
|
|
48
|
+
tokensPerRun: number;
|
|
49
|
+
tokensPerDay: number;
|
|
50
|
+
assumptions: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
warnings: string[];
|
|
54
|
+
}
|
|
55
|
+
export declare function parseInterval(token: string): number;
|
|
56
|
+
/** Runs per day for a single interval like 15m or 1d. */
|
|
57
|
+
export declare function runsPerDayForInterval(interval: string): number;
|
|
58
|
+
/**
|
|
59
|
+
* Parse pattern cadence (e.g. 5m-15m, 1d-2h) into runs/day.
|
|
60
|
+
* conservative=true picks the slower cadence in a range.
|
|
61
|
+
*/
|
|
62
|
+
export declare function cadenceToRunsPerDay(cadence: string, conservative?: boolean): number;
|
|
63
|
+
export declare function estimateCost(input: EstimateInput): EstimateResult;
|
|
64
|
+
export declare function formatEstimateHuman(r: EstimateResult): string;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const INTERVAL_MS = {
|
|
2
|
+
m: 60_000,
|
|
3
|
+
h: 3_600_000,
|
|
4
|
+
d: 86_400_000,
|
|
5
|
+
};
|
|
6
|
+
export function parseInterval(token) {
|
|
7
|
+
const m = token.match(/^(\d+)([mhd])$/);
|
|
8
|
+
if (!m)
|
|
9
|
+
throw new Error(`Invalid cadence interval: ${token}`);
|
|
10
|
+
const unit = m[2];
|
|
11
|
+
return Number(m[1]) * INTERVAL_MS[unit];
|
|
12
|
+
}
|
|
13
|
+
/** Runs per day for a single interval like 15m or 1d. */
|
|
14
|
+
export function runsPerDayForInterval(interval) {
|
|
15
|
+
const ms = parseInterval(interval);
|
|
16
|
+
return Math.floor(86_400_000 / ms);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Parse pattern cadence (e.g. 5m-15m, 1d-2h) into runs/day.
|
|
20
|
+
* conservative=true picks the slower cadence in a range.
|
|
21
|
+
*/
|
|
22
|
+
export function cadenceToRunsPerDay(cadence, conservative = false) {
|
|
23
|
+
const parts = cadence.split('-').map((p) => p.trim());
|
|
24
|
+
if (parts.length === 1)
|
|
25
|
+
return runsPerDayForInterval(parts[0]);
|
|
26
|
+
const runs = parts.map(runsPerDayForInterval);
|
|
27
|
+
return conservative ? Math.min(...runs) : Math.max(...runs);
|
|
28
|
+
}
|
|
29
|
+
function realisticMix(level, earlyExitRequired) {
|
|
30
|
+
if (level === 'L1') {
|
|
31
|
+
return {
|
|
32
|
+
noop: earlyExitRequired ? 0.9 : 0.6,
|
|
33
|
+
report: earlyExitRequired ? 0.1 : 0.4,
|
|
34
|
+
action: 0,
|
|
35
|
+
assumptions: earlyExitRequired
|
|
36
|
+
? 'L1: 90% early-exit, 10% full triage'
|
|
37
|
+
: 'L1: 60% no-op, 40% full triage',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (level === 'L2') {
|
|
41
|
+
return {
|
|
42
|
+
noop: earlyExitRequired ? 0.85 : 0.5,
|
|
43
|
+
report: earlyExitRequired ? 0.1 : 0.3,
|
|
44
|
+
action: earlyExitRequired ? 0.05 : 0.2,
|
|
45
|
+
assumptions: earlyExitRequired
|
|
46
|
+
? 'L2: 85% early-exit, 10% triage, 5% implementer+verifier'
|
|
47
|
+
: 'L2: 50% no-op, 30% triage, 20% action',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
noop: 0.4,
|
|
52
|
+
report: 0.35,
|
|
53
|
+
action: 0.25,
|
|
54
|
+
assumptions: 'L3: 40% no-op, 35% triage, 25% action (unattended — monitor closely)',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function formatTokens(n) {
|
|
58
|
+
if (n >= 1_000_000)
|
|
59
|
+
return `${(n / 1_000_000).toFixed(1)}M`;
|
|
60
|
+
if (n >= 1_000)
|
|
61
|
+
return `${Math.round(n / 1_000)}k`;
|
|
62
|
+
return String(n);
|
|
63
|
+
}
|
|
64
|
+
export function estimateCost(input) {
|
|
65
|
+
const cadence = input.cadence ?? input.pattern.cadence;
|
|
66
|
+
const runsPerDay = cadenceToRunsPerDay(cadence, input.conservative);
|
|
67
|
+
const { cost, token_cost: tokenCostTier } = input.pattern;
|
|
68
|
+
const mix = realisticMix(input.level, cost.early_exit_required);
|
|
69
|
+
const noopDay = cost.tokens_noop * runsPerDay;
|
|
70
|
+
const reportDay = cost.tokens_report * runsPerDay;
|
|
71
|
+
const actionDay = cost.tokens_action * runsPerDay;
|
|
72
|
+
const realisticPerRun = cost.tokens_noop * mix.noop +
|
|
73
|
+
cost.tokens_report * mix.report +
|
|
74
|
+
cost.tokens_action * mix.action;
|
|
75
|
+
const realisticDay = Math.round(realisticPerRun * runsPerDay);
|
|
76
|
+
const warnings = [];
|
|
77
|
+
if (cost.early_exit_required) {
|
|
78
|
+
warnings.push('Early-exit triage is required — empty watchlist should exit in <5k tokens.');
|
|
79
|
+
}
|
|
80
|
+
if (actionDay > cost.suggested_daily_cap) {
|
|
81
|
+
warnings.push(`Worst case (action every run) exceeds suggested cap (${formatTokens(cost.suggested_daily_cap)}/day).`);
|
|
82
|
+
}
|
|
83
|
+
if (realisticDay > cost.suggested_daily_cap) {
|
|
84
|
+
warnings.push(`Realistic estimate exceeds suggested daily cap — slow cadence or tighten scope.`);
|
|
85
|
+
}
|
|
86
|
+
if (runsPerDay >= 96) {
|
|
87
|
+
warnings.push(`High cadence (${runsPerDay} runs/day) — verify early-exit is working.`);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
patternId: input.pattern.id,
|
|
91
|
+
patternName: input.pattern.name,
|
|
92
|
+
cadence,
|
|
93
|
+
level: input.level,
|
|
94
|
+
runsPerDay,
|
|
95
|
+
tokenCostTier,
|
|
96
|
+
suggestedDailyCap: cost.suggested_daily_cap,
|
|
97
|
+
earlyExitRequired: cost.early_exit_required,
|
|
98
|
+
scenarios: {
|
|
99
|
+
noop: { tokensPerRun: cost.tokens_noop, tokensPerDay: noopDay },
|
|
100
|
+
report: { tokensPerRun: cost.tokens_report, tokensPerDay: reportDay },
|
|
101
|
+
action: { tokensPerRun: cost.tokens_action, tokensPerDay: actionDay },
|
|
102
|
+
realistic: {
|
|
103
|
+
tokensPerRun: Math.round(realisticPerRun),
|
|
104
|
+
tokensPerDay: realisticDay,
|
|
105
|
+
assumptions: mix.assumptions,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
warnings,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export function formatEstimateHuman(r) {
|
|
112
|
+
const lines = [];
|
|
113
|
+
lines.push('');
|
|
114
|
+
lines.push(`Loop Cost Estimate — ${r.patternName} (${r.patternId})`);
|
|
115
|
+
lines.push('═'.repeat(50));
|
|
116
|
+
lines.push(`Cadence: ${r.cadence} → ${r.runsPerDay} runs/day`);
|
|
117
|
+
lines.push(`Level: ${r.level} · Registry tier: ${r.tokenCostTier}`);
|
|
118
|
+
lines.push(`Suggested daily cap: ${formatTokens(r.suggestedDailyCap)} tokens`);
|
|
119
|
+
lines.push('');
|
|
120
|
+
lines.push('Daily token estimates:');
|
|
121
|
+
lines.push(` Early-exit / no-op: ${formatTokens(r.scenarios.noop.tokensPerDay)} (${formatTokens(r.scenarios.noop.tokensPerRun)}/run)`);
|
|
122
|
+
lines.push(` Full triage: ${formatTokens(r.scenarios.report.tokensPerDay)} (${formatTokens(r.scenarios.report.tokensPerRun)}/run)`);
|
|
123
|
+
lines.push(` Action every run: ${formatTokens(r.scenarios.action.tokensPerDay)} (${formatTokens(r.scenarios.action.tokensPerRun)}/run)`);
|
|
124
|
+
lines.push(` Realistic blend: ${formatTokens(r.scenarios.realistic.tokensPerDay)} (${r.scenarios.realistic.assumptions})`);
|
|
125
|
+
if (r.warnings.length) {
|
|
126
|
+
lines.push('');
|
|
127
|
+
lines.push('Warnings:');
|
|
128
|
+
for (const w of r.warnings)
|
|
129
|
+
lines.push(` ! ${w}`);
|
|
130
|
+
}
|
|
131
|
+
lines.push('');
|
|
132
|
+
lines.push('Docs: docs/operating-loops.md · Scaffold: npx @cobusgreyling/loop-init');
|
|
133
|
+
lines.push('');
|
|
134
|
+
return lines.join('\n');
|
|
135
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cobusgreyling/loop-cost",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Estimate daily token spend for loop engineering patterns by cadence and readiness level.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"loop-cost": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"registry.json",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"bundle": "node scripts/bundle-registry.mjs",
|
|
16
|
+
"build": "npm run bundle && tsc",
|
|
17
|
+
"test": "npm run build && node --test test/estimator.test.mjs",
|
|
18
|
+
"prepublishOnly": "npm test",
|
|
19
|
+
"start": "node dist/cli.js"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"loop-engineering",
|
|
26
|
+
"ai-agents",
|
|
27
|
+
"token-budget",
|
|
28
|
+
"cost-estimate"
|
|
29
|
+
],
|
|
30
|
+
"author": "Cobus Greyling",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/cobusgreyling/loop-engineering.git",
|
|
35
|
+
"directory": "tools/loop-cost"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://cobusgreyling.github.io/loop-engineering/",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/cobusgreyling/loop-engineering/issues"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"yaml": "^2.8.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^20.0.0",
|
|
49
|
+
"typescript": "^5.0.0"
|
|
50
|
+
}
|
|
51
|
+
}
|
package/registry.json
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
{
|
|
2
|
+
"patterns": [
|
|
3
|
+
{
|
|
4
|
+
"id": "pr-babysitter",
|
|
5
|
+
"name": "PR Babysitter",
|
|
6
|
+
"file": "pr-babysitter.md",
|
|
7
|
+
"goal": "Shepherd PRs through review, CI, rebase, and merge",
|
|
8
|
+
"cadence": "5m-15m",
|
|
9
|
+
"risk": "medium",
|
|
10
|
+
"tools": [
|
|
11
|
+
"grok",
|
|
12
|
+
"claude-code",
|
|
13
|
+
"codex",
|
|
14
|
+
"github-actions"
|
|
15
|
+
],
|
|
16
|
+
"skills": [
|
|
17
|
+
"pr-review-triage",
|
|
18
|
+
"minimal-fix",
|
|
19
|
+
"rebase-and-clean"
|
|
20
|
+
],
|
|
21
|
+
"state": "pr-babysitter-state.md",
|
|
22
|
+
"phases": [
|
|
23
|
+
"discover",
|
|
24
|
+
"triage",
|
|
25
|
+
"fix",
|
|
26
|
+
"verify",
|
|
27
|
+
"notify"
|
|
28
|
+
],
|
|
29
|
+
"human_gates": [
|
|
30
|
+
"security",
|
|
31
|
+
"payments",
|
|
32
|
+
"auth",
|
|
33
|
+
"max-fix-attempts"
|
|
34
|
+
],
|
|
35
|
+
"starter": "starters/pr-babysitter",
|
|
36
|
+
"week_one_mode": "L1",
|
|
37
|
+
"token_cost": "high",
|
|
38
|
+
"cost": {
|
|
39
|
+
"tokens_noop": 3000,
|
|
40
|
+
"tokens_report": 80000,
|
|
41
|
+
"tokens_action": 250000,
|
|
42
|
+
"suggested_daily_cap": 2000000,
|
|
43
|
+
"early_exit_required": true
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "daily-triage",
|
|
48
|
+
"name": "Daily Triage",
|
|
49
|
+
"file": "daily-triage.md",
|
|
50
|
+
"goal": "Prioritized morning scan of CI, issues, commits, and chat",
|
|
51
|
+
"cadence": "1d-2h",
|
|
52
|
+
"risk": "low",
|
|
53
|
+
"tools": [
|
|
54
|
+
"grok",
|
|
55
|
+
"claude-code",
|
|
56
|
+
"codex",
|
|
57
|
+
"github-actions"
|
|
58
|
+
],
|
|
59
|
+
"skills": [
|
|
60
|
+
"loop-triage",
|
|
61
|
+
"minimal-fix"
|
|
62
|
+
],
|
|
63
|
+
"state": "STATE.md",
|
|
64
|
+
"phases": [
|
|
65
|
+
"report",
|
|
66
|
+
"act-small-wins",
|
|
67
|
+
"escalate"
|
|
68
|
+
],
|
|
69
|
+
"human_gates": [
|
|
70
|
+
"design-decisions",
|
|
71
|
+
"multi-file-refactors"
|
|
72
|
+
],
|
|
73
|
+
"starter": "starters/minimal-loop",
|
|
74
|
+
"week_one_mode": "L1",
|
|
75
|
+
"token_cost": "low",
|
|
76
|
+
"cost": {
|
|
77
|
+
"tokens_noop": 5000,
|
|
78
|
+
"tokens_report": 50000,
|
|
79
|
+
"tokens_action": 200000,
|
|
80
|
+
"suggested_daily_cap": 100000,
|
|
81
|
+
"early_exit_required": false
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": "ci-sweeper",
|
|
86
|
+
"name": "CI Sweeper",
|
|
87
|
+
"file": "ci-sweeper.md",
|
|
88
|
+
"goal": "React to failing CI with minimal fixes and escalation",
|
|
89
|
+
"cadence": "5m-15m",
|
|
90
|
+
"risk": "medium",
|
|
91
|
+
"tools": [
|
|
92
|
+
"grok",
|
|
93
|
+
"claude-code",
|
|
94
|
+
"codex",
|
|
95
|
+
"github-actions"
|
|
96
|
+
],
|
|
97
|
+
"skills": [
|
|
98
|
+
"ci-triage",
|
|
99
|
+
"minimal-fix"
|
|
100
|
+
],
|
|
101
|
+
"state": "ci-sweeper-state.md",
|
|
102
|
+
"phases": [
|
|
103
|
+
"detect",
|
|
104
|
+
"classify",
|
|
105
|
+
"fix",
|
|
106
|
+
"verify",
|
|
107
|
+
"escalate"
|
|
108
|
+
],
|
|
109
|
+
"human_gates": [
|
|
110
|
+
"infra-failures",
|
|
111
|
+
"max-attempts",
|
|
112
|
+
"security-tests"
|
|
113
|
+
],
|
|
114
|
+
"starter": "starters/ci-sweeper",
|
|
115
|
+
"week_one_mode": "L2",
|
|
116
|
+
"token_cost": "very-high",
|
|
117
|
+
"cost": {
|
|
118
|
+
"tokens_noop": 5000,
|
|
119
|
+
"tokens_report": 50000,
|
|
120
|
+
"tokens_action": 200000,
|
|
121
|
+
"suggested_daily_cap": 1000000,
|
|
122
|
+
"early_exit_required": true
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"id": "post-merge-cleanup",
|
|
127
|
+
"name": "Post-Merge Cleanup",
|
|
128
|
+
"file": "post-merge-cleanup.md",
|
|
129
|
+
"goal": "Follow-up tech debt and cleanup after merges to main",
|
|
130
|
+
"cadence": "1d-6h",
|
|
131
|
+
"risk": "low",
|
|
132
|
+
"tools": [
|
|
133
|
+
"grok",
|
|
134
|
+
"claude-code",
|
|
135
|
+
"codex",
|
|
136
|
+
"github-actions"
|
|
137
|
+
],
|
|
138
|
+
"skills": [
|
|
139
|
+
"post-merge-scan",
|
|
140
|
+
"minimal-fix"
|
|
141
|
+
],
|
|
142
|
+
"state": "post-merge-state.md",
|
|
143
|
+
"phases": [
|
|
144
|
+
"scan-merges",
|
|
145
|
+
"prioritize",
|
|
146
|
+
"fix-small",
|
|
147
|
+
"ticket-large"
|
|
148
|
+
],
|
|
149
|
+
"human_gates": [
|
|
150
|
+
"architectural-debt",
|
|
151
|
+
"feature-flags",
|
|
152
|
+
"large-diffs"
|
|
153
|
+
],
|
|
154
|
+
"starter": "starters/post-merge-cleanup",
|
|
155
|
+
"week_one_mode": "L1",
|
|
156
|
+
"token_cost": "low",
|
|
157
|
+
"cost": {
|
|
158
|
+
"tokens_noop": 5000,
|
|
159
|
+
"tokens_report": 40000,
|
|
160
|
+
"tokens_action": 150000,
|
|
161
|
+
"suggested_daily_cap": 200000,
|
|
162
|
+
"early_exit_required": false
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"id": "dependency-sweeper",
|
|
167
|
+
"name": "Dependency Sweeper",
|
|
168
|
+
"file": "dependency-sweeper.md",
|
|
169
|
+
"goal": "Discover, safely apply, and verify dependency + vulnerability updates with human gates on risky changes",
|
|
170
|
+
"cadence": "6h-1d",
|
|
171
|
+
"risk": "medium",
|
|
172
|
+
"tools": [
|
|
173
|
+
"grok",
|
|
174
|
+
"claude-code",
|
|
175
|
+
"codex",
|
|
176
|
+
"github-actions"
|
|
177
|
+
],
|
|
178
|
+
"skills": [
|
|
179
|
+
"dependency-triage",
|
|
180
|
+
"minimal-fix",
|
|
181
|
+
"loop-verifier"
|
|
182
|
+
],
|
|
183
|
+
"state": "dependency-sweeper-state.md",
|
|
184
|
+
"phases": [
|
|
185
|
+
"scan",
|
|
186
|
+
"triage-risk",
|
|
187
|
+
"patch-safe",
|
|
188
|
+
"verify-worktree",
|
|
189
|
+
"escalate-risky"
|
|
190
|
+
],
|
|
191
|
+
"human_gates": [
|
|
192
|
+
"major-bumps",
|
|
193
|
+
"high-sev-cve",
|
|
194
|
+
"denylisted-packages",
|
|
195
|
+
"max-attempts"
|
|
196
|
+
],
|
|
197
|
+
"starter": "starters/dependency-sweeper",
|
|
198
|
+
"week_one_mode": "L2",
|
|
199
|
+
"token_cost": "medium",
|
|
200
|
+
"cost": {
|
|
201
|
+
"tokens_noop": 5000,
|
|
202
|
+
"tokens_report": 60000,
|
|
203
|
+
"tokens_action": 300000,
|
|
204
|
+
"suggested_daily_cap": 500000,
|
|
205
|
+
"early_exit_required": true
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"id": "changelog-drafter",
|
|
210
|
+
"name": "Changelog Drafter",
|
|
211
|
+
"file": "changelog-drafter.md",
|
|
212
|
+
"goal": "Scan merged PRs and commits, draft categorized high-quality release notes or CHANGELOG entries for human review",
|
|
213
|
+
"cadence": "1d",
|
|
214
|
+
"risk": "low",
|
|
215
|
+
"tools": [
|
|
216
|
+
"grok",
|
|
217
|
+
"claude-code",
|
|
218
|
+
"codex",
|
|
219
|
+
"github-actions"
|
|
220
|
+
],
|
|
221
|
+
"skills": [
|
|
222
|
+
"changelog-scan",
|
|
223
|
+
"draft-release-notes",
|
|
224
|
+
"loop-verifier"
|
|
225
|
+
],
|
|
226
|
+
"state": "changelog-drafter-state.md",
|
|
227
|
+
"phases": [
|
|
228
|
+
"scan-merges",
|
|
229
|
+
"categorize",
|
|
230
|
+
"draft",
|
|
231
|
+
"review",
|
|
232
|
+
"publish"
|
|
233
|
+
],
|
|
234
|
+
"human_gates": [
|
|
235
|
+
"breaking-changes",
|
|
236
|
+
"security",
|
|
237
|
+
"major-features",
|
|
238
|
+
"marketing-sensitive"
|
|
239
|
+
],
|
|
240
|
+
"starter": "starters/changelog-drafter",
|
|
241
|
+
"week_one_mode": "L1",
|
|
242
|
+
"token_cost": "low",
|
|
243
|
+
"cost": {
|
|
244
|
+
"tokens_noop": 5000,
|
|
245
|
+
"tokens_report": 35000,
|
|
246
|
+
"tokens_action": 80000,
|
|
247
|
+
"suggested_daily_cap": 100000,
|
|
248
|
+
"early_exit_required": false
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"id": "issue-triage",
|
|
253
|
+
"name": "Issue Triage",
|
|
254
|
+
"file": "issue-triage.md",
|
|
255
|
+
"goal": "Discover, deduplicate, prioritize and label incoming issues/discussions so the team always has a clean actionable queue. Excellent low-risk companion to Daily Triage.",
|
|
256
|
+
"cadence": "2h-1d",
|
|
257
|
+
"risk": "low",
|
|
258
|
+
"tools": [
|
|
259
|
+
"grok",
|
|
260
|
+
"claude-code",
|
|
261
|
+
"codex",
|
|
262
|
+
"github-actions"
|
|
263
|
+
],
|
|
264
|
+
"skills": [
|
|
265
|
+
"issue-triage",
|
|
266
|
+
"loop-verifier"
|
|
267
|
+
],
|
|
268
|
+
"state": "issue-triage-state.md",
|
|
269
|
+
"phases": [
|
|
270
|
+
"discover",
|
|
271
|
+
"dedupe",
|
|
272
|
+
"score",
|
|
273
|
+
"propose-labels",
|
|
274
|
+
"human-review"
|
|
275
|
+
],
|
|
276
|
+
"human_gates": [
|
|
277
|
+
"security",
|
|
278
|
+
"p0-p1",
|
|
279
|
+
"ambiguous-duplicates",
|
|
280
|
+
"stale-closures"
|
|
281
|
+
],
|
|
282
|
+
"starter": "starters/minimal-loop",
|
|
283
|
+
"week_one_mode": "L1",
|
|
284
|
+
"token_cost": "low",
|
|
285
|
+
"cost": {
|
|
286
|
+
"tokens_noop": 3000,
|
|
287
|
+
"tokens_report": 30000,
|
|
288
|
+
"tokens_action": 60000,
|
|
289
|
+
"suggested_daily_cap": 80000,
|
|
290
|
+
"early_exit_required": false
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
}
|