@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/CHANGELOG.md +57 -0
- package/README.md +2 -2
- package/bin/gsd-t-estimate-sheet.cjs +1184 -0
- package/bin/gsd-t.js +44 -0
- package/commands/cpua.md +18 -1
- package/commands/gsd-t-estimate.md +92 -111
- package/commands/gsd-t-help.md +3 -3
- package/package.json +1 -1
- package/templates/CLAUDE-global.md +1 -1
- package/templates/estimate-config.json +5 -2
- package/templates/estimate-sheet-spec.md +261 -0
- package/templates/playbooks/tekyz-estimation-and-prd-playbook.md +8 -4
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Tekyz Estimate Sheet — Structure & Formatting Spec
|
|
2
|
+
|
|
3
|
+
The exact layout, formulas, and styling of the three tabs `/gsd-t-estimate` writes:
|
|
4
|
+
**T-Shirt Size Estimate**, **Team Mix**, **Technology Stack** (plus the **Overview** cells
|
|
5
|
+
the estimates index reads). Every rule here came from a real correction on a real client
|
|
6
|
+
sheet (Hilo ATOS, Sep 2026). **Read this before writing a single cell. Re-read the target
|
|
7
|
+
sheet's actual layout before writing — layouts drift between estimates.**
|
|
8
|
+
|
|
9
|
+
Reference implementation (read it, don't guess): "ATP SOW Gap Analysis and Estimate"
|
|
10
|
+
`1jHWUUi68YyJjCI5cXcbDVOFTAKxYWPx3KaXwQSqQK28` — Team Mix tab is the hand-revised
|
|
11
|
+
2026-09-17 layout; T-Shirt tab is the current template.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 0. Universal rules (every tab)
|
|
16
|
+
|
|
17
|
+
| Rule | Why it exists |
|
|
18
|
+
|---|---|
|
|
19
|
+
| **Read before write.** Dump the tab (`includeGridData=true`) and derive every coordinate from what is there. Never write from remembered coordinates. | The header was row 2; a second header was written into row 3 from memory, doubling every label. |
|
|
20
|
+
| **Formulas, not values, for anything derived.** Only judgment inputs (sizes, Count, Mths, ramped month hours) are values. | Values computed in Python drifted from the row total by rounding and could not be audited on the sheet. |
|
|
21
|
+
| **Clear, then paint.** Before restyling, clear `userEnteredFormat` below the last data row and right of the last data column (to ~row 60 / col Z). Then paint the area computed from the CURRENT shape. | Row/column counts change between runs; bands stayed stranded outside the data and new columns went unformatted. |
|
|
22
|
+
| **Small batches, halt on the first failure.** `mergeCells`, `deleteDimension`, `insertDimension` each in their own `batchUpdate`; cell paints in batches ≤ 50 requests; the first failing batch STOPS the run and reports what landed — later batches are not sent, and the audit shows the partial state. Never continue past a failed batch. | One malformed request killed a whole batch; formatting was half-applied with no error on the cells that never painted. `mergeCells` on an already-merged range 400s — unmerge first. |
|
|
23
|
+
| **Verify by reading back**, never by the 200. After every restyle dump background colour, font, validation, and formulas for the data area and compare to this spec. | Wrong colours and a missing dropdown look identical to correct ones until someone clicks. |
|
|
24
|
+
| **URL-encode every range** (`urllib.parse.quote`). Tab names have spaces. | `'T-Shirt Size Estimate'!A19` breaks the URL unencoded. |
|
|
25
|
+
| **Do NOT send `X-Goog-User-Project`.** A 403 with it present is the header, not an unshared sheet. Retry without it BEFORE telling the operator to share. | The operator was asked to share an already-shared sheet twice. |
|
|
26
|
+
| **Insert rows with `insertDimension`**, never write into rows below a summed range. Then re-point EVERY aggregate on the tab (totals row, phase rollups, summary block). | A `SUMIF($E$18:$E$19,…)` kept summing one stale row and rendered a plausible $1,000. |
|
|
27
|
+
| **Map sheet rows to items BY NAME, not by counting.** | Section header rows shift positional alignment and put a whole column on the wrong tasks. |
|
|
28
|
+
| **Recompute the total from raw sizes independently** (`Σ(FE,BE days) × (1 + MF)`) before reporting a number. | A total was reported from a cell just repaired; the true figure differed by 13 days. |
|
|
29
|
+
| **`IMPORTRANGE` `#REF!` on the index sheet needs a human "Allow access" click.** Report it; do not "fix" it. | The service account cannot grant it. |
|
|
30
|
+
| Access token expires in 1 h — mint fresh per run; regenerate on a sudden 401. | |
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 1. T-Shirt Size Estimate tab
|
|
35
|
+
|
|
36
|
+
### 1.1 Fixed header block (rows 1–13) — READ these, never overwrite them
|
|
37
|
+
|
|
38
|
+
| Cells | Content | Style |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `A1` | `Date Submitted` | bg `#D9D9D9`, bold, Calibri |
|
|
41
|
+
| `B1` | date `mm/dd/yyyy` | Calibri 11, left |
|
|
42
|
+
| `A3:B3` | `Legends` / `Person Days` | bg `#3D85C6`, white bold Calibri |
|
|
43
|
+
| `A4:B9` | **Size legend** — `XS - Extra Small` 0.25 · `S - Small` 0.5 · `M - Medium` 1 · `L - Large` 3 · `XL - Extra Large` 5 · `XXL - Extra Extra Large` 7 | Calibri, right |
|
|
44
|
+
| `E3` | `Multiplication Factor` | bg `#3D85C6`, white bold |
|
|
45
|
+
| `E4:F9` | **MF list** — one row per factor (e.g. `QA` 0.3 · `PM` 0.1 · `Analysis` 0.1 · `Deployment` 0.05 · `StdUps/Mtgs` 0.15 · `Buffer` 0.2). **Read the live list — it varies per sheet and is the roster contract for Team Mix (§2.4).** | |
|
|
46
|
+
| `E10:F10` | `Total MF` = `=sum(F4:F9)` | bg `#C9DAF8`, bold |
|
|
47
|
+
| `G3` / `G4` | `High $ Factor` / value (1.25 default; 1.3 seen) | |
|
|
48
|
+
| `H3` / `H4` | `Avg. Hrly Rate` / `$50.00` | nf `"$"#,##0.00` |
|
|
49
|
+
| `J3:N3` | phase rollup headers: (blank) · `Low ($)` · `Low Hrs` · `High ($)` · `High Hrs` | bg `#3D85C6`, white bold |
|
|
50
|
+
| `J4:J7` | `MVP` · `Phase 1` · `Phase 2` · `Phase 3` | bg `#3D85C6`, white bold |
|
|
51
|
+
| `K4:N7` | `=SUMIF($E$14:$E$<last>,$J4,$K$14:$K$<last>)` · `=SUMIF(…,$J$14:$J$<last>)*8` · `=SUMIF(…,$L$14:$L$<last>)` · `=IF($H$4=0,0,M4/$H$4)` — **`<last>` must equal the last item row; re-point after every insert** | bold, `$` / `0.00` |
|
|
52
|
+
| `J8:N8` | `Totals` · `=SUM(K4:K7)` … | bg `#9FC5E8`, bold |
|
|
53
|
+
| `E12` / `F12:G12` / `H12:L12` | `Phase` / `Complexity` (merged) / `Effort & Cost Breakdown` (merged) | bg `#F9CB9C` / `#A2C4C9` / `#B6D7A8`, bold, centered |
|
|
54
|
+
| `A13:L13` | `Module/Functionality` · `User Type` · `Functionality` · `Low Level Requirements` · `Phase` · `Web Portal` · `Backend/API` · `Days` · `MFactor Days` · `Total Days` · `LOW $` · `HIGH $` | bg `#F3F3F3` (C–D `#E8E8E8`), bold Calibri; H–L right |
|
|
55
|
+
|
|
56
|
+
Column widths: `[150, 120, 300, 430, 122, 90, 90, 61, 53, 76, 81, 81]`.
|
|
57
|
+
|
|
58
|
+
### 1.2 Body (row 14 → last item)
|
|
59
|
+
|
|
60
|
+
**Section heading row** — one per domain group: text in `A` (e.g. `A. AUTH & TRANSPORT CONTRACT`), **merge `A:L`**, bg `#1C4F8B`, white bold **Arial 10**, left. Nothing else on the row. No subtotals.
|
|
61
|
+
|
|
62
|
+
**Item row** — Calibri 10, left for text, centered for `E:G`, right for `H:L`:
|
|
63
|
+
|
|
64
|
+
| Col | Content | Rule |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| `A` | Module | |
|
|
67
|
+
| `B` | User type | |
|
|
68
|
+
| `C` | Functionality — **include the item id** `(GA-n)` / `(TD-n)` / `(FR-n)` | |
|
|
69
|
+
| `D` | Low-level requirement (one or two sentences) | |
|
|
70
|
+
| `E` | Phase — `MVP` / `Phase 1` / `Phase 2` / `Phase 3` | **Carries a `ONE_OF_LIST` validation + chip format. Populate by `copyPaste` (`PASTE_NORMAL`) from an existing MVP cell — a values-write drops the dropdown.** |
|
|
71
|
+
| `F` | Web Portal (frontend) size | **Bare code only: `XS` `S` `M` `L` `XL` `XXL`. Never the legend text `"XS - Extra Small"`.** Blank = 0. |
|
|
72
|
+
| `G` | Backend/API size | same |
|
|
73
|
+
| `H` | `=(IF(F{r}="",0,SUMIF($A$4:$A$9,LEFT(F{r},2)&"*",$B$4:$B$9))+IF(G{r}="",0,SUMIF($A$4:$A$9,LEFT(G{r},2)&"*",$B$4:$B$9)))` | the working legend lookup — `LEFT(size,2)&"*"` against the legend labels |
|
|
74
|
+
| `I` | `=H{r}*$F$10` | |
|
|
75
|
+
| `J` | `=H{r}+I{r}` | |
|
|
76
|
+
| `K` | `=J{r}*8*$H$4` | nf `$#,##0.00` |
|
|
77
|
+
| `L` | `=K{r}*$G$4` | nf `$#,##0.00` |
|
|
78
|
+
|
|
79
|
+
### 1.3 Totals and summary (BELOW the last item)
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
<last item row>
|
|
83
|
+
(blank row)
|
|
84
|
+
Total (Days) | … | H =SUM(H14:H<last>) | I =SUM(I…) | J =SUM(J…) | K =SUM(K…) | L =SUM(L…)
|
|
85
|
+
(blank row)
|
|
86
|
+
J: Total Days | K: =J<tot> | L: =J<tot>*$G$4
|
|
87
|
+
J: Total Hrs | K: =J<tot>*8 | L: =J<tot>*$G$4*8
|
|
88
|
+
J: Total Cost | K: =K<tot> | L: =L<tot>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**The summary block sits BELOW the totals row, never inside the summed range** (the template
|
|
92
|
+
ships it at rows 18–20, which becomes circular `#REF!` once items extend past it — move it).
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 2. Team Mix tab (David's 2026-09-17 layout)
|
|
97
|
+
|
|
98
|
+
### 2.1 Layout
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
Row 1 Title (merged A:<Total Hrs col>)
|
|
102
|
+
Row 2 Header (row 2 and ONLY row 2)
|
|
103
|
+
Row 3.. one row PER PERSON
|
|
104
|
+
Row T Total (T = 3 + nroles)
|
|
105
|
+
Row T+1 Total Days (right half only, H onward)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
| Col | Header | Content | Style |
|
|
109
|
+
|---|---|---|---|
|
|
110
|
+
| `A` | `Skill set` | person label, e.g. `Backend / API Engineer 1` | left |
|
|
111
|
+
| `B` | `Count` | the person's share of full time (FTE — 1.00 = full time), **≤ 1.00** | right, `0.00` |
|
|
112
|
+
| `C` | — spacer — | empty | width 14 |
|
|
113
|
+
| `D` | `Mths` | project duration, same value every row | right, `0.00` |
|
|
114
|
+
| `E` | `Days` | `=D{r}*20*B{r}` | **sage `#D6E2DD` bold** |
|
|
115
|
+
| `F` | `Hrs` | `=E{r}*8` | **sage bold** |
|
|
116
|
+
| `G` | — spacer — | empty | width 60 |
|
|
117
|
+
| `H` | `Resource` | same label as `A` | left |
|
|
118
|
+
| `I..` | `Mon 1` … `Mon N` | months 1..N-1: **ramped hour values** (§2.5); month N: **`=F{r}-SUM(I{r}:<N-1>{r})`** (the remainder) | right, `0.00`, white |
|
|
119
|
+
| last | `Total Hrs` | `=SUM(I{r}:<N>{r})` | **sage bold** |
|
|
120
|
+
|
|
121
|
+
Widths: `[170, 60, 14, 55, 55, 55, 60, 159, 48 × N, 66]`.
|
|
122
|
+
|
|
123
|
+
Total row: `A` `Total` · `B` `=SUM(B3:B<last>)` · `E`,`F` sums · `H` `Total Hours` · each month + Total Hrs `=SUM(…)`. bg `#E5E5E5`, bold.
|
|
124
|
+
Total Days row: `H` `Total Days` · each month + Total Hrs `=<cell above>/8`. bg `#9EC1EF`, bold. **Nothing in A–G.**
|
|
125
|
+
|
|
126
|
+
Styling: title bg `#3C78D8`, white **Montserrat 20 bold**, centered. Header bg `#1C4F8C`, white **Arial 10 bold**. Body **Arial 10 — never Calibri** (Calibri is the T-Shirt tab). Every numeric cell right-aligned `0.00`; labels left. Sage marks exactly the three per-person totals (`Days`, `Hrs`, `Total Hrs`) on role rows — the monthly columns are calculated too and **stay white**. The sage is deliberate; do not clear it as a stray fill.
|
|
127
|
+
|
|
128
|
+
The old `Month / Days / Tot Days / Hrs` layout is retired: `Days` IS the per-person total.
|
|
129
|
+
|
|
130
|
+
### 2.2 The math
|
|
131
|
+
|
|
132
|
+
- `months = totalDays / (Σ Count × 20)` — solve so `Σ Days` equals the T-Shirt `Total Days`.
|
|
133
|
+
- Month columns `N = ceil(months)`, EXCEPT when the fractional tail is under ~0.1 month: fold it into the last full month (soft ceiling ~172 hrs) instead of opening a near-empty column.
|
|
134
|
+
- **Recompute `months` and `N` after ANY roster change.** Adding a role raises Σ Count, shortens the duration, and turns the remainder column negative (−12.80 hrs was the symptom).
|
|
135
|
+
- The remainder formula makes each row sum exactly. Never write a fractional last month as a value.
|
|
136
|
+
|
|
137
|
+
### 2.3 Roster shape — one row per PERSON
|
|
138
|
+
|
|
139
|
+
1. **No row over Count 1.00.** `Backend Engineer 1.40` is two people → two rows.
|
|
140
|
+
2. **Saturate, then spill.** 1.21 FTE of backend = `Backend / API Engineer 1` at 1.00 + `… 2` at 0.21. Rows per discipline = `ceil(disciplineFTE)`; only the last row is fractional. Never 0.42 / 0.56 / 0.23 across three part-timers.
|
|
141
|
+
3. Each full-time row reads 160 hrs in a full month. If a row needs more, the team is too small for the calendar — add a person, don't stretch one.
|
|
142
|
+
|
|
143
|
+
### 2.4 Roster coverage — every MF factor has a person
|
|
144
|
+
|
|
145
|
+
Read the live MF list (`E4:F9` on the T-Shirt tab). **Every non-zero factor is a person in Team Mix**, not abstract overhead:
|
|
146
|
+
|
|
147
|
+
| Factor | Person |
|
|
148
|
+
|---|---|
|
|
149
|
+
| QA | `QA Engineer` — a named row |
|
|
150
|
+
| PM | `Project Manager` — a named row |
|
|
151
|
+
| Analysis | `Business Analyst` — a named row |
|
|
152
|
+
| Deployment, StdUps/Mtgs, Buffer | absorbed across the engineers and the lead — no row of their own (the hand-corrected ATP sheet has no DevOps row and is correct) |
|
|
153
|
+
|
|
154
|
+
Minimum roster: delivery engineers + `QA Engineer` + `Project Manager` + `Business Analyst` + `Tech Lead / Architect`. Typical fractions: PM 0.20–0.25 · BA 0.10 · Tech Lead 0.25 · QA 0.40–0.50. A mix whose totals reconcile is still wrong if nobody is managing the project.
|
|
155
|
+
|
|
156
|
+
### 2.5 Ramping — flat allocation is unrealistic
|
|
157
|
+
|
|
158
|
+
Monthly hours follow when the work happens. Normalise the weights, multiply by the person's total hours, write months 1..N-1 as values, month N as the remainder formula.
|
|
159
|
+
|
|
160
|
+
| Role | Mon 1 / Mon 2 / Mon 3 weights | Shape |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| UI / Frontend | 1.35 / 1.05 / 0.60 | front-loaded, tapers |
|
|
163
|
+
| Backend / API | 0.90 / 1.15 / 0.95 | steady, peaks mid |
|
|
164
|
+
| QA | 0.40 / 0.90 / 1.70 | back-loaded — ~30% early, bulk at the end |
|
|
165
|
+
| Business Analyst | 1.75 / 0.80 / 0.45 | front-loaded, small tail for change requests |
|
|
166
|
+
| DevOps | 0.45 / 0.85 / 1.70 | back-loaded — mirror of BA |
|
|
167
|
+
| Project Manager | 1.00 / 1.00 / 1.00 | flat |
|
|
168
|
+
| Tech Lead / Architect | 1.30 / 0.80 / 0.90 | design-heavy, uptick at integration |
|
|
169
|
+
|
|
170
|
+
- A flat `160 / 160 / 147` for every role is the tell that no ramping was applied.
|
|
171
|
+
- **The only ceiling is 160 hrs per person per month, soft to ~172.** There is NO `Count × 160` per-row ceiling — a 0.40 QA can work 106 hrs in their heavy month; that is what ramping means.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 3. Technology Stack tab
|
|
176
|
+
|
|
177
|
+
`A1` `Technology Stack` (merged `A:B`, bg `#1C4F8C`, white bold 12). Rows 2+: `A` category · `B` one-line description, Arial. Widths `[210, 760]`.
|
|
178
|
+
|
|
179
|
+
Fill it from the codebase and docs (`docs/architecture.md`, `docs/infrastructure.md`, package manifests) — internal facts, grep-able, never guessed. Categories seen in a filled sheet: `Frontend` · `Backend` · `LLM / Reasoning` · `Tools / Integrations` · `Auth` · `Events / Webhooks` · `Data Store` · `Cache / Session Store` · `Object Storage` · `Observability` · `Infrastructure`. Use the ones that apply; add ones the project needs. **An empty Technology Stack tab is a defect** — two shipped estimates left it blank.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 4. Overview tab — cells the estimates index reads
|
|
184
|
+
|
|
185
|
+
The Hilo Estimates Summaries index (`1sOf6dpd8gvMo0eTfc1uJuf6jH6wrTGjYinPrVh1FBAg`) pulls every estimate via `IMPORTRANGE("<id>","Overview!C2")` etc. Keep this shape exactly:
|
|
186
|
+
|
|
187
|
+
| Row | A | B | C | D | E |
|
|
188
|
+
|---|---|---|---|---|---|
|
|
189
|
+
| 1 | `Estimate` | `Effort Summary` | `Estimated Hours` | `Estimated Avg. Hourly Rate` | `Cost Estimate` |
|
|
190
|
+
| 2 | `Low` | `Project Hours` | `='T-Shirt Size Estimate'!L8` | `='T-Shirt Size Estimate'!H4` | `='T-Shirt Size Estimate'!K8` |
|
|
191
|
+
| 3 | `High` | `Project Hours` | `='T-Shirt Size Estimate'!N8` | `='T-Shirt Size Estimate'!H4` | `='T-Shirt Size Estimate'!M8` |
|
|
192
|
+
|
|
193
|
+
`A7` `Disclaimer` + `A8` the Tekyz disclaimer text (merged `A8:F22`) ships with the template — leave it. Row 2 = Low, row 3 = High; if a title row is added above, the index's cell refs break for that estimate.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 5. Post-write audit — run it, print it, then report
|
|
198
|
+
|
|
199
|
+
**This checklist is code: `gsd-t estimate-sheet audit --sheet <id>`** (`bin/gsd-t-estimate-sheet.cjs`) reads the live sheet back and prints every line below with ✓/✗, exit 4 on any ✗. `write` runs it automatically after writing. The list below is what it checks.
|
|
200
|
+
|
|
201
|
+
Every item is a read-back check against the live sheet. Any ✗ blocks delivery.
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
T-SHIRT
|
|
205
|
+
[ ] every size cell in F:G is one of XS S M L XL XXL or blank (no legend text)
|
|
206
|
+
[ ] every item row's E cell has ONE_OF_LIST validation
|
|
207
|
+
[ ] every item row's H:L are the §1.2 formulas (no values)
|
|
208
|
+
[ ] section heading rows are merged A:L, bg #1C4F8B, and hold no sizes
|
|
209
|
+
[ ] totals row + phase rollups (K4:N7) + summary block reference <last item row>
|
|
210
|
+
[ ] summary block is BELOW the totals row
|
|
211
|
+
[ ] Σ raw sizes × (1+MF) == Total Days cell (recomputed independently)
|
|
212
|
+
TEAM MIX
|
|
213
|
+
[ ] header is row 2 only; row 3 is a person
|
|
214
|
+
[ ] header months read Mon 1..Mon N with N == month column count; last header is Total Hrs
|
|
215
|
+
[ ] no Count > 1.00; per discipline, all rows but the last are 1.00
|
|
216
|
+
[ ] every non-zero MF factor has a matching person row
|
|
217
|
+
[ ] Days/Hrs/Total Hrs are formulas; last month is the remainder formula; no negative month
|
|
218
|
+
[ ] no month cell > 172; full-time rows read 160 in a full month
|
|
219
|
+
[ ] ramp applied: no two roles share the same flat pattern (unless PM)
|
|
220
|
+
[ ] font Arial 10 everywhere; sage on exactly E, F, Total Hrs of role rows; monthly cells white
|
|
221
|
+
[ ] Total row (#E5E5E5) and Total Days row (#9EC1EF) span every month column + Total Hrs
|
|
222
|
+
[ ] nothing formatted outside the data area
|
|
223
|
+
[ ] Σ Days == T-Shirt Total Days
|
|
224
|
+
TECH STACK
|
|
225
|
+
[ ] at least the categories that apply are filled; none says TBD
|
|
226
|
+
OVERVIEW
|
|
227
|
+
[ ] C2/E2/C3/E3 formulas resolve to numbers
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## 6. The tool — `gsd-t estimate-sheet` (v5.20.10)
|
|
233
|
+
|
|
234
|
+
Everything in §1–§5 is executed by `bin/gsd-t-estimate-sheet.cjs`, not re-derived from this prose. The model supplies **judgment only** as a JSON plan; the tool computes and writes everything derived, then audits by reading back. Any violation halts (exit 4); an API or input problem halts (exit 64). Nothing continues past a failure.
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
gsd-t estimate-sheet plan-schema # the plan shape
|
|
238
|
+
gsd-t estimate-sheet read --sheet <id|url> [--tab <name>] # read-before-write dump
|
|
239
|
+
gsd-t estimate-sheet plan-check --sheet <id|url> --plan plan.json # validate + the roster it WOULD write (the Step 4 pause)
|
|
240
|
+
gsd-t estimate-sheet write --sheet <id|url> --plan plan.json [--replace] # T-Shirt + Team Mix + Tech Stack, then audit
|
|
241
|
+
gsd-t estimate-sheet audit --sheet <id|url> # §5 checklist by read-back
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The plan (judgment only):
|
|
245
|
+
|
|
246
|
+
```json
|
|
247
|
+
{
|
|
248
|
+
"title": "Hilo ATOS — ATP Gap Closure",
|
|
249
|
+
"tshirt": { "mode": "items",
|
|
250
|
+
"sections": [ { "heading": "A. AUTH & TRANSPORT CONTRACT",
|
|
251
|
+
"items": [ { "id": "GA-1", "module": "Ingest API", "userType": "ATP System",
|
|
252
|
+
"functionality": "Reconcile HMAC signing contract", "requirement": "Align canonical string with Exhibit B.",
|
|
253
|
+
"phase": "MVP", "fe": "", "be": "M" } ] } ] },
|
|
254
|
+
"teamMix": { "fte": { "backend": 1.5, "frontend": 0.4, "qa": 0.4, "pm": 0.25, "ba": 0.1 } },
|
|
255
|
+
"techStack": [ { "category": "Frontend", "description": "Next.js 16 App Router / React" } ]
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
- `tshirt.mode` `items` writes whole rows below the header (halts if rows exist unless `--replace`); `sizes` fills `E:L` on rows that already exist (a gap-analysis sheet), matched by the `(id)` suffix in column C — never by position.
|
|
260
|
+
- `teamMix.fte` is per-discipline FTE (`backend` `frontend` `qa` `pm` `ba` `devops` `techlead`). The tool splits it into people (saturate then spill), computes months and the column count, ramps by discipline, writes the remainder formula, and refuses a roster that leaves a weighted MF factor unstaffed.
|
|
261
|
+
- The MF list, legend, rate and high factor are READ from the sheet; the plan never carries them.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Tekyz Estimation + PRD Playbook
|
|
2
2
|
|
|
3
3
|
> Reusable procedure for producing a Tekyz client estimate (Google Sheet: T-Shirt
|
|
4
|
-
> Size + Team Mix
|
|
4
|
+
> Size + Team Mix + Technology Stack), from **any structured work
|
|
5
5
|
> document** — a GSD-T tech-debt scan register, a new-feature or new-application
|
|
6
6
|
> requirements doc, or an existing PRD. Proven on the HILO Figma ATOS project
|
|
7
7
|
> (21 criticals → 32.73 eng-days → $13,090–$16,362). Apply to E-Learning and any
|
|
@@ -106,7 +106,11 @@ range; `insertDimension` to shift hardcoded SUMIF ranges; full `spreadsheets` sc
|
|
|
106
106
|
|
|
107
107
|
---
|
|
108
108
|
|
|
109
|
-
## Phase 6 — The PRD deliverable
|
|
109
|
+
## Phase 6 — The PRD deliverable (NOT part of `/gsd-t-estimate` since v5.20.10 — run `/gsd-t-prd`)
|
|
110
|
+
|
|
111
|
+
`/gsd-t-estimate` writes the SHEET only (T-Shirt Size + Team Mix + Technology Stack; layout,
|
|
112
|
+
formulas, styling and the post-write audit are in `~/.claude/templates/estimate-sheet-spec.md`).
|
|
113
|
+
The PRD below is a separate deliverable when the client wants one.
|
|
110
114
|
|
|
111
115
|
One document, **domain sub-sections (A–G) inside each numbered section** (0–15).
|
|
112
116
|
|
|
@@ -126,5 +130,5 @@ One document, **domain sub-sections (A–G) inside each numbered section** (0–
|
|
|
126
130
|
|
|
127
131
|
## Phase 7 — Deliver
|
|
128
132
|
|
|
129
|
-
|
|
130
|
-
|
|
133
|
+
Confirm the estimate views agree: T-Shirt total = Team Mix total (= PRD §10 total, if a PRD was
|
|
134
|
+
written). Run the read-back audit in `estimate-sheet-spec.md` §5 before reporting.
|