great-cto 3.28.7 → 3.28.8
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "great-cto",
|
|
3
3
|
"description": "You already have the agent. This is everything around it. great_cto runs Claude Code as a pipeline of 70 specialist agents \u2014 an independent model checks each stage before the next builds on it, spending caps refuse rather than warn, and three decisions stay yours: what gets built, how, and whether it ships.",
|
|
4
|
-
"version": "3.28.
|
|
4
|
+
"version": "3.28.8",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Alexander Velikiy",
|
|
7
7
|
"url": "https://hashnode.com/@Greatcto"
|
|
@@ -13,15 +13,33 @@ import { readVerdicts, readPlanCosts, readQAStats, readSecStats } from './verdic
|
|
|
13
13
|
// accepted change yet" is the honest reading (same discipline as the null-score
|
|
14
14
|
// oracle). Only two of the five metrics the literature lists are built here on
|
|
15
15
|
// purpose — a solo project's denominator is tiny and the other three would be noise.
|
|
16
|
+
//
|
|
17
|
+
// A halting verdict is split three ways by the `need` its author declared:
|
|
18
|
+
// `implementer` is a pass the implementer can make (rework), `decision` is a
|
|
19
|
+
// question for a human, and anything else is undeclared. Counting every halt as
|
|
20
|
+
// rework called a waiver request "rework"; and REWORK itself, the token the
|
|
21
|
+
// dispatcher sends work back with, matched none of the old patterns and was never
|
|
22
|
+
// counted. Undeclared is its own count because it is not known to be either.
|
|
16
23
|
function acceptanceMetrics(verdicts = [], windowCostUsd = null) {
|
|
17
|
-
const
|
|
18
|
-
const
|
|
24
|
+
const token = (v) => String(v?.verdict || '');
|
|
25
|
+
const isAccepted = (v) => /^APPROVED$/i.test(token(v));
|
|
26
|
+
const isSentBack = (v) => /^(REWORK|CHANGES(_REQUESTED)?)$/i.test(token(v));
|
|
27
|
+
const isHalt = (v) => /^(REJECT(ED)?|FAIL(ED)?|BLOCKED)$/i.test(token(v));
|
|
28
|
+
const needOf = (v) => v?.meta?.need;
|
|
29
|
+
let rework_rounds = 0, decisions = 0, undeclared_blocks = 0;
|
|
30
|
+
for (const v of verdicts) {
|
|
31
|
+
if (isSentBack(v)) rework_rounds++;
|
|
32
|
+
else if (isHalt(v)) {
|
|
33
|
+
if (needOf(v) === 'implementer') rework_rounds++;
|
|
34
|
+
else if (needOf(v) === 'decision') decisions++;
|
|
35
|
+
else undeclared_blocks++;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
19
38
|
const accepted = verdicts.filter(isAccepted).length;
|
|
20
|
-
const rework_rounds = verdicts.filter(isRework).length;
|
|
21
39
|
const cost_per_accepted = (accepted > 0 && windowCostUsd != null)
|
|
22
40
|
? Math.round((windowCostUsd / accepted) * 100) / 100
|
|
23
41
|
: null;
|
|
24
|
-
return { accepted, cost_per_accepted, rework_rounds };
|
|
42
|
+
return { accepted, cost_per_accepted, rework_rounds, decisions, undeclared_blocks };
|
|
25
43
|
}
|
|
26
44
|
|
|
27
45
|
// ── Metrics ────────────────────────────────────────────────────────────────────
|
|
@@ -140,6 +140,9 @@ function readVerdicts(cwd = null, health = null) {
|
|
|
140
140
|
agent,
|
|
141
141
|
verdict,
|
|
142
142
|
cost_usd: parsed.rec.cost_usd ?? null,
|
|
143
|
+
// Kept so a metric can read what the author declared (`need`, `task`).
|
|
144
|
+
// Dropping it made every halt look undeclared.
|
|
145
|
+
meta: parsed.rec.meta ?? null,
|
|
143
146
|
raw: line.replace(/\s*\bcost=\$?\d+\.?\d*\b/i, ''),
|
|
144
147
|
});
|
|
145
148
|
}
|
|
@@ -55,6 +55,24 @@ export const KNOWN_VERDICTS = Object.freeze([
|
|
|
55
55
|
* @property {object} [meta] free-form key/value from the caller
|
|
56
56
|
*/
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Who a halting verdict is for, as `meta.need`.
|
|
60
|
+
*
|
|
61
|
+
* implementer — the agent that did the work can fix this; no human choice
|
|
62
|
+
* decision — a waiver, a scope change or a trade-off; a human decides
|
|
63
|
+
*
|
|
64
|
+
* Anything else, including absence, reads as `undeclared`, and undeclared is never
|
|
65
|
+
* routed anywhere: it halts exactly as a BLOCKED always has. qa and security wrote
|
|
66
|
+
* this distinction in prose for months and no verdict line carried it.
|
|
67
|
+
*/
|
|
68
|
+
export const NEED_VALUES = Object.freeze(['implementer', 'decision']);
|
|
69
|
+
|
|
70
|
+
/** 'implementer' | 'decision' | 'undeclared'. Never throws. */
|
|
71
|
+
export function needOf(rec) {
|
|
72
|
+
const n = rec?.meta?.need;
|
|
73
|
+
return NEED_VALUES.includes(n) ? n : 'undeclared';
|
|
74
|
+
}
|
|
75
|
+
|
|
58
76
|
const ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
|
|
59
77
|
|
|
60
78
|
/**
|
|
@@ -97,9 +115,15 @@ export function makeVerdict({ ts, agent, verdict, project, cost_usd, meta, recei
|
|
|
97
115
|
if (receipt && typeof receipt === 'object' && receipt.head) rec.receipt = receipt;
|
|
98
116
|
|
|
99
117
|
const { valid, errors } = validateVerdict(rec);
|
|
118
|
+
// An unknown `need` is refused HERE, at write time, and not in validateVerdict:
|
|
119
|
+
// that also runs on read, where refusing would discard the verdict itself. On
|
|
120
|
+
// read the value survives and needOf() calls it undeclared.
|
|
121
|
+
if (rec.meta && 'need' in rec.meta && !NEED_VALUES.includes(rec.meta.need)) {
|
|
122
|
+
errors.push(`meta.need must be one of ${NEED_VALUES.join(', ')}, got ${JSON.stringify(rec.meta.need)}`);
|
|
123
|
+
}
|
|
100
124
|
// Refusing here is the point: a malformed record that reaches the log is read
|
|
101
125
|
// by something later, and by then nobody knows which agent wrote it.
|
|
102
|
-
if (
|
|
126
|
+
if (errors.length) throw new Error(`invalid verdict record: ${errors.join('; ')}`);
|
|
103
127
|
return rec;
|
|
104
128
|
}
|
|
105
129
|
|
package/package.json
CHANGED