@tekyzinc/gsd-t 5.19.11 → 5.20.11

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/bin/gsd-t.js CHANGED
@@ -1910,6 +1910,10 @@ const GLOBAL_BIN_TOOLS = [
1910
1910
  // caught four times before ([[project_global_bin_propagation_gap]]). Also in
1911
1911
  // PROJECT_BIN_TOOLS below — both lists, both tools.
1912
1912
  "gsd-t-testplan-lint.cjs", "gsd-t-testplan-halt.cjs", "gsd-t-testplan-rows.cjs",
1913
+ // v5.20.10 — deterministic Tekyz estimate-sheet writer + read-back audit
1914
+ // (`gsd-t estimate-sheet`). Dispatched by bin/gsd-t.js AND read by
1915
+ // /gsd-t-estimate, so it ships in BOTH lists ([[project_global_bin_propagation_gap]]).
1916
+ "gsd-t-estimate-sheet.cjs",
1913
1917
  ];
1914
1918
 
1915
1919
  // Directories under bin/ that must ship whole. A runner whose parts stay behind
@@ -2180,6 +2184,7 @@ const SHARED_TEMPLATES = [
2180
2184
  "design-contract.md",
2181
2185
  "shared-services-contract.md",
2182
2186
  "estimate-config.json",
2187
+ "estimate-sheet-spec.md",
2183
2188
  ];
2184
2189
 
2185
2190
  function installSharedTemplates() {
@@ -3461,6 +3466,20 @@ async function doUpdateAll() {
3461
3466
  );
3462
3467
  }
3463
3468
 
3469
+ // Numeric dotted-version compare: negative when a < b, 0 when equal, positive when a > b.
3470
+ // GSD-T versions are plain Major.Minor.Patch integers (patch always ≥ 10), so a
3471
+ // segment-wise numeric compare is exact; no prerelease tags exist.
3472
+ function versionCmp(a, b) {
3473
+ const pa = String(a).split(".").map((n) => parseInt(n, 10));
3474
+ const pb = String(b).split(".").map((n) => parseInt(n, 10));
3475
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
3476
+ const x = Number.isNaN(pa[i]) ? 0 : pa[i];
3477
+ const y = Number.isNaN(pb[i]) ? 0 : pb[i];
3478
+ if (x !== y) return x - y;
3479
+ }
3480
+ return 0;
3481
+ }
3482
+
3464
3483
  // Upgrade the globally-installed @tekyzinc/gsd-t to @latest. Returns
3465
3484
  // { upgraded: bool, reexec: bool, error?: string }.
3466
3485
  // - reexec=true when the on-disk version after `npm install -g` is newer than
@@ -3494,6 +3513,19 @@ async function upgradeGlobalBinary() {
3494
3513
  // Best-effort; fall through.
3495
3514
  }
3496
3515
 
3516
+ if (newVersion && versionCmp(newVersion, PKG_VERSION) < 0) {
3517
+ // v5.20.10 (2026-09-17): right after `npm publish`, npm's cached package
3518
+ // listing still named the PREVIOUS version as @latest, so this step
3519
+ // installed 5.19.11 over a running 5.20.10, reported it as an "upgrade",
3520
+ // handed off to the OLD binary, and that binary overwrote ~/.claude/commands
3521
+ // and every project with the old release. A downgrade is never an upgrade —
3522
+ // HALT and say how to fix it, never continue on a stale listing.
3523
+ error(`HALT: npm installed v${newVersion} over the running v${PKG_VERSION} — @latest resolved to an OLDER release.`);
3524
+ info("npm's cached package listing is stale. Run `npm cache clean --force`, wait until");
3525
+ info(`\`curl -sI https://registry.npmjs.org/@tekyzinc/gsd-t/-/gsd-t-${PKG_VERSION}.tgz\` returns 200, then re-run update-all.`);
3526
+ info(`Restore the current release first: npm install -g @tekyzinc/gsd-t@${PKG_VERSION}`);
3527
+ process.exit(1);
3528
+ }
3497
3529
  if (newVersion && newVersion !== PKG_VERSION) {
3498
3530
  success(`Global binary upgraded: v${PKG_VERSION} → v${newVersion}`);
3499
3531
  info("Handing off to the newly-installed binary for propagation...");
@@ -3701,6 +3733,10 @@ const PROJECT_BIN_TOOLS = [
3701
3733
  // and /gsd-t-test-plan calls them directly — same propagation-gap class as the
3702
3734
  // graph tools above. Also in GLOBAL_BIN_TOOLS — both lists, both tools.
3703
3735
  "gsd-t-testplan-lint.cjs", "gsd-t-testplan-halt.cjs", "gsd-t-testplan-rows.cjs",
3736
+ // v5.20.10 — deterministic Tekyz estimate-sheet writer + read-back audit
3737
+ // (`gsd-t estimate-sheet`). Dispatched by bin/gsd-t.js AND read by
3738
+ // /gsd-t-estimate, so it ships in BOTH lists ([[project_global_bin_propagation_gap]]).
3739
+ "gsd-t-estimate-sheet.cjs",
3704
3740
  ];
3705
3741
 
3706
3742
  // Files that older versions of this installer copied into project bin/ but
@@ -5990,6 +6026,14 @@ if (require.main === module) {
5990
6026
  });
5991
6027
  process.exit(res.status == null ? 1 : res.status);
5992
6028
  }
6029
+ case "estimate-sheet": {
6030
+ // v5.20.10 — deterministic Tekyz estimate-sheet writer + audit
6031
+ // (`gsd-t estimate-sheet read|plan-check|write|audit --sheet <id>`).
6032
+ const { spawnSync } = require("child_process");
6033
+ const js = path.join(__dirname, "gsd-t-estimate-sheet.cjs");
6034
+ const res = spawnSync(process.execPath, [js, ...args.slice(1)], { stdio: "inherit" });
6035
+ process.exit(res.status == null ? 1 : res.status);
6036
+ }
5993
6037
  case "test-data": {
5994
6038
  // M58 D1 — `gsd-t test-data --list|--purge` thin dispatcher.
5995
6039
  const { spawnSync } = require("child_process");
package/commands/cpua.md CHANGED
@@ -120,6 +120,19 @@ G="$(npm root -g)/@tekyzinc/gsd-t"
120
120
  # the pre-publish package listing cached and `@tekyzinc/gsd-t@{NEW}` returns ETARGET
121
121
  # for minutes while `npm view` already shows the version (v5.17.14, 2026-09-03).
122
122
  # The tarball URL bypasses the cached listing and is deterministic.
123
+ # 1b. WAIT until the registry actually SERVES the tarball, then CLEAR npm's cached listing.
124
+ # v5.20.10 (2026-09-17): `npm publish` printed "+ @tekyzinc/gsd-t@5.20.10" and `npm view`
125
+ # showed it, but the tarball URL 404'd for ~4 minutes. Worse, npm's cached package
126
+ # listing still said latest = the PREVIOUS version, so `gsd-t update-all` (which runs
127
+ # `npm install -g @tekyzinc/gsd-t@latest`) DOWNGRADED the global, handed off to the old
128
+ # binary, and that binary rewrote ~/.claude/commands and 34 projects with the old release
129
+ # — twice. Poll the tarball, then clean the cache, then install. Never skip either.
130
+ for i in $(seq 1 60); do
131
+ [ "$(curl -s -o /dev/null -w '%{http_code}' "https://registry.npmjs.org/@tekyzinc/gsd-t/-/gsd-t-{NEW_VERSION}.tgz")" = "200" ] && break
132
+ sleep 10
133
+ done
134
+ [ "$(curl -s -o /dev/null -w '%{http_code}' "https://registry.npmjs.org/@tekyzinc/gsd-t/-/gsd-t-{NEW_VERSION}.tgz")" = "200" ] || { echo "HALT: tarball still 404 after 10 min"; exit 1; }
135
+ npm cache clean --force
123
136
  npm install -g "https://registry.npmjs.org/@tekyzinc/gsd-t/-/gsd-t-{NEW_VERSION}.tgz"
124
137
  # 2. VERIFY ON DISK — never trust npm's exit code for this step. On v5.17.13 `npm install -g`
125
138
  # reported success and left the OLD version in place; `update-all` then ran from the
@@ -140,7 +153,11 @@ fi
140
153
  # file and the command files; only then does update-all see the new release.
141
154
  gsd-t install 2>&1 | tail -5
142
155
  [ "$(cat ~/.claude/.gsd-t-version)" = "{NEW_VERSION}" ] || { echo "HALT: ~/.claude/.gsd-t-version did not advance"; exit 1; }
143
- # 4. Propagate, then prove the global is STILL the new version and a NEW file reached a project.
156
+ # 4. Prove `@latest` now resolves to the NEW version (update-all installs `@latest`; a stale
157
+ # listing here is the downgrade path — since v5.20.11 update-all HALTS on a downgrade
158
+ # instead of handing off, but do not rely on the halt: check first).
159
+ [ "$(npm view @tekyzinc/gsd-t dist-tags.latest)" = "{NEW_VERSION}" ] || { echo "HALT: registry latest is not {NEW_VERSION}"; exit 1; }
160
+ # 5. Propagate, then prove the global is STILL the new version and a NEW file reached a project.
144
161
  gsd-t update-all 2>&1 | tail -30
145
162
  [ "$(node -p "require('$G/package.json').version")" = "{NEW_VERSION}" ] || { echo "HALT: global package reverted during update-all"; exit 1; }
146
163
  ```
@@ -1,8 +1,10 @@
1
- # GSD-T: Estimate — Tekyz Client Estimate + PRD from any Work Document
1
+ # GSD-T: Estimate — Tekyz Client Estimate (T-Shirt Size + Team Mix + Technology Stack)
2
2
 
3
- You are turning a **structured work document** into a **Tekyz client estimate** (a Google Sheet with a T-Shirt-Size tab + a Team-Mix cross-check) and a matching **PRD deliverable**. The input can be a GSD-T tech-debt scan register, a **new-feature requirements doc**, a **new-application requirements doc**, an existing PRD, or any comparable spec. `$ARGUMENTS` may carry the input path + a scope override (e.g. `--severity high`, `--input docs/requirements.md`).
3
+ You are turning a **structured work document** into a **Tekyz client estimate** in a Google Sheet: a **T-Shirt Size** tab (sized line-items → days → dollars), a **Team Mix** tab (who does the work, month by month), and a **Technology Stack** tab. The input can be a GSD-T tech-debt scan register, a gap-analysis sheet, a new-feature or new-application requirements doc, or any comparable spec. `$ARGUMENTS` may carry `--sheet <url>`, `--input <path>`, and a scope override (`--severity high`).
4
4
 
5
- **Full proven procedure:** read `~/.claude/playbooks/tekyz-estimation-and-prd-playbook.md` if present, else the bundled copy `templates/playbooks/tekyz-estimation-and-prd-playbook.md` in the GSD-T package (produced on the HILO Figma ATOS project: 21 criticals → 32.73 eng-days → $13,090–$16,362). Supporting memories (in the originating project's memory dir): `tekyz-estimation-method`, `tekyz-familiarization-bump`, `tekyz-client-prd-structure`, `tekyz-tech-debt-numbering-caution`, `google-sheets-service-account-workaround`. This skill ENCODES those; read them for edge-case depth.
5
+ **Scope is the SHEET only.** This command does NOT write a PRD or any other document — a client PRD is `/gsd-t-prd`'s job, run separately if wanted. The one client-facing artifact here is the sheet; the only optional file is `share/<Repo>-estimate-redteam-notes.md` (overridden Red Team objections).
6
+
7
+ **THE SHEET IS WRITTEN BY A TOOL, NOT BY HAND.** `gsd-t estimate-sheet` (`bin/gsd-t-estimate-sheet.cjs`, project-local `bin/` first, else the global `gsd-t`) reads the sheet, validates a JSON plan, writes all three tabs with the exact formulas and styling, and audits by reading back — halting on any violation. **You never PUT a cell or send a batchUpdate yourself.** Your output is the plan (judgment only: sizes, team FTE, tech-stack lines); the tool computes everything derived. Spec: `~/.claude/templates/estimate-sheet-spec.md` (bundled: `templates/estimate-sheet-spec.md`) — §6 shows the plan shape (`gsd-t estimate-sheet plan-schema` prints it). Procedure background: `~/.claude/playbooks/tekyz-estimation-and-prd-playbook.md` (else `templates/playbooks/`).
6
8
 
7
9
  > **Client-billed work, not GSD-T build work.** This produces a paid client estimate — the "no cost estimates" rule (`feedback_no_human_hour_estimates`) governs GSD-T's OWN Max-funded build work, NOT client deliverables. Dollar figures here are correct and expected.
8
10
 
@@ -10,180 +12,159 @@ You are turning a **structured work document** into a **Tekyz client estimate**
10
12
 
11
13
  This process is judgment-heavy. **You (the operator) are the final arbiter of every estimate.** The command does NOT run end-to-end autonomously.
12
14
 
13
- - **Judgment phases PAUSE for review** before advancing: **Step 2 (sizing)**, **Step 2.5 (adjustments)**, **Step 6 (PRD)**, **Step 8 (Red Team)**. Present the output, wait for the user's `continue` or corrections.
14
- - **Mechanical phases FLOW but SHOW their result**: Step 1 (numbering), Step 5 (sheet write), Step 7 (reconcile). Don't block on these, but display what happened so nothing is invisible.
15
+ - **Judgment phases PAUSE for review** before advancing: **Step 2 (sizing)**, **Step 2.5 (adjustments)**, **Step 4 (Team Mix roster + ramp)**, **Step 8 (Red Team)**. Present the output, wait for `continue` or corrections.
16
+ - **Mechanical phases FLOW but SHOW their result**: Step 1 (numbering), Step 6 (sheet write + audit), Step 7 (reconcile). Don't block, but display what happened so nothing is invisible.
17
+ - **Never skip Step 2.5 or Step 8 silently.** Both were skipped on shipped estimates and the operator had to ask for them afterwards. If you skip one, say so in the report and why.
15
18
  - **Escape hatch:** if the user says e.g. "run through sizing and grouping, then stop," batch those phases and pause where they asked.
16
19
 
17
20
  ## Configuration (parameterized — defaults are Tekyz values)
18
21
 
19
- Read these from `$ARGUMENTS` or `.gsd-t/estimate-config.json` if present; otherwise use the Tekyz defaults. **Always name the active values in the report** (no silent defaults). This config is **optional and NOT auto-created** (it's Tekyz-estimate-specific, not every-project state) — to override the defaults, copy the documented template `templates/estimate-config.json` (in the GSD-T package, or `~/.claude/templates/estimate-config.json`) to `.gsd-t/estimate-config.json` and edit. Every field is individually optional; an omitted field falls back to its Tekyz default.
22
+ Read from `$ARGUMENTS` or `.gsd-t/estimate-config.json` if present; otherwise use the Tekyz defaults. **Always name the active values in the report** (no silent defaults). The config is **optional and NOT auto-created** — to override, copy `templates/estimate-config.json` (or `~/.claude/templates/estimate-config.json`) to `.gsd-t/estimate-config.json` and edit. Every field is individually optional.
20
23
 
21
24
  | Param | Default (Tekyz) | Meaning |
22
25
  |-------|-----------------|---------|
23
26
  | `rate` | `$50/hr` | Blended hourly rate for the LOW figure. |
24
27
  | `hoursPerDay` | `8` | Hours per person-day. |
25
28
  | `sizeScale` | `XS 0.25 · S 0.5 · M 1 · L 3 · XL 5 · XXL 7` | T-shirt → person-days. |
26
- | `totalMF` | `0.7` | Overhead multiplier = QA 0.3 + PM 0.1 + Analysis 0.05 + Deployment 0.05 + Buffer 0.2. |
27
- | `highFactor` | `1.25` | HIGH = LOW × this. |
28
- | `sheetTemplateId` | (blank) | Optional template to clone; normally blank — operator pastes the target sheet URL at Step 0. |
29
+ | `totalMF` | `0.7` | Overhead multiplier. **The sheet's own MF list (`E4:F9`) wins when a sheet exists** — read it, never overwrite it. Hilo sheets run `0.9` (QA .3 · PM .1 · Analysis .1 · Deployment .05 · StdUps/Mtgs .15 · Buffer .2). |
30
+ | `highFactor` | `1.25` | HIGH = LOW × this (the sheet's `G4` wins when a sheet exists). |
31
+ | `sheetTemplateId` | (blank) | Optional template to clone; normally blank — the operator supplies the target sheet. |
29
32
  | `gcpProject` | `ai-estimator-415612` | GCP project hosting the permanent Sheets-writer SA. |
30
33
  | `serviceAccountEmail` | `gsd-t-sheets-writer@ai-estimator-415612.iam.gserviceaccount.com` | **Permanent** SA — share each sheet with this as Editor. |
31
34
  | `serviceAccountKeyPath` | `~/.claude/gsd-t-secrets/gsd-t-sheets-writer-key.json` | SA key (chmod 600, outside any repo). |
35
+ | `newTeamDefault` | `true` | Apply the new-team familiarization adjustment (Step 2.5a) by default. |
32
36
 
33
37
  ## Step 0: Inputs + Scope + Sheet
34
38
 
35
- 1. **ASK FOR THE GOOGLE SHEET URL FIRST.** The operator ALWAYS provides an existing sheet to edit — this command never creates the sheet. Prompt: *"Paste the Google Sheet URL for this estimate."* Extract the **sheet ID** = the segment between `/d/` and the next `/` in `https://docs.google.com/spreadsheets/d/<ID>/edit`. Confirm the ID back to the operator.
36
- 2. **Ensure the sheet is shared with the permanent estimate service account** (details in Step 5). The reusable SA email is **`gsd-t-sheets-writer@ai-estimator-415612.iam.gserviceaccount.com`**. Do a quick read-probe (Step 5's JWT→token→`GET spreadsheets/<id>`): if it returns `403`, the sheet isn't shared yet → prompt the operator to share it with that email as **Editor** ("Notify people" unchecked), wait for confirmation, re-probe. If the SA/key doesn't exist yet, Step 5 provisions it once.
37
- 3. **Resolve the input document** (from `$ARGUMENTS --input`, else default to `.gsd-t/techdebt.md`). If none found → "No input document found. Pass `--input <path>` or run `/gsd-t-scan` first." and stop.
38
- 4. **Classify the input** so the parser + line-item vocabulary match:
39
- - **Scan register** (`.gsd-t/techdebt.md`) → line-items are *findings* (`TD-n`), scoped by severity.
40
- - **Requirements doc / feature spec / app spec / PRD-in** → line-items are *requirements* (`FR-n` or the doc's own numbering).
41
- 5. **Scope** (from `$ARGUMENTS`): scan default = **all CRITICAL findings** ("close the critical gap"); `--severity high|medium|low|all` widens it. Requirements default = **all requirements** unless the user narrows. Confirm the scope + item count with the user before sizing.
42
- 6. Confirm the active config values above (rate/MF/highFactor — from `.gsd-t/estimate-config.json` or Tekyz defaults).
43
- 7. Decide whether this is a **new-team project** (triggers the familiarization adjustment, Step 2.5) — usually YES for a fresh client.
39
+ 1. **Resolve the Google Sheet.** Take `--sheet <url>` from `$ARGUMENTS`; otherwise ask: *"Paste the Google Sheet URL for this estimate."* The operator ALWAYS provides an existing sheet — this command never creates one. Extract the **sheet ID** (between `/d/` and the next `/`) and confirm it back.
40
+ 2. **Probe access + read the layout in one step:** `gsd-t estimate-sheet read --sheet <url>` (then `--tab "T-Shirt Size Estimate"`, `--tab "Team Mix"`). A `403` halt means the sheet is not shared — prompt the operator to share it with the SA email as **Editor** ("Notify people" unchecked), wait, re-run. (The tool never sends `X-Goog-User-Project`; with it these sheets 403 even when shared.)
41
+ 3. From the read: note the MF list (`E4:F9`), high factor (`G4`), rate (`H4`), whether item rows already exist (→ `sizes` mode) or the tab is empty (→ `items` mode), and whether the Technology Stack tab is filled. The tool reads these again at write time; you read them now so the plan matches the sheet.
42
+ 4. **Resolve the input document** (`--input`, else `.gsd-t/techdebt.md`). None → "No input document found. Pass `--input <path>` or run `/gsd-t-scan` / `/gsd-t-gap-analysis` first." and stop.
43
+ 5. **Classify the input** so the line-item vocabulary matches: scan register → *findings* (`TD-n`) scoped by severity; gap-analysis sheet → *gaps* (`GA-n`, rows already on the T-Shirt tab with columns A–D filled — you fill E–G only); requirements / feature / app spec → *requirements* (`FR-n` or the doc's own numbering).
44
+ 6. **Scope**: scan default = all CRITICAL findings; `--severity high|medium|low|all` widens. Requirements default = all. Confirm scope + item count with the user before sizing.
45
+ 7. **Confirm the active config values** — rate, MF list (from the sheet), high factor, and whether this is a **new-team project** (Step 2.5a; usually YES for a fresh client).
44
46
 
45
47
  ## Step 1: Numbering hygiene (MECHANICAL — show result)
46
48
 
47
- Client-facing line-items must carry **sequential, rational numbering starting at 1**. Requirements docs often have NO numbers, or hierarchical numbers, or (rarely) a scan that crashed-and-reran starts high (e.g. TD-618, which looks bad — "why does #1 start at 618?"). (`tekyz-tech-debt-numbering-caution`.)
48
-
49
- **Renumber ONLY when the numbering is absent, non-sequential, or doesn't start at 1.** If it is already sequential and rational, LEAVE IT.
49
+ Client-facing line-items carry **sequential, rational numbering starting at 1**. Renumber ONLY when numbering is absent, non-sequential, or doesn't start at 1 (a crashed-and-rerun scan starting at TD-618 looks bad). Already clean → leave it.
50
50
 
51
- - **Unnumbered input** → assign sequential ids (`FR-1, FR-2, …` or `TD-1, …`).
52
- - **Hierarchical numbering** (`1`, `1.1`, `1.1.1` = requirement / sub / sub-sub) → **preserve the hierarchy**; renumber only to make each level sequential-and-rational (no gaps, starts at 1 within its parent). Never flatten a hierarchy.
53
- - **Already-clean numbering** → no change.
54
- - When you DO renumber, do it SAFELY (numbering-only, zero value changes):
55
- - Confirm the id range is contiguous first, so a blind offset aligns.
56
- - **Range-bounded regex** — remap ONLY numbers in the doc's actual range, so you never corrupt `DC-n`, another project's `TD-4`, or a different repo's numbering.
57
- - **Scope to CURRENT deliverables only:** the input doc, plain-English companion, `.gsd-t/scan/*.md`, `docs/*.md`, README, the PRD, `share/*`, and the Google Sheet labels. **Leave `.gsd-t/scan/archive/`, transcripts, heartbeats UNTOUCHED.**
58
- - **Second pass for non-prefixed formats:** bare `| 618 |` table cells, slashed chains (`TD-2/623`), header text. A first-pass `PREFIX-NNN` regex misses these.
59
- - **Back up files before the bulk edit.**
51
+ - **Unnumbered** → assign sequential ids (`FR-1, FR-2, …` or `TD-1, …`).
52
+ - **Hierarchical** (`1`, `1.1`, `1.1.1`) → preserve the hierarchy; renumber only to make each level sequential within its parent. Never flatten.
53
+ - When you DO renumber, do it SAFELY: confirm the id range is contiguous; **range-bounded regex** so you never touch `DC-n` or another project's numbering; scope to CURRENT deliverables only (input doc, plain-English companion, `.gsd-t/scan/*.md`, `docs/*.md`, README, `share/*`, the sheet labels) — **leave `.gsd-t/scan/archive/`, transcripts, heartbeats untouched**; second pass for non-prefixed forms (`| 618 |`, `TD-2/623`, headers); **back up files first**.
60
54
  - A client that wants the original numbering keeps it — offer, don't force.
61
55
 
62
56
  ## Step 2: Size each line-item (T-Shirt Size tab) — JUDGMENT · PAUSE FOR REVIEW
63
57
 
64
- For each in-scope item, build a row — cols **A** Module · **B** User Type · **C** Functionality (**include the `(TD-n)` / `(FR-n)`**) · **D** Low-Level Requirement · **E** Phase (MVP) · **F** Web Portal (frontend) size · **G** Backend/API size. **Leave H–L (formulas) alone.**
58
+ For each in-scope item build a row per spec §1.2 — `A` Module · `B` User Type · `C` Functionality (**with the item id**) · `D` Low-Level Requirement · `E` Phase · `F` Web Portal size · `G` Backend/API size. `H:L` are formulas, never values.
65
59
 
66
- - **Size each column INDEPENDENTLY** (FE and BE each get their own letter; blank = 0). Sizes per `sizeScale`: **XS 0.25 · S 0.5 · M 1 · L 3 · XL 5 · XXL 7** person-days.
67
- - The sheet computes: `Days = F+G` · `MFactor Days = Days × totalMF` · `Total Days = Days + MFactor` · `LOW $ = Total × hoursPerDay × rate` · `HIGH $ = LOW × highFactor`. **Ignore the Phase column.**
68
- - **Cluster by fix-shape to size fast:** "add existing auth guard to N routes" (XS–S, repeated pattern) vs "new backend surface" (M, +FE) vs "config / single route" (XS). Size the cluster once, apply to its members.
69
- - **Tune the MF per project:** raise Buffer/QA when confidence is low; raise `highFactor` above 1.25 for more unknowns.
70
- - **PAUSE:** present the sized rows (or the clusters + representative sizes) and the running total. Wait for `continue` or corrections before Step 2.5.
60
+ - **Size each column INDEPENDENTLY** (FE and BE each get their own letter; blank = 0). Scale: **XS 0.25 · S 0.5 · M 1 · L 3 · XL 5 · XXL 7** person-days.
61
+ - **Bare codes in `F:G`** — `XS` `S` `M` `L` `XL` `XXL`. Never the legend text (`"XS - Extra Small"`). The lookup happens to compute either way, which is why the long form shipped unnoticed.
62
+ - The sheet computes: `Days = F+G` → `MFactor Days = Days × Total MF` → `Total Days` → `LOW $ = Total × 8 × rate` → `HIGH $ = LOW × high factor`.
63
+ - **Cluster by fix-shape to size fast**: "add existing guard to N routes" (XS–S, repeated) vs "new backend surface" (M, +FE) vs "config / single route" (XS). Size the cluster once, apply to members.
64
+ - **Tune the MF per project** (raise Buffer/QA when confidence is low; raise the high factor above 1.25 for more unknowns) — but change the sheet's MF list only with the operator's say-so.
65
+ - **PAUSE:** present the sized rows (or clusters + representative sizes) and the running total (recomputed from raw sizes: `Σ(FE,BE days) × (1 + MF)`). Wait for `continue` or corrections.
71
66
 
72
67
  ## Step 2.5: Estimate Adjustments (familiarization + risk/unknowns) — JUDGMENT · PAUSE FOR REVIEW
73
68
 
74
- Base sizes assume *familiar* devs on *well-understood* work. Adjust for the two things that make real work heavier than the naive size. Document each adjustment per-item so the client sees **why** an item is heavier. (`tekyz-familiarization-bump`.)
69
+ Base sizes assume *familiar* devs on *well-understood* work. Adjust for the two things that make real work heavier. Document each adjustment per-item so the client sees **why**. **This step is ON by default (`newTeamDefault: true`) — it was skipped on shipped estimates and had to be asked for.**
70
+
71
+ **(a) New-team familiarization** — bump each item's SIZE in proportion to its complexity — **NOT the MF** (the Analysis MF is for a Business Analyst, not dev ramp). Trivial config / single route → no bump. Repeated-pattern guards, few routes → +0–1 tier. High-volume sweeps + new-surface builds → +1 tier. Optionally add a one-time **"Codebase Onboarding & Downstream Analysis"** Common line (L–XL), documented as optional.
75
72
 
76
- **(a) New-team familiarization** — ramp for a team new to the codebase. Bump each item's SIZE in proportion to its complexity — **NOT the MF** (the Analysis MF is for a Business Analyst, not dev ramp).
77
- - Trivial config / single-route → **no bump**. Repeated-pattern guards, few routes → **+0–1 tier**. High-volume sweeps + new-surface builds → **+1 tier**.
78
- - Optionally add a one-time **"Codebase Onboarding & Downstream Analysis"** Common line (sized L–XL) — document as optional, client can remove.
73
+ **(b) R&D / unknown-approach / spike risk** — an item needing research, an unproven approach, or an unknown integration gets an uplift for the uncertainty: bump its SIZE or raise the high factor if unknowns dominate. Name the unknown explicitly ("requires spike: undocumented 3rd-party API").
79
74
 
80
- **(b) R&D / unknown-approach / spike risk** — an item needing research, an unproven approach, or an unknown integration gets an uplift for the uncertainty. Either bump its SIZE, or raise its per-item risk contribution (and consider raising `highFactor` for the whole estimate if unknowns dominate). Name the unknown explicitly ("requires spike: undocumented 3rd-party API").
75
+ **⚠️ The scale is NON-LINEAR. M→L is a 3× cliff (1 day → 3 days).** Never push an item across M→L unless it is genuinely multi-day. Cap routine-work bumps at M. Calibration: HILO 21 criticals = $8,700 familiar → $11,730 new-team (+35%, bumps capped at M).
81
76
 
82
- **⚠️ The scale is NON-LINEAR. M→L is a 3× cliff (1 day → 3 days).** A blind one-tier bump across M→L doubles the total. **Never push an item across M→L unless it is genuinely multi-day.** Cap routine-work bumps at M.
83
- - Reference calibration: HILO 21 criticals = $8,700 familiar → $11,730 new-team (+35%, bumps capped at M) → $13,090 incl Project Setup.
84
77
  - **PAUSE:** present every adjustment (item, reason, before→after size, total delta). Wait for `continue` or corrections.
85
78
 
86
- ## Step 3: Group by domain + blue headings (T-Shirt tab)
79
+ ## Step 3: Group by domain + section headings (T-Shirt tab)
80
+
81
+ Reorder items into domains. Insert a **section-heading row** before each group per spec §1.2 (merge `A:L`, bg `#1C4F8B`, white bold Arial 10). **No subtotals.**
82
+
83
+ - Insert rows with `insertDimension` (it shifts ranges); never write into rows below a summed range.
84
+ - After reordering, **re-point EVERY aggregate on the tab**: the totals row, the phase rollups `K4:N7` (their `$E$14:$E$<last>` ranges), and the summary block — then move the summary block BELOW the totals row if the template left it inside the summed range (circular `#REF!` otherwise).
87
85
 
88
- Reorder items into domains (**A–G to match the PRD sections**). Insert a **blue section-heading row** before each group (merge A:L, white bold on blue). **No subtotals** (they complicate formulas).
86
+ ## Step 4: Team Mix — JUDGMENT · PAUSE FOR REVIEW
89
87
 
90
- - **After reordering, WIDEN the rollup SUMIF ranges** (e.g. `E19:En`). Writing cells does NOT auto-expand hardcoded ranges — only `insertDimension` shifts them. A missed widen silently under-counts the total.
88
+ Your judgment is ONE thing: the FTE per discipline (`teamMix.fte` in the plan — `backend` `frontend` `qa` `pm` `ba`, optionally `techlead` `devops`). Everything else on the tab is computed by the tool per spec §2: the split into people (saturate at 1.00, then spill), months, the column count, the ramp by discipline, the remainder formula.
91
89
 
92
- ## Step 4: Team Mix cross-check
90
+ 1. **Staff every weighted MF factor** — QA → `qa`, PM → `pm`, Analysis → `ba`. Deployment / standups / buffer are absorbed by the engineers and lead. Typical fractions: PM 0.20–0.25 · BA 0.10 · Tech Lead 0.25 · QA 0.40–0.50. The tool HALTS on a roster that leaves a factor unstaffed — do not argue with it; add the person.
91
+ 2. **Run `gsd-t estimate-sheet plan-check --sheet <url> --plan <plan.json>`.** It prints the MF list it read, the T-Shirt total, and the roster table it WOULD write (person · Count · Mths · Days · Hrs · Mon 1..N with the remainder) — or halts with the violation.
92
+ 3. **PAUSE:** present that table verbatim. Wait for `continue` or corrections (a resize or a different FTE → edit the plan, re-run plan-check, present again).
93
93
 
94
- `Count` = fractional headcount per role (e.g. Backend 0.75 = one BE dev at 75% over the window). `Days (E) = Month(D) × 20` · `Total Days = Days × Count`.
94
+ ## Step 5: Technology Stack
95
95
 
96
- - **Solve `Month` (D5:D12)** so `sum(Count) × (Month×20) = the T-shirt total`. Sync the Resource (J) column.
97
- - **Check for hardcoded cells** breaking the formula chain (restore `=E×B`, `=J` where a static number was pasted — the Testing row is a known offender).
98
- - **Verify BOTH halves (F13, J13) equal the T-shirt total.**
96
+ Fill the tab per spec §3 from **internal, grep-able facts** — `docs/architecture.md`, `docs/infrastructure.md`, package manifests, lockfiles, CI config. One row per category that applies (`Frontend` · `Backend` · `Auth` · `Data Store` · `Cache / Session Store` · `Object Storage` · `Events / Webhooks` · `Tools / Integrations` · `LLM / Reasoning` · `Observability` · `Infrastructure`), one plain-English line each with versions where the manifest states them. **Never leave the tab blank and never guess a version** — an unknown is "not determined from the repo", not an invented number.
99
97
 
100
- ## Step 5: Write to the Google Sheet (PERMANENT reusable service account) — MECHANICAL · show result
98
+ ## Step 6: Write to the Google Sheet + audit (MECHANICAL · show result)
101
99
 
102
- **gcloud's `spreadsheets` OAuth scope is blocked by Google** (browser shows "This app is blocked"; `gcloud auth print-access-token` is unusable for Sheets), so a **service account with a self-signed JWT** is the only working path (`google-sheets-service-account-workaround`). A **PERMANENT, reusable SA already exists** — do NOT create a throwaway per run.
100
+ **Write the plan to `.gsd-t/estimate-plan.json`, then run:**
103
101
 
104
- **The permanent estimate SA (provisioned once, reused every run):**
105
- - **Email (the sheet share-target):** `gsd-t-sheets-writer@ai-estimator-415612.iam.gserviceaccount.com`
106
- - **GCP project:** `ai-estimator-415612` · **Sheets API:** enabled
107
- - **Key file:** `~/.claude/gsd-t-secrets/gsd-t-sheets-writer-key.json` (chmod 600, outside any repo — never commit an SA key)
102
+ ```bash
103
+ gsd-t estimate-sheet write --sheet <url> --plan .gsd-t/estimate-plan.json # add --replace only if the T-Shirt tab already has rows you mean to overwrite
104
+ ```
108
105
 
109
- 1. **Self-heal / provision-once (idempotent):** if the key file is missing, recreate the SA + mint a key before proceeding — this is the ONLY create path, and it runs at most once ever:
110
- ```bash
111
- PROJECT=ai-estimator-415612
112
- SA=gsd-t-sheets-writer@$PROJECT.iam.gserviceaccount.com
113
- KEY=~/.claude/gsd-t-secrets/gsd-t-sheets-writer-key.json
114
- gcloud services enable sheets.googleapis.com --project=$PROJECT
115
- gcloud iam service-accounts describe "$SA" --project=$PROJECT >/dev/null 2>&1 || \
116
- gcloud iam service-accounts create gsd-t-sheets-writer \
117
- --display-name="GSD-T Estimate Sheets Writer (permanent, reusable)" --project=$PROJECT
118
- # SA creation is eventually-consistent — poll describe before minting the key
119
- [ -f "$KEY" ] || { mkdir -p ~/.claude/gsd-t-secrets && chmod 700 ~/.claude/gsd-t-secrets && \
120
- gcloud iam service-accounts keys create "$KEY" --iam-account="$SA" --project=$PROJECT && chmod 600 "$KEY"; }
121
- ```
122
- 2. **Share check:** the SA has no access until the sheet is shared with it. If a read-probe (below) returns `403`, **prompt the operator to share the sheet with `gsd-t-sheets-writer@ai-estimator-415612.iam.gserviceaccount.com` as Editor** (uncheck "Notify people"), wait for confirmation, re-probe. Because the SA is permanent, this is a **one-time** share per sheet — no re-share churn.
123
- 3. **Auth = self-signed JWT** (pure stdlib + openssl, no client libraries): read the key file, sign an RS256 JWT (`openssl dgst -sha256 -sign`), scope `https://www.googleapis.com/auth/spreadsheets`, exchange at `oauth2.googleapis.com/token` (grant `urn:ietf:params:oauth:grant-type:jwt-bearer`) for a Bearer token. JWT `exp = now + 3600`; **mint fresh each run** (1h expiry).
124
- 4. **Write via Sheets v4 REST** with the Bearer token: `values/<range>/PUT?valueInputOption=USER_ENTERED` for cell values; `:batchUpdate` for inserts/formatting/merges. **Reads:** `GET spreadsheets/<id>?ranges=...&includeGridData=true` (used for the 403 share-probe too).
125
- 5. **Gotcha fixes (all proven on HILO):**
126
- - **403 quota-project** on the Sheets API → add header `X-Goog-User-Project: ai-estimator-415612`.
127
- - **URL-encode every range** (`urllib.parse.quote`) — spaces in tab names (e.g. `'T-Shirt Size Estimate'!A19`) break the URL.
128
- - **Hardcoded SUMIF/total ranges don't auto-expand** on a plain values-write → use `insertDimension` (which shifts ranges) when inserting rows (ties back to Step 3's WIDEN note).
129
- - **`drive.file` scope 404s on a pre-existing sheet** → must use the full `spreadsheets` scope.
130
- 6. **No cleanup / no delete.** The SA is PERMANENT and reused — never delete it, never delete the key. (Deleting + recreating an SA changes its internal id and silently breaks every existing sheet share even with an identical email → 403. Keeping it permanent is the whole point.)
106
+ It writes the T-Shirt tab (items or sizes mode), the Team Mix, the Technology Stack, re-points every rollup, propagates the Phase dropdown, applies the styling — and then runs the spec §5 audit by reading back, printing every check with ✓/✗. **Exit 4 = a ✗ — fix the plan (or the sheet, for a template problem it names) and re-run. Exit 64 = auth/API/input halt.** Show the tool's output to the operator verbatim. Do not hand-patch cells around the tool; if the tool cannot express something the sheet needs, that is a tool change, and you say so.
131
107
 
132
- ## Step 6: Generate the PRD — JUDGMENT · PAUSE FOR REVIEW
108
+ **Auth** (used by the tool) = the PERMANENT reusable service account (never create a throwaway, never delete or recreate it — recreation breaks every existing share): email `gsd-t-sheets-writer@ai-estimator-415612.iam.gserviceaccount.com`, project `ai-estimator-415612`, key `~/.claude/gsd-t-secrets/gsd-t-sheets-writer-key.json` (chmod 600, outside any repo). gcloud's `spreadsheets` OAuth scope is Google-blocked, so: read the key, sign an RS256 JWT (`openssl dgst -sha256 -sign`), scope `https://www.googleapis.com/auth/spreadsheets`, exchange at `oauth2.googleapis.com/token` (grant `urn:ietf:params:oauth:grant-type:jwt-bearer`), `exp = now + 3600`, mint fresh each run. If the key file is missing, provision ONCE:
133
109
 
134
- **ONE document** (ATOS contractor-handoff template, sections 0–15) with **domain sub-sections (A–G) inside each numbered section** — not one PRD per item. (`tekyz-client-prd-structure`.)
110
+ ```bash
111
+ PROJECT=ai-estimator-415612
112
+ SA=gsd-t-sheets-writer@$PROJECT.iam.gserviceaccount.com
113
+ KEY=~/.claude/gsd-t-secrets/gsd-t-sheets-writer-key.json
114
+ gcloud services enable sheets.googleapis.com --project=$PROJECT
115
+ gcloud iam service-accounts describe "$SA" --project=$PROJECT >/dev/null 2>&1 || \
116
+ gcloud iam service-accounts create gsd-t-sheets-writer \
117
+ --display-name="GSD-T Estimate Sheets Writer (permanent, reusable)" --project=$PROJECT
118
+ # SA creation is eventually-consistent — poll describe before minting the key
119
+ [ -f "$KEY" ] || { mkdir -p ~/.claude/gsd-t-secrets && chmod 700 ~/.claude/gsd-t-secrets && \
120
+ gcloud iam service-accounts keys create "$KEY" --iam-account="$SA" --project=$PROJECT && chmod 600 "$KEY"; }
121
+ ```
135
122
 
136
- - **§0 Metadata + top-of-doc ⚠️ Estimate Basis & Disclaimer** — planning estimates, NOT a quote/bid/fixed price; will change; no not-to-exceed; no delivery guarantee. **Purge all quote/fixed/guarantee/binding language.**
137
- - **§3.0 requirement↔finding crosswalk** — per-domain tables (Requirement · Finding · Fix). `FR-xN` (domain-sequenced requirement id) and `TD-n` (permanent scan finding id) do NOT run in parallel — always crosswalk them. (For a pure requirements input with no scan, the "Finding" column becomes the source requirement id.)
138
- - **§3.1 FR tables** — a **dedicated Finding/Source column** (never bury the id in trailing prose).
139
- - **§3.2 NFR** — an **"Applies to" column listing EVERY id** each cross-cutting NFR touches.
140
- - **§4 enforcement, §8 API, §10 estimate** — explicit id refs (not bare numbers).
141
- - **§10 total MUST equal the live sheet rollup** — verify against the sheet's rollup cell, not memory. Watch that Project Setup carries its MF (M/M = 2 raw → 3.4 total, not 2.0). Point-in-time sync is fine; note it.
142
- - **§15 sign-off = "Approved to proceed (scope, not fixed cost)".**
143
- - Group tables **by domain with a bold header per group** (no repeating "Domain" column — reads as broken).
144
- - **Save to `share/<Repo-Name>-PRD-*.md`** (repo-name prefix, matching `/gsd-t-scan`'s `share/` convention).
145
- - **PAUSE:** present the PRD (or its outline + disclaimer + §10 total) for review before finalizing.
123
+ **What the tool does** (spec §0, all learned the hard way — listed so you can recognise a template problem when it halts): no `X-Goog-User-Project` header; every range URL-encoded; Phase cells `copyPaste`d from the sheet's existing dropdown cell (it halts if the template has none — add one to E14); clear-then-paint from the current shape; small batches that halt on the first failure; Team Mix header on row 2 only with `Mon 1..N` + `Total Hrs` derived from the month count; sage on exactly Days / Hrs / Total Hrs; Arial 10; bands across every month column; then the read-back audit. **Any ✗ is fixed before Step 7** — a 200 on the write is not evidence the cell landed.
146
124
 
147
- ## Step 7: Verify — reconcile the three totals (MECHANICAL · show result)
125
+ ## Step 7: Verify — reconcile (MECHANICAL · show result)
148
126
 
149
- **The three totals MUST agree:** T-Shirt Size total = Team Mix total (F13/J13) = PRD §10 total. If any differ, find the break (usually a hardcoded cell or an un-widened SUMIF range) and fix before advancing to the Red Team.
127
+ - `gsd-t estimate-sheet audit --sheet <url>` must exit 0 (it recomputes **T-Shirt `Total Days` == Team Mix `Σ Days`** independently — raw sizes × (1+MF) vs Σ Count × Mths × 20 — and checks every §5 line). A ✗ here after a clean write means someone edited the sheet; find the break before the Red Team.
128
+ - **Overview `C2/E2/C3/E3` resolve to numbers** (they reference the T-Shirt rollups `L8/K8/N8/M8`; the estimates index imports exactly these cells).
129
+ - If the estimate is linked from the estimates index and shows `#REF!`, that is the one-time `IMPORTRANGE` "Allow access" click — report it for the operator; it is not yours to fix.
150
130
 
151
131
  ## Step 8: Estimate Red Team (adversarial) — JUDGMENT · PAUSE · YOU ARE THE ARBITER
152
132
 
153
- An independent adversarial pass that challenges the estimate before it reaches the client — the estimate-time analogue of GSD-T's build Red Team. **Its job is to protect Tekyz from a money-losing under-estimate AND to keep the estimate competitive** (paired realism, per `feedback_red_team_realism_gate` — don't pad every item to XXL "to be safe").
133
+ An independent adversarial pass that challenges the estimate before it reaches the client. **Its job is to protect Tekyz from a money-losing under-estimate AND to keep the estimate competitive** (paired realism, per `feedback_red_team_realism_gate` — don't pad every item to XXL).
154
134
 
155
135
  **What it attacks:**
156
136
  - **Under-sized items** — "this 'S' implies a DB migration + backfill → really M; +2 days."
157
- - **Missing line-items** — work a requirement implies but nothing sized (migrations, tests, auth, error states, rollout).
158
- - **Optimistic multipliers** — Buffer/QA too low for the stated confidence; `highFactor` too tight for the unknowns.
159
- - **Adjustment gaps** — an R&D/unknown item (Step 2.5b) sized as if it were routine.
160
- - **Cross-check integrity** — do the three totals ACTUALLY reconcile, or is a hardcoded cell hiding a break.
161
- - **Assumption / scope gaps** — unstated assumptions that would blow up mid-project.
137
+ - **Missing line-items** — work a requirement implies but nothing sized (migrations, tests, auth, error states, rollout, sandbox issuance, decision cycles with the client, gate-acceptance evidence).
138
+ - **Counts that were guessed** — "510 call sites" that measure at 897; re-measure the big ones.
139
+ - **Optimistic multipliers** — Buffer/QA too low for the stated confidence; high factor too tight for the unknowns.
140
+ - **Adjustment gaps** — an R&D/unknown item sized as if routine; familiarization not applied.
141
+ - **Team Mix realism** — a role missing for a weighted MF factor, a flat ramp, a smeared roster.
142
+ - **Cross-check integrity** — do the two totals ACTUALLY reconcile, or is a hardcoded cell hiding a break.
143
+ - **Assumption / scope gaps** — unstated assumptions that would blow up mid-project (record them on the Overview as assumptions, not padding).
162
144
 
163
- **Verdict:** `FAIL` (material under-estimate or missing scope found) / `GRUDGING-PASS` (exhaustive search, nothing material).
145
+ **Verdict:** `FAIL` (material under-estimate or missing scope) / `GRUDGING-PASS` (exhaustive search, nothing material).
164
146
 
165
- ### The arbitration protocol (David is the final judge — NOT bot ping-pong)
147
+ ### The arbitration protocol (the operator is the final judge — NOT bot ping-pong)
166
148
 
167
- When the Red Team returns `FAIL`, it does **NOT** loop back-and-forth with the skill until it grudgingly passes. It surfaces to **the operator**:
149
+ When the Red Team returns `FAIL`, it does **NOT** loop with the skill until it grudgingly passes. It surfaces to **the operator**:
168
150
 
169
- 1. **Present each objection PLAINLY:** *what* shouldn't pass, *why*, and *the estimate impact* (which item, before→after size, dollar delta). One clear list, ranked by dollar impact.
170
- 2. **The operator decides, per objection:**
171
- - **Agree** → operator says `continue` → apply the fix.
172
- - **Disagree** → operator gives feedback → **the Red Team argues back.** The argument continues until **one side concedes** OR the operator ends it definitively.
173
- 3. **Definitive-decision override:** if the operator says anything conclusive — e.g. *"No more argument. I've decided on X,"* or any clearly final ruling — the Red Team **MUST grudgingly accept the operator's decision immediately, regardless of how many rounds have passed.** It **MAY document its unresolved objection** (in the PRD as a footnote or a `share/<Repo>-estimate-redteam-notes.md` file) **for later consideration** — but it does not re-litigate.
174
- 4. The Red Team **never self-satisfies into a pass** and **never overrides the operator.** It either persuades the operator or defers to them.
151
+ 1. **Present each objection PLAINLY:** *what* shouldn't pass, *why*, and *the estimate impact* (item, before→after size, dollar delta). One list, ranked by dollar impact.
152
+ 2. **The operator decides, per objection:** **Agree** → `continue` → edit the plan and re-run Steps 4, 6, 7 (a resize changes the roster and the month count; the tool recomputes both). **Disagree** → operator gives feedback → **the Red Team argues back** until one side concedes or the operator ends it.
153
+ 3. **Definitive-decision override:** anything conclusive from the operator ("No more argument. I've decided on X") is accepted immediately, regardless of round count. The Red Team **MAY document its unresolved objection** in `share/<Repo>-estimate-redteam-notes.md` — it does not re-litigate.
154
+ 4. The Red Team **never self-satisfies into a pass** and **never overrides the operator.**
175
155
 
176
156
  **PAUSE** at every objection — this phase is inherently interactive.
177
157
 
178
158
  ## Step 9: Deliver
179
159
 
180
- All client-facing files land in `share/` with the repo-name prefix. Report: input type + scope + item count, the active config values (rate/MF/highFactor), the eng-days + LOW–HIGH dollar range, the sheet URL, the PRD path, the Red Team verdict, and any documented-but-overridden Red Team objections.
160
+ Report: input type + scope + item count; the active config values (rate / MF list / high factor); Total Days and the LOW–HIGH dollar range; the sheet URL; the roster (people × Count, months); the audit checklist result; the Red Team verdict and any documented-but-overridden objections; any `#REF!` awaiting the operator's Allow-access click.
181
161
 
182
162
  ## Document Ripple
183
163
 
184
- - `share/<Repo>-PRD-*.md` (new PRD deliverable) + the Google Sheet (external) + optional `share/<Repo>-estimate-redteam-notes.md` (overridden objections).
164
+ - The Google Sheet (external) — T-Shirt Size Estimate, Team Mix, Technology Stack (+ Overview cells verified) + optional `share/<Repo>-estimate-redteam-notes.md`.
185
165
  - If renumbering (Step 1) ran: the input + plain-English + `scan/*.md` + `docs/*` + README + `share/*` were remapped (archives untouched) — note it in the report so the numbering change is traceable.
166
+ - No PRD. A client PRD is `/gsd-t-prd`.
186
167
 
187
168
  ## ▶ Next Up
188
169
 
189
- Standalone command — no auto-successor. After delivering, the user shares the sheet + PRD with the client.
170
+ Standalone command — no auto-successor. After delivering, the user shares the sheet with the client (and runs `/gsd-t-prd` if a PRD is wanted).
@@ -378,10 +378,10 @@ Use these when user asks for help on a specific command:
378
378
  - **Use when**: Ready to address technical debt items
379
379
 
380
380
  ### estimate
381
- - **Summary**: Turn any structured work document — a scan register, a new-feature or new-app requirements doc, or a PRD-in — into a Tekyz client estimate (Google Sheet: T-Shirt Size + Team Mix) and a matching PRD deliverable
381
+ - **Summary**: Turn any structured work document — a scan register, a gap-analysis sheet, a new-feature or new-app requirements doc — into a Tekyz client estimate Google Sheet: **T-Shirt Size + Team Mix + Technology Stack**, written and audited against `~/.claude/templates/estimate-sheet-spec.md`. No PRD (`/gsd-t-prd` owns that)
382
382
  - **Auto-invoked**: No
383
- - **Updates**: the Tekyz estimate Google Sheet + `share/<Repo>-PRD-*.md` (and, if renumbered, the source doc/docs/scan files) + optional `share/<Repo>-estimate-redteam-notes.md`
384
- - **Use when**: You need a client-facing paid estimate + PRD (T-shirt sizing, dollar range, sign-off) from a scan OR a requirements/feature/app spec. **SUPERVISED** — judgment phases (sizing, adjustments, PRD, Red Team) pause for your review; **you are the final arbiter** of an Estimate Red Team that challenges the numbers. Rate + sheet template + factors are parameterized (default Tekyz). Encodes the Tekyz playbook (`~/.claude/playbooks/tekyz-estimation-and-prd-playbook.md`)
383
+ - **Updates**: the Tekyz estimate Google Sheet (three tabs, written and read-back-audited by `gsd-t estimate-sheet` from `.gsd-t/estimate-plan.json`) + optional `share/<Repo>-estimate-redteam-notes.md` (and, if renumbered, the source doc/docs/scan files)
384
+ - **Use when**: You need a client-facing paid estimate (T-shirt sizing, dollar range, staffed team by month) from a scan, a gap analysis, or a requirements/feature/app spec. **SUPERVISED** — judgment phases (sizing, adjustments, Team Mix, Red Team) pause for your review; **you are the final arbiter** of an Estimate Red Team that challenges the numbers. Accepts `--sheet <url>`. Rate + factors are parameterized (default Tekyz; the sheet's own MF list wins). Playbook: `~/.claude/playbooks/tekyz-estimation-and-prd-playbook.md`
385
385
 
386
386
  ### stories
387
387
  - **Summary**: Generate a dev-team handoff document in the Tekyz user-stories format — discrete user stories with workflows, grouped acceptance criteria, per-story flow diagrams (Mermaid rendered to embedded images), and mapped test-case tables — from any source (scan register, requirements doc, design contract, or a reverse-engineered codebase)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekyzinc/gsd-t",
3
- "version": "5.19.11",
3
+ "version": "5.20.11",
4
4
  "description": "GSD-T: Contract-Driven Development for Claude Code — 54 slash commands with headless-by-default workflow spawning, unattended supervisor relay with event stream, graph-powered code analysis, real-time agent dashboard, task telemetry, doc-ripple enforcement, backlog management, impact analysis, test sync, milestone archival, and PRD generation",
5
5
  "author": "Tekyz, Inc.",
6
6
  "license": "MIT",
@@ -617,7 +617,7 @@ Add `**Also available:**` with `- /gsd-t-{alt} — {desc}` lines if alternatives
617
617
  | `integrate` | `verify` | |
618
618
  | `verify` | *(auto-invokes complete-milestone)* | |
619
619
  | `complete-milestone` | `status` | |
620
- | `scan` | `promote-debt` | `milestone`, `estimate` (client estimate + PRD) |
620
+ | `scan` | `promote-debt` | `milestone`, `estimate` (client estimate sheet) |
621
621
  | `init` | `scan` | `milestone` |
622
622
  | `init-scan-setup` | `milestone` | |
623
623
  | `gap-analysis` | `milestone` | `feature` |
@@ -14,7 +14,7 @@
14
14
  "_totalMF": "Overhead multiplier applied to raw Days. Tekyz default 0.7 = QA 0.3 + PM 0.1 + Analysis 0.05 + Deployment 0.05 + Buffer 0.2. Raise Buffer/QA when confidence is low.",
15
15
 
16
16
  "mfBreakdown": { "qa": 0.3, "pm": 0.1, "analysis": 0.05, "deployment": 0.05, "buffer": 0.2 },
17
- "_mfBreakdown": "Optional itemized breakdown of totalMF (must sum to totalMF). Tune per project; e.g. raise buffer for low-confidence scope.",
17
+ "_mfBreakdown": "Optional itemized breakdown of totalMF (must sum to totalMF). Tune per project; e.g. raise buffer for low-confidence scope. When a target sheet exists its own MF list (T-Shirt tab E4:F9) is the source of truth and is READ, never overwritten — Hilo sheets run 0.9 (qa .3, pm .1, analysis .1, deployment .05, standups .15, buffer .2). Every non-zero factor must have a person in Team Mix (see estimate-sheet-spec.md section 2.4).",
18
18
 
19
19
  "highFactor": 1.25,
20
20
  "_highFactor": "HIGH dollar figure = LOW x this. Raise above 1.25 when unknowns dominate (many R&D / spike items).",
@@ -35,5 +35,8 @@
35
35
  "_serviceAccountKeyPath": "Local path to the SA JSON key (chmod 600, outside any repo — NEVER commit). Step 5 self-provisions the SA+key here if missing.",
36
36
 
37
37
  "newTeamDefault": true,
38
- "_newTeamDefault": "Whether to assume a new-team familiarization adjustment (Step 2.5a) by default. Usually true for a fresh client."
38
+ "_newTeamDefault": "Whether to assume a new-team familiarization adjustment (Step 2.5a) by default. Usually true for a fresh client.",
39
+
40
+ "sheetSpec": "~/.claude/templates/estimate-sheet-spec.md",
41
+ "_sheetSpec": "The layout/formula/styling/roster spec every write is audited against. Bundled at templates/estimate-sheet-spec.md in the GSD-T package."
39
42
  }