great-cto 3.10.0 → 3.12.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": "3.10.0",
5
+ "version": "3.12.0",
6
6
  "author": {
7
7
  "name": "Great CTO",
8
8
  "url": "https://github.com/avelikiy/great_cto"
@@ -146,8 +146,17 @@ function readVerdicts(cwd = null, health = null) {
146
146
  // verdict in that position, and `\S+` accepted it — producing keys like
147
147
  // `{"v":1,"ts":"202|qa-engineer` that could never match a verdict.
148
148
  // Lines from that era are skipped here instead of being half-parsed.
149
- const m = line.match(/^(\d{4}-\d{2}-\d{2}T\S+)\s+(\S+)\s+(\d+\.?\d*)(?:\s|$)/);
149
+ const m = line.match(/^(\d{4}-\d{2}-\d{2}T\S+)\s+(\S+)\s+(\d+\.?\d*)(?:\s+turns=(\d+))?(?:\s|$)/);
150
150
  if (!m) continue;
151
+ // A cost whose turn count is a whole session is not one agent's cost.
152
+ //
153
+ // The writer records those as `(unattributed)` now, but lines already in
154
+ // this file predate that and carry a real agent name — the board showed
155
+ // $3,385 against four qa-engineer runs under a `measured` label, which is
156
+ // a wrong number wearing the badge of a right one. Skipped here so the
157
+ // history stops asserting it; the line stays on disk because the spend was
158
+ // real and deleting it would understate the total.
159
+ if (m[4] && Number(m[4]) > 400) continue;
151
160
  const usd = parseFloat(m[3]);
152
161
  // A recorded zero is `log-verdict.sh` writing through whatever the agent
153
162
  // reported, which is nothing. Enriching a zero verdict with a zero from
@@ -140,13 +140,24 @@
140
140
  Legal 10px: --fs-eyebrow, and only for mono uppercase labels with tracking.
141
141
  10px in sans body text is not a size, it is a mistake. */
142
142
  :root {
143
- --fs-eyebrow: 10px; /* mono uppercase label, tracking 0.08–0.1em */
144
- --fs-caption: 11px; /* metadata, counts, timestamps */
145
- --fs-small: 12px; /* table cells, secondary meta, chips */
146
- --fs-body: 13px; /* dense body: cards, list rows, form fields */
147
- --fs-base: 14px; /* document base / longform prose */
148
- --fs-title-s: 15px; /* card or section heading (h3 role) */
149
- --fs-title-m: 18px; /* region heading (h2 role) */
143
+ /* The bottom of the ramp was lifted one step on 2026-08-26, and the top was
144
+ not. Measured on the Inbox before the change: 42% of all rendered text sat
145
+ at 11px or smaller, and 11px alone carried 37% of it across 895 elements.
146
+ A step defined as "metadata, counts, timestamps" was doing the work of body
147
+ text the role written here and the role in use had come apart, and the
148
+ operator reported exactly that: small text, hard to read.
149
+
150
+ Titles were left where they were on purpose. The same report called the
151
+ screen overloaded, and enlarging headings would have answered one complaint
152
+ by worsening the other. Density is fixed by showing less, not by shrinking
153
+ what is shown. */
154
+ --fs-eyebrow: 11px; /* mono uppercase label, tracking 0.08–0.1em */
155
+ --fs-caption: 12px; /* metadata, counts, timestamps */
156
+ --fs-small: 13px; /* table cells, secondary meta, chips */
157
+ --fs-body: 14px; /* dense body: cards, list rows, form fields */
158
+ --fs-base: 15px; /* document base / longform prose */
159
+ --fs-title-s: 16px; /* card or section heading (h3 role) */
160
+ --fs-title-m: 19px; /* region heading (h2 role) */
150
161
  --fs-title-l: 22px; /* page heading (h1 role) */
151
162
 
152
163
  --fs-num-s: 24px; /* inline stat */
@@ -367,6 +378,14 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
367
378
  .proj-switch .star { color: var(--accent); font-size: 16px; }
368
379
  .proj-switch .name { font-weight: 600; font-size: var(--fs-base); }
369
380
  .proj-switch .sw-meta { flex: 1; display: flex; flex-direction: column; gap: 1px; }
381
+ .sw-ancestry[hidden] { display: none; }
382
+ .sw-ancestry { font-size: var(--fs-eyebrow); color: var(--text3);
383
+ line-height: 1.35; margin-top: 2px; word-break: break-word;
384
+ /* Two lines at most: this is context, and context that pushes the navigation
385
+ down has stopped being context. The full wire value is on the title. */
386
+ display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
387
+ overflow: hidden;
388
+ }
370
389
  .proj-switch .sw-arch { font-size: var(--fs-eyebrow); color: var(--text3); font-weight: 400; letter-spacing: 0.03em; }
371
390
  .proj-switch .chev {
372
391
  color: var(--text3); width: 12px; height: 12px;
@@ -2647,6 +2666,7 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2647
2666
  <div class="sw-meta">
2648
2667
  <span class="name" id="project-name">—</span>
2649
2668
  <span class="sw-arch" id="proj-switch-arch"></span>
2669
+ <span class="sw-ancestry" id="proj-ancestry" hidden></span>
2650
2670
  </div>
2651
2671
  <svg class="chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
2652
2672
  <div class="proj-menu" id="proj-menu" onclick="event.stopPropagation()">
@@ -2774,8 +2794,16 @@ h1, h2, h3, h4 { font-weight: 600; font-family: var(--sans); }
2774
2794
  </div>
2775
2795
  </div>
2776
2796
 
2777
- <!-- Goal ancestry banner (Paperclip pattern) shows archetype+compliance+phase -->
2778
- <div id="goal-ancestry-banner" style="display:none;font-family:var(--mono,monospace);font-size:var(--fs-caption);color:var(--text3,#6b7280);background:var(--bg-muted);padding:6px 10px;border-radius:6px;margin-bottom:10px;letter-spacing:0.3px;"></div>
2797
+ <!-- Project archetype / compliance / phase used to be a full-width mono
2798
+ row here, between the KPI tiles and the pipeline. On a screen whose
2799
+ title is "what needs your decision" it answered no question the page
2800
+ asks: it is configuration, it does not change day to day, and it was
2801
+ one of three stacked strips competing with the two that ARE
2802
+ actionable. Moved onto the sidebar project block, where the archetype
2803
+ was already shown — same information, one place, and the stack of
2804
+ strips on this page goes from three to two.
2805
+ Not deleted: `PROJECT.md` is not in the Docs list, so compliance and
2806
+ phase would have become unreachable. -->
2779
2807
  <!-- Tool failure rate watchdog warning -->
2780
2808
  <div id="tool-failure-rate" style="display:none;font-size:var(--fs-small);color:var(--status-blocked,#f87171);background:rgba(248,113,113,0.08);padding:6px 10px;border-radius:6px;margin-bottom:10px;"></div>
2781
2809
 
@@ -3501,12 +3529,32 @@ async function refreshHeartbeat() {
3501
3529
  if (!h) return;
3502
3530
 
3503
3531
  // Goal ancestry banner
3504
- const bannerEl = document.getElementById('goal-ancestry-banner');
3532
+ const bannerEl = document.getElementById('proj-ancestry');
3505
3533
  if (bannerEl && h.goal_ancestry) {
3506
- bannerEl.textContent = h.goal_ancestry;
3507
- bannerEl.style.display = 'block';
3534
+ // `[archetype:x] [compliance:[a, b]] [phase:y]` → `x · a, b · y`.
3535
+ //
3536
+ // Parsed rather than regex-chained. The value NESTS — `compliance:[...]`
3537
+ // sits inside its own brackets — and a first attempt that stripped `[` and
3538
+ // `]` globally produced `developer-tools [openssf, api-stability,
3539
+ // soc2-type-2 implementation`: an unbalanced bracket and a lost separator.
3540
+ // The wire format is how the pipeline passes this between stages; rendering
3541
+ // it verbatim is most of why the row read as debug output.
3542
+ const parts = [];
3543
+ const re = /\[([a-z_]+):((?:[^\[\]]|\[[^\]]*\])*)\]/g;
3544
+ let m;
3545
+ while ((m = re.exec(String(h.goal_ancestry))) !== null) {
3546
+ const v = m[2].replace(/^\[|\]$/g, '').trim();
3547
+ if (v) parts.push(v);
3548
+ }
3549
+ bannerEl.textContent = parts.length ? parts.join(' · ') : String(h.goal_ancestry);
3550
+ bannerEl.title = h.goal_ancestry; // the exact wire value stays one hover away
3551
+ // `''`, not `'block'`: an inline display beats the stylesheet, and the
3552
+ // stylesheet is what applies the two-line clamp. Setting it here silently
3553
+ // disabled the clamp and the block ran to three lines, pushing the
3554
+ // navigation down — the opposite of the reason it was moved here.
3555
+ bannerEl.hidden = false;
3508
3556
  } else if (bannerEl) {
3509
- bannerEl.style.display = 'none';
3557
+ bannerEl.hidden = true;
3510
3558
  }
3511
3559
 
3512
3560
  // Tool failure rate warning
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "great-cto",
3
- "version": "3.10.0",
3
+ "version": "3.12.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",