@pixerate/editor 0.223.0 → 0.223.2
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/dist/agent/index.cjs +2 -2
- package/dist/agent/index.js +1 -1
- package/dist/{chunk-SVHXD2VH.js → chunk-H73MSHYZ.js} +5 -1
- package/dist/{chunk-BDT2IYXK.cjs → chunk-VXLYEYP4.cjs} +6 -2
- package/dist/tools/index.cjs +2 -2
- package/dist/tools/index.js +1 -1
- package/dist/tools/skill-evals.d.cts +6 -1
- package/dist/tools/skill-evals.d.ts +6 -1
- package/package.json +1 -1
- package/skills/README.md +6 -1
- package/skills/pixerate-chart/SKILL.md +8 -0
- package/skills/pixerate-chart-annotate/SKILL.md +96 -0
- package/skills/pixerate-chart-annotate/evals/highlight-the-point.json +78 -0
- package/skills/pixerate-chart-annotate/evals/source-and-units.json +76 -0
- package/skills/pixerate-chart-annotate/evals/target-line.json +85 -0
- package/skills/pixerate-chart-choose-and-build/SKILL.md +100 -0
- package/skills/pixerate-chart-choose-and-build/evals/colour-blind-safe.json +113 -0
- package/skills/pixerate-chart-choose-and-build/evals/honest-baseline.json +81 -0
- package/skills/pixerate-chart-choose-and-build/evals/trend-over-time.json +175 -0
- package/skills/pixerate-spreadsheet/SKILL.md +2 -0
- package/skills/pixerate-spreadsheet-analysis/SKILL.md +97 -0
- package/skills/pixerate-spreadsheet-analysis/evals/count-and-average.json +148 -0
- package/skills/pixerate-spreadsheet-analysis/evals/month-over-month.json +128 -0
- package/skills/pixerate-spreadsheet-analysis/evals/summary-by-region.json +178 -0
- package/skills/pixerate-spreadsheet-financial-model/SKILL.md +98 -0
- package/skills/pixerate-spreadsheet-financial-model/evals/find-hardcoded.json +113 -0
- package/skills/pixerate-spreadsheet-financial-model/evals/projection-with-inputs.json +118 -0
- package/skills/pixerate-spreadsheet-financial-model/evals/scenarios.json +136 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "For each product, show how many orders there were and the average order amount, in a small table starting at G1 (Product, Orders, Average), with formulas.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "spreadsheet",
|
|
5
|
+
"rows": [
|
|
6
|
+
[
|
|
7
|
+
"Date",
|
|
8
|
+
"Region",
|
|
9
|
+
"Product",
|
|
10
|
+
"Amount"
|
|
11
|
+
],
|
|
12
|
+
[
|
|
13
|
+
"2026-01-05",
|
|
14
|
+
"North",
|
|
15
|
+
"Widget",
|
|
16
|
+
120
|
|
17
|
+
],
|
|
18
|
+
[
|
|
19
|
+
"2026-01-07",
|
|
20
|
+
"South",
|
|
21
|
+
"Gadget",
|
|
22
|
+
80
|
|
23
|
+
],
|
|
24
|
+
[
|
|
25
|
+
"2026-01-12",
|
|
26
|
+
"North",
|
|
27
|
+
"Gadget",
|
|
28
|
+
95
|
|
29
|
+
],
|
|
30
|
+
[
|
|
31
|
+
"2026-01-15",
|
|
32
|
+
"East",
|
|
33
|
+
"Widget",
|
|
34
|
+
60
|
|
35
|
+
],
|
|
36
|
+
[
|
|
37
|
+
"2026-01-20",
|
|
38
|
+
"West",
|
|
39
|
+
"Widget",
|
|
40
|
+
150
|
|
41
|
+
],
|
|
42
|
+
[
|
|
43
|
+
"2026-02-02",
|
|
44
|
+
"South",
|
|
45
|
+
"Widget",
|
|
46
|
+
110
|
|
47
|
+
],
|
|
48
|
+
[
|
|
49
|
+
"2026-02-08",
|
|
50
|
+
"East",
|
|
51
|
+
"Gadget",
|
|
52
|
+
70
|
|
53
|
+
],
|
|
54
|
+
[
|
|
55
|
+
"2026-02-11",
|
|
56
|
+
"North",
|
|
57
|
+
"Widget",
|
|
58
|
+
130
|
|
59
|
+
],
|
|
60
|
+
[
|
|
61
|
+
"2026-02-19",
|
|
62
|
+
"West",
|
|
63
|
+
"Gadget",
|
|
64
|
+
90
|
|
65
|
+
],
|
|
66
|
+
[
|
|
67
|
+
"2026-02-25",
|
|
68
|
+
"South",
|
|
69
|
+
"Gadget",
|
|
70
|
+
85
|
|
71
|
+
]
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"checks": [
|
|
75
|
+
{
|
|
76
|
+
"why": "Widget: 5 orders averaging 114; Gadget: 5 orders averaging 84",
|
|
77
|
+
"tool": "get_range",
|
|
78
|
+
"args": {
|
|
79
|
+
"range": "G2:I10"
|
|
80
|
+
},
|
|
81
|
+
"expect": {
|
|
82
|
+
"values": {
|
|
83
|
+
"$nonEmpty": {
|
|
84
|
+
"$length": 2,
|
|
85
|
+
"$expr": "v.some((r) => r[0] === 'Widget' && r[1] === 5 && Math.abs(r[2] - 114) < 0.01) && v.some((r) => r[0] === 'Gadget' && r[1] === 5 && Math.abs(r[2] - 84) < 0.01)"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"why": "formulas for the counts and averages",
|
|
92
|
+
"tool": "get_range",
|
|
93
|
+
"args": {
|
|
94
|
+
"range": "H2:I3"
|
|
95
|
+
},
|
|
96
|
+
"expect": {
|
|
97
|
+
"raw": {
|
|
98
|
+
"$every": {
|
|
99
|
+
"$every": {
|
|
100
|
+
"$match": "^="
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"why": "headers",
|
|
108
|
+
"tool": "get_range",
|
|
109
|
+
"args": {
|
|
110
|
+
"range": "G1:I1"
|
|
111
|
+
},
|
|
112
|
+
"expect": {
|
|
113
|
+
"values": [
|
|
114
|
+
{
|
|
115
|
+
"$every": {
|
|
116
|
+
"$match": "\\S"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
"solution": [
|
|
124
|
+
{
|
|
125
|
+
"tool": "set_range",
|
|
126
|
+
"args": {
|
|
127
|
+
"start": "G1",
|
|
128
|
+
"values": [
|
|
129
|
+
[
|
|
130
|
+
"Product",
|
|
131
|
+
"Orders",
|
|
132
|
+
"Average"
|
|
133
|
+
],
|
|
134
|
+
[
|
|
135
|
+
"Widget",
|
|
136
|
+
"=COUNTIFS($C$2:$C$11,G2)",
|
|
137
|
+
"=AVERAGEIFS($D$2:$D$11,$C$2:$C$11,G2)"
|
|
138
|
+
],
|
|
139
|
+
[
|
|
140
|
+
"Gadget",
|
|
141
|
+
"=COUNTIFS($C$2:$C$11,G3)",
|
|
142
|
+
"=AVERAGEIFS($D$2:$D$11,$C$2:$C$11,G3)"
|
|
143
|
+
]
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Add month-over-month growth in column C (header in C1) as a fraction (0.1 for 10 %), with formulas. January has no previous month.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "spreadsheet",
|
|
5
|
+
"rows": [
|
|
6
|
+
[
|
|
7
|
+
"Month",
|
|
8
|
+
"Revenue"
|
|
9
|
+
],
|
|
10
|
+
[
|
|
11
|
+
"Jan",
|
|
12
|
+
100
|
|
13
|
+
],
|
|
14
|
+
[
|
|
15
|
+
"Feb",
|
|
16
|
+
110
|
|
17
|
+
],
|
|
18
|
+
[
|
|
19
|
+
"Mar",
|
|
20
|
+
99
|
|
21
|
+
],
|
|
22
|
+
[
|
|
23
|
+
"Apr",
|
|
24
|
+
121
|
|
25
|
+
],
|
|
26
|
+
[
|
|
27
|
+
"May",
|
|
28
|
+
121
|
|
29
|
+
],
|
|
30
|
+
[
|
|
31
|
+
"Jun",
|
|
32
|
+
133
|
|
33
|
+
]
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"checks": [
|
|
37
|
+
{
|
|
38
|
+
"why": "growth for February to June",
|
|
39
|
+
"tool": "get_range",
|
|
40
|
+
"args": {
|
|
41
|
+
"range": "C3:C7"
|
|
42
|
+
},
|
|
43
|
+
"expect": {
|
|
44
|
+
"values": {
|
|
45
|
+
"$expr": "[0.1, -0.1, 0.2222, 0, 0.0992].every((g, i) => Math.abs(v[i][0] - g) < 0.001)"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"why": "formulas, not typed numbers",
|
|
51
|
+
"tool": "get_range",
|
|
52
|
+
"args": {
|
|
53
|
+
"range": "C3:C7"
|
|
54
|
+
},
|
|
55
|
+
"expect": {
|
|
56
|
+
"raw": {
|
|
57
|
+
"$every": {
|
|
58
|
+
"$every": {
|
|
59
|
+
"$match": "^="
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"why": "January is left empty, not 0 or an error",
|
|
67
|
+
"tool": "get_range",
|
|
68
|
+
"args": {
|
|
69
|
+
"range": "C2"
|
|
70
|
+
},
|
|
71
|
+
"expect": {
|
|
72
|
+
"values": [
|
|
73
|
+
[
|
|
74
|
+
""
|
|
75
|
+
]
|
|
76
|
+
],
|
|
77
|
+
"errors": []
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"why": "a header",
|
|
82
|
+
"tool": "get_range",
|
|
83
|
+
"args": {
|
|
84
|
+
"range": "C1"
|
|
85
|
+
},
|
|
86
|
+
"expect": {
|
|
87
|
+
"values": [
|
|
88
|
+
[
|
|
89
|
+
{
|
|
90
|
+
"$match": "\\S"
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"solution": [
|
|
98
|
+
{
|
|
99
|
+
"tool": "set_range",
|
|
100
|
+
"args": {
|
|
101
|
+
"start": "C1",
|
|
102
|
+
"values": [
|
|
103
|
+
[
|
|
104
|
+
"Growth"
|
|
105
|
+
],
|
|
106
|
+
[
|
|
107
|
+
null
|
|
108
|
+
],
|
|
109
|
+
[
|
|
110
|
+
"=B3/B2-1"
|
|
111
|
+
],
|
|
112
|
+
[
|
|
113
|
+
"=B4/B3-1"
|
|
114
|
+
],
|
|
115
|
+
[
|
|
116
|
+
"=B5/B4-1"
|
|
117
|
+
],
|
|
118
|
+
[
|
|
119
|
+
"=B6/B5-1"
|
|
120
|
+
],
|
|
121
|
+
[
|
|
122
|
+
"=B7/B6-1"
|
|
123
|
+
]
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Make a summary of total sales by region next to the data, starting at G1 (a header row, then one row per region), using formulas so it updates when the data changes.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "spreadsheet",
|
|
5
|
+
"rows": [
|
|
6
|
+
[
|
|
7
|
+
"Date",
|
|
8
|
+
"Region",
|
|
9
|
+
"Product",
|
|
10
|
+
"Amount"
|
|
11
|
+
],
|
|
12
|
+
[
|
|
13
|
+
"2026-01-05",
|
|
14
|
+
"North",
|
|
15
|
+
"Widget",
|
|
16
|
+
120
|
|
17
|
+
],
|
|
18
|
+
[
|
|
19
|
+
"2026-01-07",
|
|
20
|
+
"South",
|
|
21
|
+
"Gadget",
|
|
22
|
+
80
|
|
23
|
+
],
|
|
24
|
+
[
|
|
25
|
+
"2026-01-12",
|
|
26
|
+
"North",
|
|
27
|
+
"Gadget",
|
|
28
|
+
95
|
|
29
|
+
],
|
|
30
|
+
[
|
|
31
|
+
"2026-01-15",
|
|
32
|
+
"East",
|
|
33
|
+
"Widget",
|
|
34
|
+
60
|
|
35
|
+
],
|
|
36
|
+
[
|
|
37
|
+
"2026-01-20",
|
|
38
|
+
"West",
|
|
39
|
+
"Widget",
|
|
40
|
+
150
|
|
41
|
+
],
|
|
42
|
+
[
|
|
43
|
+
"2026-02-02",
|
|
44
|
+
"South",
|
|
45
|
+
"Widget",
|
|
46
|
+
110
|
|
47
|
+
],
|
|
48
|
+
[
|
|
49
|
+
"2026-02-08",
|
|
50
|
+
"East",
|
|
51
|
+
"Gadget",
|
|
52
|
+
70
|
|
53
|
+
],
|
|
54
|
+
[
|
|
55
|
+
"2026-02-11",
|
|
56
|
+
"North",
|
|
57
|
+
"Widget",
|
|
58
|
+
130
|
|
59
|
+
],
|
|
60
|
+
[
|
|
61
|
+
"2026-02-19",
|
|
62
|
+
"West",
|
|
63
|
+
"Gadget",
|
|
64
|
+
90
|
|
65
|
+
],
|
|
66
|
+
[
|
|
67
|
+
"2026-02-25",
|
|
68
|
+
"South",
|
|
69
|
+
"Gadget",
|
|
70
|
+
85
|
|
71
|
+
]
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"checks": [
|
|
75
|
+
{
|
|
76
|
+
"why": "one row per region with the right totals",
|
|
77
|
+
"tool": "get_range",
|
|
78
|
+
"args": {
|
|
79
|
+
"range": "G2:H10"
|
|
80
|
+
},
|
|
81
|
+
"expect": {
|
|
82
|
+
"values": {
|
|
83
|
+
"$nonEmpty": {
|
|
84
|
+
"$length": 4,
|
|
85
|
+
"$expr": "[['North',345],['South',275],['East',130],['West',240]].every(([r, t]) => v.some((row) => row[0] === r && row[1] === t))"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"why": "the totals are formulas over the data, not typed numbers",
|
|
92
|
+
"tool": "get_range",
|
|
93
|
+
"args": {
|
|
94
|
+
"range": "H2:H5"
|
|
95
|
+
},
|
|
96
|
+
"expect": {
|
|
97
|
+
"raw": {
|
|
98
|
+
"$every": {
|
|
99
|
+
"$every": {
|
|
100
|
+
"$match": [
|
|
101
|
+
"^=.*(SUMIFS?|SUMPRODUCT|FILTER)",
|
|
102
|
+
"i"
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"why": "a header row",
|
|
111
|
+
"tool": "get_range",
|
|
112
|
+
"args": {
|
|
113
|
+
"range": "G1:H1"
|
|
114
|
+
},
|
|
115
|
+
"expect": {
|
|
116
|
+
"values": [
|
|
117
|
+
{
|
|
118
|
+
"$every": {
|
|
119
|
+
"$match": "\\S"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"why": "no formula errors",
|
|
127
|
+
"tool": "get_range",
|
|
128
|
+
"args": {
|
|
129
|
+
"range": "G1:H10"
|
|
130
|
+
},
|
|
131
|
+
"expect": {
|
|
132
|
+
"errors": []
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
],
|
|
136
|
+
"solution": [
|
|
137
|
+
{
|
|
138
|
+
"tool": "get_range",
|
|
139
|
+
"args": {
|
|
140
|
+
"range": "A1:D20"
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"tool": "set_range",
|
|
145
|
+
"args": {
|
|
146
|
+
"start": "G1",
|
|
147
|
+
"values": [
|
|
148
|
+
[
|
|
149
|
+
"Region",
|
|
150
|
+
"Total sales"
|
|
151
|
+
],
|
|
152
|
+
[
|
|
153
|
+
"North",
|
|
154
|
+
"=SUMIFS($D$2:$D$11,$B$2:$B$11,G2)"
|
|
155
|
+
],
|
|
156
|
+
[
|
|
157
|
+
"South",
|
|
158
|
+
"=SUMIFS($D$2:$D$11,$B$2:$B$11,G3)"
|
|
159
|
+
],
|
|
160
|
+
[
|
|
161
|
+
"East",
|
|
162
|
+
"=SUMIFS($D$2:$D$11,$B$2:$B$11,G4)"
|
|
163
|
+
],
|
|
164
|
+
[
|
|
165
|
+
"West",
|
|
166
|
+
"=SUMIFS($D$2:$D$11,$B$2:$B$11,G5)"
|
|
167
|
+
]
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"tool": "get_range",
|
|
173
|
+
"args": {
|
|
174
|
+
"range": "G1:H5"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
]
|
|
178
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pixerate-spreadsheet-financial-model
|
|
3
|
+
description: The financial model workflow in a Pixerate spreadsheet - inputs (assumptions) in labelled cells kept apart from calculations and outputs, one consistent formula across each row, absolute references to inputs, no hard-coded numbers in formulas, projections that compound, scenario and sensitivity tables, checks that totals tie, and finding the hard-coded or broken cell in an existing model. Use when asked to build or check a financial model, forecast, projection, budget, business case, revenue or cost model, break-even, loan or growth calculation, scenario or what-if table, or to find why a model does not update, or when get_range and set_range are available. Load with pixerate-spreadsheet, which lists every tool.
|
|
4
|
+
metadata:
|
|
5
|
+
kind: workflow
|
|
6
|
+
reference: pixerate-spreadsheet
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Building a financial model
|
|
10
|
+
|
|
11
|
+
A model is inputs, calculations and outputs. Inputs are the assumptions a person changes
|
|
12
|
+
(starting revenue, growth, prices); calculations turn them into numbers period by period;
|
|
13
|
+
outputs are what the reader looks at (totals, year-5 revenue, profit). Every calculation
|
|
14
|
+
refers to inputs by absolute reference and never contains a typed number, so changing one
|
|
15
|
+
assumption flows through everything.
|
|
16
|
+
|
|
17
|
+
This skill is the method. `pixerate-spreadsheet` has the full tool reference and functions.
|
|
18
|
+
|
|
19
|
+
## Goal and quality bar
|
|
20
|
+
|
|
21
|
+
- Inputs live in their own labelled cells at the top (or on their own block), one assumption per
|
|
22
|
+
cell, with units in the label.
|
|
23
|
+
- Each row of the projection uses one formula pattern across its periods, referring to inputs
|
|
24
|
+
absolutely (`$B$2`) and to the previous period relatively (`B5`).
|
|
25
|
+
- No hard-coded numbers in calculation formulas (no `*1.08`, no `+5000`); constants that matter
|
|
26
|
+
are inputs.
|
|
27
|
+
- Outputs are formulas; checks tie (subtotals add to totals, a balance closes).
|
|
28
|
+
- No formula errors; the model gives the expected value for a case you can work out by hand.
|
|
29
|
+
|
|
30
|
+
## Inputs to confirm
|
|
31
|
+
|
|
32
|
+
- **Assumptions** and their values and units (growth as 0.08 or 8 %, currency).
|
|
33
|
+
- **Periods** (years or months) and how many.
|
|
34
|
+
- **Layout** the user expects (where inputs, the projection and outputs go).
|
|
35
|
+
- **Scenarios** to compare (low, base, high).
|
|
36
|
+
|
|
37
|
+
## Working method
|
|
38
|
+
|
|
39
|
+
1. **Read** what exists: `get_range` the whole model area, `raw` as well as `values`.
|
|
40
|
+
**Check:** you can list the inputs, the calculation rows and the outputs.
|
|
41
|
+
2. **Inputs block.** Labels in column A, values in B (`set_range` in one call). Rates as
|
|
42
|
+
fractions (0.08), money as plain numbers with the currency in the label.
|
|
43
|
+
3. **Timeline.** A header row of periods (years 1-5 in B4:F4).
|
|
44
|
+
4. **Calculation rows.** One `set_range` per row: the first period from an input (`=$B$1`),
|
|
45
|
+
later periods from the previous one and the input (`=B5*(1+$B$2)`), the same pattern across.
|
|
46
|
+
**Check:** `get_range` values match a hand calculation (100,000 at 8 % is 108,000, then
|
|
47
|
+
116,640); `errors` empty.
|
|
48
|
+
5. **Outputs and checks.** Totals (`=SUM(B5:F5)`), year-N values, and a check cell that should be
|
|
49
|
+
0 or TRUE (`=SUM(B7:F7)-B9`).
|
|
50
|
+
6. **Scenarios.** A small table with the scenario inputs in one column and the output formula
|
|
51
|
+
next to each, referring to the starting input absolutely and its own scenario cell
|
|
52
|
+
relatively (`=$B$1*(1+A9)^4` for year 5).
|
|
53
|
+
**Check:** the base scenario equals the main model's output.
|
|
54
|
+
7. **Audit.** Read `raw` across every calculation row: each should match its neighbours' pattern;
|
|
55
|
+
a formula with a typed number or a different reference is the bug.
|
|
56
|
+
|
|
57
|
+
## Decision table
|
|
58
|
+
|
|
59
|
+
| Need | Formula pattern |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| Compound growth period on period | `=prev*(1+$rate$)` |
|
|
62
|
+
| Value at period n directly | `=$start$*(1+$rate$)^(n-1)` |
|
|
63
|
+
| Cost as a share of revenue | `=revenue_cell*$cost_share$` |
|
|
64
|
+
| Fixed cost each period | `=$fixed_cost$` |
|
|
65
|
+
| Running total | `=prev_total+this_period` |
|
|
66
|
+
| Profit | `=revenue-costs` |
|
|
67
|
+
| Scenario output | Input in its own cell per scenario, output formula next to it |
|
|
68
|
+
| Something does not update | Find the formula with a typed number or a missing `$` |
|
|
69
|
+
|
|
70
|
+
## Recipes
|
|
71
|
+
|
|
72
|
+
**Five-year projection.** `set_range` from A1: `[["Starting revenue", 100000], ["Growth", 0.08],
|
|
73
|
+
[], ["Year", 1, 2, 3, 4, 5], ["Revenue", "=$B$1", "=B5*(1+$B$2)", "=C5*(1+$B$2)",
|
|
74
|
+
"=D5*(1+$B$2)", "=E5*(1+$B$2)"]]`.
|
|
75
|
+
|
|
76
|
+
**Find a hard-coded number.** `get_range` the calculation rows; compare each `raw` with its
|
|
77
|
+
neighbours; replace the odd one with the pattern (`set_cell`).
|
|
78
|
+
|
|
79
|
+
**Scenarios.** From A8: `[["Growth", "Year 5 revenue"], [0.05, "=$B$1*(1+A9)^4"], [0.08,
|
|
80
|
+
"=$B$1*(1+A10)^4"], [0.12, "=$B$1*(1+A11)^4"]]`.
|
|
81
|
+
|
|
82
|
+
## Pitfalls
|
|
83
|
+
|
|
84
|
+
- **Typed numbers in formulas** (`=C5*1.08`) do not follow the input; the classic broken model.
|
|
85
|
+
- **Relative input references** shift as formulas copy across; inputs need `$`.
|
|
86
|
+
- **Mixed patterns in a row** hide errors; one formula pattern per row.
|
|
87
|
+
- **Percent confusion**: 8 typed as a rate is 800 %; write 0.08 or `8%`.
|
|
88
|
+
- **Circular references** (`#CYCLE!`) from a total that includes itself.
|
|
89
|
+
- **`set_range` does not shift references** the way fill does; write each cell's own.
|
|
90
|
+
- **Outputs typed as values** break when an input changes.
|
|
91
|
+
|
|
92
|
+
## Tools used
|
|
93
|
+
|
|
94
|
+
- `get_range`: read `raw` and `values`, check results and errors
|
|
95
|
+
- `set_range`: write the inputs, timeline, rows and scenario tables, one undo step each
|
|
96
|
+
- `set_cell`: fix one formula
|
|
97
|
+
- `insert_rows`, `insert_columns`: add periods or lines
|
|
98
|
+
- `undo_spreadsheet`: take back a step
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Something in this revenue model doesn't update when I change the growth rate in B2. Find and fix it.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "spreadsheet",
|
|
5
|
+
"rows": [
|
|
6
|
+
[
|
|
7
|
+
"Starting revenue",
|
|
8
|
+
100000,
|
|
9
|
+
null,
|
|
10
|
+
null,
|
|
11
|
+
null,
|
|
12
|
+
null
|
|
13
|
+
],
|
|
14
|
+
[
|
|
15
|
+
"Growth",
|
|
16
|
+
0.08,
|
|
17
|
+
null,
|
|
18
|
+
null,
|
|
19
|
+
null,
|
|
20
|
+
null
|
|
21
|
+
],
|
|
22
|
+
[
|
|
23
|
+
null,
|
|
24
|
+
null,
|
|
25
|
+
null,
|
|
26
|
+
null,
|
|
27
|
+
null,
|
|
28
|
+
null
|
|
29
|
+
],
|
|
30
|
+
[
|
|
31
|
+
"Year",
|
|
32
|
+
1,
|
|
33
|
+
2,
|
|
34
|
+
3,
|
|
35
|
+
4,
|
|
36
|
+
5
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
"Revenue",
|
|
40
|
+
"=$B$1",
|
|
41
|
+
"=B5*(1+$B$2)",
|
|
42
|
+
"=C5*1.08",
|
|
43
|
+
"=D5*(1+$B$2)",
|
|
44
|
+
"=E5*(1+$B$2)"
|
|
45
|
+
]
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"checks": [
|
|
49
|
+
{
|
|
50
|
+
"why": "no typed growth rate left in the projection",
|
|
51
|
+
"tool": "get_range",
|
|
52
|
+
"args": {
|
|
53
|
+
"range": "C5:F5"
|
|
54
|
+
},
|
|
55
|
+
"expect": {
|
|
56
|
+
"raw": [
|
|
57
|
+
{
|
|
58
|
+
"$every": {
|
|
59
|
+
"$match": "^=.*\\$?B\\$?2",
|
|
60
|
+
"$not": {
|
|
61
|
+
"$match": "1\\.08"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"why": "the values are unchanged at 8 %",
|
|
70
|
+
"tool": "get_range",
|
|
71
|
+
"args": {
|
|
72
|
+
"range": "F5"
|
|
73
|
+
},
|
|
74
|
+
"expect": {
|
|
75
|
+
"values": {
|
|
76
|
+
"$expr": "Math.abs(v[0][0] - 136048.896) < 0.01"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"why": "the inputs are untouched",
|
|
82
|
+
"tool": "get_range",
|
|
83
|
+
"args": {
|
|
84
|
+
"range": "B1:B2"
|
|
85
|
+
},
|
|
86
|
+
"expect": {
|
|
87
|
+
"values": [
|
|
88
|
+
[
|
|
89
|
+
100000
|
|
90
|
+
],
|
|
91
|
+
[
|
|
92
|
+
0.08
|
|
93
|
+
]
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
"solution": [
|
|
99
|
+
{
|
|
100
|
+
"tool": "get_range",
|
|
101
|
+
"args": {
|
|
102
|
+
"range": "A1:F5"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"tool": "set_cell",
|
|
107
|
+
"args": {
|
|
108
|
+
"cell": "D5",
|
|
109
|
+
"value": "=C5*(1+$B$2)"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|