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.86.0",
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 { getMetrics, getCanonicalAgents };
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "great-cto",
3
- "version": "2.86.0",
3
+ "version": "2.87.0",
4
4
  "description": "One command install for the great_cto Claude Code plugin. Auto-detects your stack, picks the right archetype, bootstraps PROJECT.md.",
5
5
  "keywords": [
6
6
  "claude-code",