great-cto 2.86.0 → 2.87.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.
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "great_cto",
|
|
3
3
|
"id": "great_cto",
|
|
4
4
|
"description": "Engineering process for solo founders and teams up to 50 engineers. Agents do architecture, code review, QA, and security. You make two decisions per feature.",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.87.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Great CTO",
|
|
8
8
|
"url": "https://github.com/avelikiy/great_cto"
|
|
@@ -152,6 +152,12 @@
|
|
|
152
152
|
"command": "PLUGIN_DIR=$(ls -d ~/.claude/plugins/cache/local/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||'); node \"${PLUGIN_DIR}/scripts/hooks/frozen-gates-guard.mjs\" 2>&1; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
|
|
153
153
|
"timeout": 5,
|
|
154
154
|
"statusMessage": "Checking frozen gates..."
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"type": "command",
|
|
158
|
+
"command": "PLUGIN_DIR=$(ls -d ~/.claude/plugins/cache/local/great_cto/*/ 2>/dev/null | sort -V | tail -1 | sed 's|/$||'); node \"${PLUGIN_DIR}/scripts/hooks/edit-scope-guard.mjs\" 2>&1; EXIT=$?; if [ $EXIT -eq 2 ]; then exit 2; fi; exit 0",
|
|
159
|
+
"timeout": 5,
|
|
160
|
+
"statusMessage": "Checking edit scope..."
|
|
155
161
|
}
|
|
156
162
|
]
|
|
157
163
|
}
|
|
@@ -5,6 +5,25 @@ import { sseClients, bdCache } from './state.mjs';
|
|
|
5
5
|
import { getTasks } from './beads.mjs';
|
|
6
6
|
import { readVerdicts, readPlanCosts, readQAStats, readSecStats } from './verdicts.mjs';
|
|
7
7
|
|
|
8
|
+
// Acceptance-oriented metrics (the "verified acceptance" shift): measure the cost
|
|
9
|
+
// of a change that a gate actually APPROVED — not throughput — and how often work
|
|
10
|
+
// was sent back before it landed. Both derive from verdict logs already on disk;
|
|
11
|
+
// no new capture. cost_per_accepted is null when nothing was accepted in the
|
|
12
|
+
// window: dividing a real cost by zero approvals would invent a number, and "no
|
|
13
|
+
// accepted change yet" is the honest reading (same discipline as the null-score
|
|
14
|
+
// oracle). Only two of the five metrics the literature lists are built here on
|
|
15
|
+
// purpose — a solo project's denominator is tiny and the other three would be noise.
|
|
16
|
+
function acceptanceMetrics(verdicts = [], windowCostUsd = null) {
|
|
17
|
+
const isAccepted = (v) => /^APPROVED$/i.test(v.verdict || '');
|
|
18
|
+
const isRework = (v) => /^(CHANGES(_REQUESTED)?|REJECT(ED)?|FAIL(ED)?|BLOCKED)$/i.test(v.verdict || '');
|
|
19
|
+
const accepted = verdicts.filter(isAccepted).length;
|
|
20
|
+
const rework_rounds = verdicts.filter(isRework).length;
|
|
21
|
+
const cost_per_accepted = (accepted > 0 && windowCostUsd != null)
|
|
22
|
+
? Math.round((windowCostUsd / accepted) * 100) / 100
|
|
23
|
+
: null;
|
|
24
|
+
return { accepted, cost_per_accepted, rework_rounds };
|
|
25
|
+
}
|
|
26
|
+
|
|
8
27
|
// ── Metrics ────────────────────────────────────────────────────────────────────
|
|
9
28
|
function getMetrics(cwd = process.cwd(), days = 30) {
|
|
10
29
|
// `days` controls the window for cost/agents_cost and for "shipped in window".
|
|
@@ -245,8 +264,14 @@ function getMetrics(cwd = process.cwd(), days = 30) {
|
|
|
245
264
|
// Count tasks completed in selected window (for period-scoped reports)
|
|
246
265
|
const doneInWindow = done.filter(t => t.closed_at && (now - new Date(t.closed_at).getTime()) <= costWindowMs);
|
|
247
266
|
|
|
267
|
+
// Acceptance metrics over the same window as cost, so cost_per_accepted lines
|
|
268
|
+
// up with the cost tile.
|
|
269
|
+
const verdictsInWindow = verdicts.filter(v => v.ts && (now - new Date(v.ts).getTime()) <= costWindowMs);
|
|
270
|
+
const acceptance = acceptanceMetrics(verdictsInWindow, cost.llm_usd);
|
|
271
|
+
|
|
248
272
|
return {
|
|
249
273
|
window_days: days,
|
|
274
|
+
acceptance,
|
|
250
275
|
tasks: {
|
|
251
276
|
total: tasks.length,
|
|
252
277
|
done: done.length,
|
|
@@ -304,4 +329,5 @@ function getCanonicalAgents() {
|
|
|
304
329
|
return set;
|
|
305
330
|
}
|
|
306
331
|
|
|
307
|
-
export {
|
|
332
|
+
export {
|
|
333
|
+
acceptanceMetrics, getMetrics, getCanonicalAgents };
|
|
@@ -4174,9 +4174,15 @@ function renderDashboard(m) {
|
|
|
4174
4174
|
<div class="metric-trend">${s.trend}</div>
|
|
4175
4175
|
</div>`).join('');
|
|
4176
4176
|
|
|
4177
|
+
const acc = m.acceptance || {};
|
|
4177
4178
|
const secondary = [
|
|
4178
4179
|
{ v: avgMin ? `${avgMin}` : '—', sub: avgMin ? 'm' : '', label: cycleStat },
|
|
4179
4180
|
{ v: m.qa?.pass_rate != null ? m.qa.pass_rate : '—', sub: m.qa?.pass_rate != null ? '%' : '', label: m.qa?.pass_rate != null ? 'QA pass rate' : 'QA — run /qa', cls: 'green' },
|
|
4181
|
+
// Verified-acceptance metrics: cost of a change a gate actually APPROVED, and
|
|
4182
|
+
// how often work bounced. '—' when nothing was accepted in the window — an
|
|
4183
|
+
// honest gap, not a zero.
|
|
4184
|
+
{ v: acc.cost_per_accepted != null ? `$${acc.cost_per_accepted.toFixed(2)}` : '—', sub: '', label: acc.accepted ? `Cost / accepted (${acc.accepted})` : 'Cost / accepted' },
|
|
4185
|
+
{ v: acc.rework_rounds ?? 0, sub: '', label: 'Rework rounds', cls: acc.rework_rounds ? 'amber' : 'green' },
|
|
4180
4186
|
{ v: m.security?.blocked ?? 0, sub: '', label: 'Open security blocks', cls: m.security?.blocked ? 'red' : 'green' },
|
|
4181
4187
|
{ v: m.tasks?.in_progress ?? 0, sub: '', label: 'In progress', cls: 'amber' },
|
|
4182
4188
|
];
|
package/package.json
CHANGED