dsh-office-kit 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/CAPABILITY.md +147 -68
  2. package/LICENSE.md +21 -21
  3. package/README.md +108 -106
  4. package/assets/skills/excel-live-control/SKILL.md +234 -0
  5. package/assets/skills/excel-live-control/container_tools/live_excel.py +1589 -0
  6. package/assets/skills/excel-live-control/domain_guidance/financial_models.md +86 -0
  7. package/assets/skills/excel-live-control/domain_guidance/healthcare.md +58 -0
  8. package/assets/skills/excel-live-control/domain_guidance/marketing_advertising.md +53 -0
  9. package/assets/skills/excel-live-control/domain_guidance/scientific_research.md +53 -0
  10. package/assets/skills/excel-live-control/references/office-automation.md +207 -0
  11. package/assets/skills/excel-live-control/references/read_only_qna.md +59 -0
  12. package/assets/skills/excel-live-control/style_guidelines.md +87 -0
  13. package/assets/skills/excel-live-control/workflows/create_workflows.md +78 -0
  14. package/assets/skills/excel-live-control/workflows/edit_workflows.md +72 -0
  15. package/assets/skills/office-documents/SKILL.md +90 -77
  16. package/assets/skills/office-documents/examples/end_to_end_smoke_test.md +86 -0
  17. package/assets/skills/office-documents/ooxml/comments.md +40 -0
  18. package/assets/skills/office-documents/ooxml/hyperlinks_and_fields.md +66 -0
  19. package/assets/skills/office-documents/ooxml/rels_and_content_types.md +62 -0
  20. package/assets/skills/office-documents/ooxml/tracked_changes.md +53 -0
  21. package/assets/skills/office-documents/references/docx-structure.md +83 -0
  22. package/assets/skills/office-documents/references/template-elicitation.md +49 -0
  23. package/assets/skills/office-documents/references/writing-quality.md +49 -0
  24. package/assets/skills/office-documents/render_docx.py +92 -0
  25. package/assets/skills/office-documents/scripts/a11y_audit.py +194 -0
  26. package/assets/skills/office-documents/scripts/accept_tracked_changes.py +151 -0
  27. package/assets/skills/office-documents/scripts/add_tracked_replacements.py +148 -0
  28. package/assets/skills/office-documents/scripts/append_blocks.py +108 -0
  29. package/assets/skills/office-documents/scripts/apply_template_styles.py +104 -0
  30. package/assets/skills/office-documents/scripts/captions_and_crossrefs.py +102 -0
  31. package/assets/skills/office-documents/scripts/comments_add.py +136 -0
  32. package/assets/skills/office-documents/scripts/comments_apply_patch.py +114 -0
  33. package/assets/skills/office-documents/scripts/comments_extract.py +85 -0
  34. package/assets/skills/office-documents/scripts/comments_strip.py +74 -0
  35. package/assets/skills/office-documents/scripts/content_controls.py +192 -0
  36. package/assets/skills/office-documents/scripts/core_properties.py +124 -0
  37. package/assets/skills/office-documents/scripts/docx_kit.py +587 -0
  38. package/assets/skills/office-documents/scripts/docx_ooxml_patch.py +225 -0
  39. package/assets/skills/office-documents/scripts/docx_table_to_csv.py +99 -0
  40. package/assets/skills/office-documents/scripts/fields_materialize.py +66 -0
  41. package/assets/skills/office-documents/scripts/fields_report.py +59 -0
  42. package/assets/skills/office-documents/scripts/flatten_ref_fields.py +62 -0
  43. package/assets/skills/office-documents/scripts/footnotes_report.py +77 -0
  44. package/assets/skills/office-documents/scripts/google_docs_title_sanitize.py +96 -0
  45. package/assets/skills/office-documents/scripts/heading_audit.py +81 -0
  46. package/assets/skills/office-documents/scripts/images_audit.py +80 -0
  47. package/assets/skills/office-documents/scripts/insert_note.py +126 -0
  48. package/assets/skills/office-documents/scripts/insert_ref_fields.py +90 -0
  49. package/assets/skills/office-documents/scripts/insert_toc.py +84 -0
  50. package/assets/skills/office-documents/scripts/internal_nav.py +146 -0
  51. package/assets/skills/office-documents/scripts/make_fixtures.py +169 -0
  52. package/assets/skills/office-documents/scripts/merge_docx_append.py +239 -0
  53. package/assets/skills/office-documents/scripts/privacy_scrub.py +131 -0
  54. package/assets/skills/office-documents/scripts/redact_docx.py +113 -0
  55. package/assets/skills/office-documents/scripts/render_and_diff.py +101 -0
  56. package/assets/skills/office-documents/scripts/replace_text.py +154 -0
  57. package/assets/skills/office-documents/scripts/section_audit.py +91 -0
  58. package/assets/skills/office-documents/scripts/set_protection.py +92 -0
  59. package/assets/skills/office-documents/scripts/style_lint.py +111 -0
  60. package/assets/skills/office-documents/scripts/style_normalize.py +103 -0
  61. package/assets/skills/office-documents/scripts/table_geometry.py +167 -0
  62. package/assets/skills/office-documents/scripts/watermark_add.py +48 -0
  63. package/assets/skills/office-documents/scripts/watermark_audit_remove.py +67 -0
  64. package/assets/skills/office-documents/scripts/xlsx_to_docx_table.py +117 -0
  65. package/assets/skills/office-documents/tasks/accessibility_a11y.md +45 -0
  66. package/assets/skills/office-documents/tasks/captions_crossrefs.md +49 -0
  67. package/assets/skills/office-documents/tasks/clean_tracked_changes.md +49 -0
  68. package/assets/skills/office-documents/tasks/comments_manage.md +59 -0
  69. package/assets/skills/office-documents/tasks/compare_diff.md +40 -0
  70. package/assets/skills/office-documents/tasks/create_edit.md +107 -0
  71. package/assets/skills/office-documents/tasks/equations.md +59 -0
  72. package/assets/skills/office-documents/tasks/fields_update.md +44 -0
  73. package/assets/skills/office-documents/tasks/fixtures_edge_cases.md +50 -0
  74. package/assets/skills/office-documents/tasks/footnotes_endnotes.md +38 -0
  75. package/assets/skills/office-documents/tasks/forms_content_controls.md +48 -0
  76. package/assets/skills/office-documents/tasks/headings_numbering.md +55 -0
  77. package/assets/skills/office-documents/tasks/images_figures.md +63 -0
  78. package/assets/skills/office-documents/tasks/multi_doc_merge.md +43 -0
  79. package/assets/skills/office-documents/tasks/navigation_internal_links.md +38 -0
  80. package/assets/skills/office-documents/tasks/privacy_scrub_metadata.md +45 -0
  81. package/assets/skills/office-documents/tasks/protection_restrict_editing.md +41 -0
  82. package/assets/skills/office-documents/tasks/read_review.md +50 -0
  83. package/assets/skills/office-documents/tasks/redaction_anonymization.md +42 -0
  84. package/assets/skills/office-documents/tasks/sections_layout.md +54 -0
  85. package/assets/skills/office-documents/tasks/style_lint_normalize.md +44 -0
  86. package/assets/skills/office-documents/tasks/tables_spreadsheets.md +64 -0
  87. package/assets/skills/office-documents/tasks/templates_style_packs.md +53 -0
  88. package/assets/skills/office-documents/tasks/toc_workflow.md +39 -0
  89. package/assets/skills/office-documents/tasks/verify_render.md +50 -0
  90. package/assets/skills/office-documents/tasks/watermarks_background.md +35 -0
  91. package/assets/skills/office-documents/troubleshooting/libreoffice_headless.md +47 -0
  92. package/assets/skills/office-documents/troubleshooting/run_splitting.md +36 -0
  93. package/assets/skills/office-pdf/SKILL.md +73 -56
  94. package/assets/skills/office-pdf/references/form-fields.md +56 -0
  95. package/assets/skills/office-pdf/references/geometry.md +44 -0
  96. package/assets/skills/office-pdf/references/layout-quality.md +41 -0
  97. package/assets/skills/office-pdf/references/limits.md +58 -0
  98. package/assets/skills/office-pdf/scripts/extract_text.py +172 -0
  99. package/assets/skills/office-pdf/scripts/forms.py +556 -0
  100. package/assets/skills/office-pdf/scripts/metadata.py +161 -0
  101. package/assets/skills/office-pdf/scripts/page_ops.py +198 -0
  102. package/assets/skills/office-pdf/scripts/redact_region.py +347 -0
  103. package/assets/skills/office-pdf/scripts/render_pages.py +207 -0
  104. package/assets/skills/office-pdf/scripts/split_pages.py +155 -0
  105. package/assets/skills/office-pdf/scripts/stamp_overlay.py +199 -0
  106. package/assets/skills/office-pdf/tasks/annotations.md +98 -0
  107. package/assets/skills/office-pdf/tasks/create.md +75 -0
  108. package/assets/skills/office-pdf/tasks/edit-pages.md +71 -0
  109. package/assets/skills/office-pdf/tasks/forms.md +67 -0
  110. package/assets/skills/office-pdf/tasks/read.md +62 -0
  111. package/assets/skills/office-pdf/tasks/redact.md +72 -0
  112. package/assets/skills/office-pdf/tasks/stamp.md +60 -0
  113. package/assets/skills/office-presentations/SKILL.md +141 -87
  114. package/assets/skills/office-presentations/container_tools/add_slides.py +136 -0
  115. package/assets/skills/office-presentations/container_tools/core_properties.py +120 -0
  116. package/assets/skills/office-presentations/container_tools/create_montage.py +126 -0
  117. package/assets/skills/office-presentations/container_tools/inspect_presentation_layout_geometry.py +285 -0
  118. package/assets/skills/office-presentations/container_tools/inspect_presentation_package_integrity.py +157 -0
  119. package/assets/skills/office-presentations/container_tools/native_chart_title_gate.py +144 -0
  120. package/assets/skills/office-presentations/container_tools/native_quantitative_chart_gate.py +199 -0
  121. package/assets/skills/office-presentations/container_tools/native_table_arithmetic_gate.py +212 -0
  122. package/assets/skills/office-presentations/container_tools/ooxml_helpers.py +345 -0
  123. package/assets/skills/office-presentations/container_tools/preview_render.py +375 -0
  124. package/assets/skills/office-presentations/container_tools/render_slides.py +139 -0
  125. package/assets/skills/office-presentations/container_tools/replace_text.py +168 -0
  126. package/assets/skills/office-presentations/container_tools/template_source_fidelity_gate.py +229 -0
  127. package/assets/skills/office-presentations/domain_guidance/finance.md +66 -0
  128. package/assets/skills/office-presentations/recipes/README.md +71 -0
  129. package/assets/skills/office-presentations/recipes/autolayout.md +88 -0
  130. package/assets/skills/office-presentations/recipes/connectors.md +83 -0
  131. package/assets/skills/office-presentations/recipes/images.md +80 -0
  132. package/assets/skills/office-presentations/recipes/master-and-layouts.md +127 -0
  133. package/assets/skills/office-presentations/recipes/native-charts.md +87 -0
  134. package/assets/skills/office-presentations/recipes/rich-text-runs.md +86 -0
  135. package/assets/skills/office-presentations/recipes/speaker-notes.md +65 -0
  136. package/assets/skills/office-presentations/recipes/tables.md +88 -0
  137. package/assets/skills/office-presentations/recipes/textbox-at-coordinate.md +71 -0
  138. package/assets/skills/office-presentations/recipes/theme-tokens.md +87 -0
  139. package/assets/skills/office-presentations/references/authoring-workflow.md +65 -0
  140. package/assets/skills/office-presentations/references/finalization.md +63 -0
  141. package/assets/skills/office-presentations/references/native-bullets.md +78 -0
  142. package/assets/skills/office-presentations/references/native-evidence.md +67 -0
  143. package/assets/skills/office-presentations/references/style-guidelines.md +66 -0
  144. package/assets/skills/office-presentations/references/templates.md +76 -0
  145. package/assets/skills/office-presentations/routing/google_slides.md +45 -0
  146. package/assets/skills/office-presentations/tasks/apply-annotations.md +83 -0
  147. package/assets/skills/office-presentations/tasks/charts-tables-notes.md +79 -0
  148. package/assets/skills/office-presentations/tasks/create-deck.md +106 -0
  149. package/assets/skills/office-presentations/tasks/edit-deck.md +93 -0
  150. package/assets/skills/office-presentations/tasks/inspect-and-verify.md +107 -0
  151. package/assets/skills/office-presentations/tasks/template-following.md +132 -0
  152. package/assets/skills/office-presentations/template_following_scripts/check_template_fidelity.py +176 -0
  153. package/assets/skills/office-presentations/template_following_scripts/inspect_template_deck.py +154 -0
  154. package/assets/skills/office-presentations/template_following_scripts/make_contact_sheet.py +111 -0
  155. package/assets/skills/office-presentations/template_following_scripts/prepare_template_starter_deck.py +239 -0
  156. package/assets/skills/office-presentations/template_following_scripts/validate_template_plan.py +185 -0
  157. package/assets/skills/office-spreadsheets/SKILL.md +105 -101
  158. package/assets/skills/office-spreadsheets/container_tools/add_chart.py +236 -0
  159. package/assets/skills/office-spreadsheets/container_tools/add_image.py +107 -0
  160. package/assets/skills/office-spreadsheets/container_tools/append_rows.py +122 -0
  161. package/assets/skills/office-spreadsheets/container_tools/diff_workbooks.py +306 -0
  162. package/assets/skills/office-spreadsheets/container_tools/inspect_workbook.py +398 -0
  163. package/assets/skills/office-spreadsheets/container_tools/recalc_gate.py +275 -0
  164. package/assets/skills/office-spreadsheets/container_tools/set_cells.py +135 -0
  165. package/assets/skills/office-spreadsheets/container_tools/sheet_ops.py +134 -0
  166. package/assets/skills/office-spreadsheets/container_tools/style_cells.py +133 -0
  167. package/assets/skills/office-spreadsheets/recipes/charts.md +66 -0
  168. package/assets/skills/office-spreadsheets/recipes/conditional-formats.md +69 -0
  169. package/assets/skills/office-spreadsheets/recipes/data-tables.md +58 -0
  170. package/assets/skills/office-spreadsheets/recipes/named-ranges.md +41 -0
  171. package/assets/skills/office-spreadsheets/recipes/number-formats.md +72 -0
  172. package/assets/skills/office-spreadsheets/recipes/sparklines.md +43 -0
  173. package/assets/skills/office-spreadsheets/references/charts.md +63 -0
  174. package/assets/skills/office-spreadsheets/references/domain/financial-models.md +40 -0
  175. package/assets/skills/office-spreadsheets/references/domain/healthcare.md +22 -0
  176. package/assets/skills/office-spreadsheets/references/domain/marketing.md +28 -0
  177. package/assets/skills/office-spreadsheets/references/domain/research.md +22 -0
  178. package/assets/skills/office-spreadsheets/references/formula-patterns.md +78 -0
  179. package/assets/skills/office-spreadsheets/references/style-guidelines.md +69 -0
  180. package/assets/skills/office-spreadsheets/references/workbook-structure.md +60 -0
  181. package/assets/skills/office-spreadsheets/routing/google-sheets.md +36 -0
  182. package/assets/skills/office-spreadsheets/tasks/analyze-workbook.md +37 -0
  183. package/assets/skills/office-spreadsheets/tasks/annotations.md +51 -0
  184. package/assets/skills/office-spreadsheets/tasks/create-workbook.md +66 -0
  185. package/assets/skills/office-spreadsheets/tasks/diff-workbooks.md +38 -0
  186. package/assets/skills/office-spreadsheets/tasks/edit-workbook.md +69 -0
  187. package/assets/skills/office-spreadsheets/tasks/inspect-workbook.md +38 -0
  188. package/assets/skills/office-spreadsheets/tasks/recalc-and-check.md +57 -0
  189. package/assets/skills/shared/annotation-recipes.md +252 -0
  190. package/assets/skills/shared/check_runtime.py +253 -0
  191. package/assets/skills/shared/common-workflow.md +139 -0
  192. package/assets/skills/shared/scripts/ensure_raster_image.py +187 -0
  193. package/dist/client.js +4 -4
  194. package/dist/client.js.map +1 -1
  195. package/dist/index.js +239 -89
  196. package/package.json +146 -149
  197. package/postinstall.mjs +92 -82
  198. package/python/office_cli.py +171 -246
@@ -0,0 +1,72 @@
1
+ # Number formats
2
+
3
+ A number format changes how a stored value is displayed, never the value. The
4
+ cell keeps full precision; `0.0%` shows `6.4%` for `0.06398`. Never store a
5
+ rounded number to make a column look tidy, and never store `"6.4%"` as text —
6
+ it stops summing.
7
+
8
+ ```python
9
+ import sys
10
+ from openpyxl import load_workbook
11
+
12
+ wb = load_workbook(sys.argv[1])
13
+ ws = wb["Model"]
14
+
15
+ formats = {
16
+ "B11:B14": "#,##0", # whole dollars
17
+ "B6:B7": "0.0%", # rates, one decimal
18
+ "B8": '"$"#,##0', # currency with a literal sign
19
+ "C2:C13": "yyyy-mm-dd", # real dates, ISO order
20
+ "D2:D13": '"$"#,##0;[Red]-"$"#,##0', # negatives in red
21
+ }
22
+ for rng, code in formats.items():
23
+ block = ws[rng]
24
+ rows = block if isinstance(block, tuple) else ((block,),) # a single cell is not a tuple
25
+ for row in rows:
26
+ for cell in row:
27
+ cell.number_format = code
28
+ wb.save(sys.argv[2])
29
+ ```
30
+
31
+ ## The codes worth knowing
32
+
33
+ | Purpose | Code | Notes |
34
+ | --- | --- | --- |
35
+ | Thousands, no decimals | `#,##0` | The default for money in a model |
36
+ | Thousands, two decimals | `#,##0.00` | Prices, unit costs |
37
+ | Percent, one decimal | `0.0%` | Analytical percentages — rates, margins, growth |
38
+ | Percent, no decimal | `0%` | Dashboard and KPI tiles only |
39
+ | Percent, two decimals | `0.00%` | When differences of a tenth of a point matter |
40
+ | Currency | `"$"#,##0` | A literal sign keeps the unit visible in every cell |
41
+ | Negative in red | `#,##0;[Red]-#,##0` | Check that the reader wants colour, not a `( )` convention |
42
+ | Date | `yyyy-mm-dd` | Sortable, unambiguous; store a real date, not text |
43
+ | Text passthrough | `@` | For codes and account numbers with leading zeros |
44
+
45
+ Do not use `0,0%` or `#.##0`. They are not locale-independent and render
46
+ differently on an engine that reads `,` as a decimal separator.
47
+
48
+ ## Signs, units, and scale
49
+
50
+ - State the unit once, in the header or title (`Revenue (USD)`), and keep the
51
+ stored value in that unit. A column that stores thousands but is headed
52
+ "USD" is a defect waiting to be discovered.
53
+ - If the presentation needs thousands (`$K`) while the data is in units,
54
+ format does not divide — either store thousands and say so, or divide with a
55
+ formula in a helper column. Never scale twice.
56
+ - Percentages: store `0.064`, never `6.4`. A stored `6.4` formatted `0.0%`
57
+ reads `640.0%`.
58
+
59
+ ## Widths and `###`
60
+
61
+ `###` means the column is narrower than the formatted number. Widen that
62
+ column (`ws.column_dimensions["B"].width = 14`) or shrink the font for the block.
63
+ Never shorten the number, switch to text, or reduce decimals just to fit — and do
64
+ not autofit every column in the sheet to fix one.
65
+
66
+ ## Dates
67
+
68
+ Write a real date (`datetime.date(2026, 1, 31)`) and set `yyyy-mm-dd`. Storing
69
+ an ISO string sorts alphabetically and calculates as text: `EDATE`, `SUMIFS` on
70
+ dates, and `YEAR()` all fail silently or with `#VALUE!`. A date typed as a number
71
+ of days since 1899-12-30 is a serial — fine for formulas, unreadable for people,
72
+ so format it.
@@ -0,0 +1,43 @@
1
+ # Sparklines and in-cell bars
2
+
3
+ openpyxl writes no sparkline object (and neither does the helper CLI), so a
4
+ "sparkline" here is one of two things: a formula that draws a bar inside the
5
+ cell, or a tiny native chart. Pick by what the user needs to read.
6
+
7
+ ```python
8
+ import sys
9
+ from openpyxl import load_workbook
10
+ from openpyxl.styles import Alignment, Font
11
+
12
+ wb = load_workbook(sys.argv[1])
13
+ ws = wb["Trend"]
14
+
15
+ for col in range(2, 14): # B..M
16
+ letter = ws.cell(1, col).column_letter
17
+ cell = ws.cell(4, col, f'=IF({letter}3="","",REPT("█",MAX(1,ROUND({letter}3/MAX($B$3:$M$3)*7,0))))')
18
+ cell.font = Font(size=9, color="FF2E7D32")
19
+ cell.alignment = Alignment(horizontal="left", shrink_to_fit=True)
20
+ ws.row_dimensions[4].height = 14
21
+ wb.save(sys.argv[2])
22
+ ```
23
+
24
+ - `REPT` needs a repeat count that fits the column. A 9-character column shows
25
+ about 7 blocks at 9pt; anything longer spills into the neighbouring cell and
26
+ the bars look like one continuous smear. Scale to the column width, not to 22.
27
+ - Keep the count formula-driven (`value / MAX(values) * width`), so a changed
28
+ number moves the bar.
29
+ - The block character is text. Sorting the column sorts blocks, and a reader who
30
+ copies the cell gets `████`, not a number — never put these in the column the
31
+ numbers should be read from.
32
+ - Guard blanks with `IF(cell="","",…)`, otherwise a blank reads as zero and every
33
+ missing month draws a minimum bar.
34
+ - A native in-cell chart is not the same as a chart object: if the user needs a
35
+ hover tooltip, axis, or legend, build a real chart with `recipes/charts.md`
36
+ instead of stretching a formula.
37
+
38
+ ## When a real chart is the right answer
39
+
40
+ A sparkline is context for one number, in a dense table. If the sheet has space
41
+ and the point is the trend itself, a small `LineChart` (height 4, width 9, no
42
+ legend, no gridlines) reads better and stays editable. Two or three word-sized
43
+ charts beat a wall of block characters.
@@ -0,0 +1,63 @@
1
+ # Chart choice and audit
2
+
3
+ A chart earns its place when it makes a comparison, a trend, a distribution, or a
4
+ relationship easier to see than a table would. If the reader only needs the
5
+ figures, a compact table or a KPI tile is better — and cheaper to check.
6
+
7
+ | Question | Default | Notes |
8
+ | --- | --- | --- |
9
+ | Compare and rank categories | Column or bar, sorted by value | Horizontal bar for long labels |
10
+ | Change over time | Line, chronological left to right | Area only when the volume under the line means something |
11
+ | Distribution of values | Histogram (formula bins + bar) | Box-and-whisker when median and spread matter and the engine supports it |
12
+ | Relationship between two numbers | Scatter | Never a line chart over unordered pairs |
13
+ | Share of a whole | Sorted bar, or a table | Pie/doughnut only for two or three slices, rough share |
14
+ | Exact values | Table | A chart cannot carry precision |
15
+ | One metric with context | KPI tile plus a small line | Sparkline-style bars only if the sheet is dense |
16
+
17
+ ## Honesty
18
+
19
+ - Series must read from cells, not from typed arrays, and derived values must be
20
+ formulas. A chart of pasted numbers drifts the moment the table updates.
21
+ - Include zero on bar, column, and area axes. Narrowing a line or scatter axis is
22
+ acceptable when the limits are stated; a truncated bar axis turns a 5% change
23
+ into a dramatic cliff.
24
+ - No 3-D, no shadows, no gradients. They occlude data and date the workbook.
25
+ - One period, one baseline, one unit across comparable charts. Disclose omitted
26
+ periods and missing values; do not let a blank read as zero.
27
+ - Sort categories deliberately (by value, or by the natural order of time), and
28
+ keep the same order across charts that will be compared.
29
+
30
+ ## Formatting rules that catch reviewers' eyes
31
+
32
+ - Title states subject and unit: `Profit by category ($m)`, `Monthly revenue
33
+ (USD)`. No placeholder titles, no legend standing in for a title.
34
+ - Units in the title or on the axis: `($K)`, `($M)`, `(USD thousands)`. Never
35
+ `($000)`.
36
+ - Axis number formats are set on the chart. Source cell formats do not carry over,
37
+ and a `0.0%` column can still produce `0.06, 0.08` ticks.
38
+ - Few series: direct labels. More series: legend above the plot area, consistent
39
+ colours per series across the workbook.
40
+ - Single series: no legend, one colour. A reference or target line is neutral or
41
+ dashed so it does not read as another data series.
42
+ - Selective data labels only — the last point, the maximum, the one the user asked
43
+ about. Ten labels on a twelve-point line is a table with extra steps.
44
+ - Line strokes are set explicitly; a fill alone may not colour an exported line.
45
+
46
+ ## Placement
47
+
48
+ Charts sit next to the data they describe, aligned to a column edge, over empty
49
+ cells, and never over the data, the controls, or a note. Size them to their
50
+ content; a chart stretched across the page with two bars in it looks unfinished.
51
+
52
+ ## Verifying a chart
53
+
54
+ 1. Read the object back: type, anchor, series count, category source.
55
+ 2. Render and look at the image: are there categories, is anything clipped, is the
56
+ legend empty, do the bars reach the values in the table?
57
+ 3. Compare the visual against the sheet's numbers at one or two points.
58
+ 4. Re-open in the app the user will use if the colours matter — an authoring
59
+ preview is not proof that a filled series exports as a visible line.
60
+
61
+ If the chart cannot be delivered as asked (the engine has no box plot, the data
62
+ cannot be rearranged), say so and use the closest clear alternative. Never claim a
63
+ requirement was met by a chart that does not show it.
@@ -0,0 +1,40 @@
1
+ # Financial models
2
+
3
+ Applies to forecasts, budgets, cash-flow plans, and valuation work in any
4
+ industry.
5
+
6
+ - Periods run left to right with the scale in the header (Actual, Forecast,
7
+ Budget). Do not mix a monthly column with a quarterly total in the same row
8
+ without a labelled total column at the edge.
9
+ - Drivers first, results second: volume × price, headcount × cost, days × rate.
10
+ A revenue row that jumps straight to a number cannot be reviewed.
11
+ - Units are stated once and kept: thousands vs millions in the same block is the
12
+ classic reconciliation failure. Put the unit in the title, not in a footnote.
13
+ - Three statements or none: if the workbook includes a balance sheet and a cash
14
+ flow, the balance sheet must balance and closing cash must equal the cash-flow
15
+ ending balance. Add those as checks, not as comments.
16
+ - Working capital moves: receivables from revenue × DSO, payables from cost × DPO,
17
+ inventory from COGS × days. Hard-coded changes in working capital break when the
18
+ drivers move.
19
+ - Scenarios: one selector, one set of case inputs per driver, and an explicit
20
+ `Case Selected:` on each output. A workbook with two switch cells reports two
21
+ answers.
22
+ - Never hard-code a total that already exists as a sum, and never sum a column that
23
+ already contains a total (double counting is silent).
24
+ - Sensitivity: one or two variables, closed-form or a small grid with the base case
25
+ marked. A native What-If Data Table is a different object with its own limits —
26
+ only build one when the user asks for it and the engine supports it.
27
+
28
+ ## Checks a finance reader looks for first
29
+
30
+ | Check | Expected |
31
+ | --- | --- |
32
+ | Balance sheet balances | Assets − Liabilities − Equity = 0 |
33
+ | Cash ties | Closing cash on the balance sheet = cash-flow ending balance |
34
+ | Sum of segments | Segment revenue = consolidated revenue |
35
+ | Period total | Sum of monthly columns = annual total column |
36
+ | Case consistency | Every tab shows the same selected case |
37
+ | No hard-coded totals | Total cells are formulas |
38
+
39
+ Put them on a check tab, terminal and read-only, and make a failing check visually
40
+ obvious without painting passing checks green.
@@ -0,0 +1,22 @@
1
+ # Healthcare operations
2
+
3
+ Workbooks here usually track volume, capacity, cost, and quality — not patients.
4
+ Treat patient-level data as sensitive: do not paste identifiers into a workbook
5
+ unless the user asks for it, do not copy rows from files you were shown into new
6
+ sheets, and prefer aggregates.
7
+
8
+ - Name the denominator in every rate: per patient day, per discharge, per 1,000
9
+ members, per available bed. A percentage without its base is not a metric.
10
+ - Volume and capacity are different columns. Utilization is
11
+ `used ÷ available`, and both sides need the same period and unit (bed-days, not
12
+ beds).
13
+ - Payer mix, service line, and site of care are separate dimensions. One table with
14
+ a column per dimension beats four tables that must be read together.
15
+ - Quality and safety metrics keep their own definitions; do not average a rate
16
+ across sites without stating whether it is volume-weighted.
17
+ - Claims and remittance data arrive with gaps. Show missing as blank or `n.a.`,
18
+ never as zero, and state the coverage period.
19
+ - Do not invent clinical thresholds, targets, or compliance claims. Use the
20
+ targets the source file provides, and cite the sheet and cell.
21
+ - Money and counts follow the finance conventions: units stated once, totals as
22
+ formulas, checks that detail rows reconcile to the summary.
@@ -0,0 +1,28 @@
1
+ # Marketing and advertising
2
+
3
+ Campaign work is a funnel plus a spend table, and both are easy to make look
4
+ better than they are.
5
+
6
+ - One row per campaign, channel, or creative — with that grain named in the header.
7
+ Mixing a channel total into a campaign table double counts.
8
+ - The funnel reads spend → impressions → clicks → conversions → revenue, in that
9
+ order, with rates derived by formula: `CTR = clicks / impressions`,
10
+ `CVR = conversions / clicks`, `CPA = spend / conversions`,
11
+ `ROAS = revenue / spend`.
12
+ - Never compute a ratio from cells that are not the same period and the same
13
+ population. A monthly CTR built from a quarterly spend and a weekly click count
14
+ is a number nobody can defend.
15
+ - Attribution is a claim: say which model the numbers use (last click, platform
16
+ reported, blended) and keep it constant across the table. Do not add platform
17
+ conversions together and call it incremental revenue.
18
+ - Missing spend or reach for a period is blank, not zero — a zero-spend month
19
+ shows an infinite CPA and a fake collapse in the trend.
20
+ - Benchmarks and targets belong in a labelled row or column with their source, not
21
+ in a note the reader has to find.
22
+
23
+ ## Charts for marketing
24
+
25
+ Spend and return on the same chart only when both are in the same unit; otherwise
26
+ two aligned charts. Sort channel bars by spend or by return — not alphabetically —
27
+ and keep the same order across charts. A conversion trend belongs on a line chart
28
+ with the campaign launch dates marked, not on a pie.
@@ -0,0 +1,22 @@
1
+ # Scientific and research data
2
+
3
+ The spreadsheet is usually a working copy of measurements, not the record of
4
+ truth. Keep it that way: never edit raw measurements in place, and never let a
5
+ formatted view replace the values behind it.
6
+
7
+ - Raw data on its own sheet, untouched, in columns whose headers carry the unit
8
+ (`time (s)`, `temp (C)`, `conc (mg/L)`). Derived columns live on a build sheet
9
+ and reference the raw cells.
10
+ - One row per observation, one column per variable. Do not encode a variable in a
11
+ colour, a note, or a header font.
12
+ - Replicates are separate rows with a replicate id and a group id; the mean and
13
+ standard deviation are formulas over those rows, never typed.
14
+ - Keep the precision the instrument gives you and let the format hide extra
15
+ decimals. Rounding stored values destroys the ability to recompute a mean.
16
+ - Uncertainty, detection limits, and excluded points are stated in labelled cells.
17
+ An excluded point stays in the data with a reason column — deleting it hides the
18
+ decision.
19
+ - Smoothing, binning, and unit conversion are formulas with visible inputs, or they
20
+ do not exist. A trend line drawn over noisy points is not an analysis.
21
+ - Distribution, trend, and relationship: choose the chart by the question, and
22
+ check that the axis ranges and bins are stated (`references/charts.md`).
@@ -0,0 +1,78 @@
1
+ # Formula patterns
2
+
3
+ Spreadsheet formulas are read by people who did not write them and recalculated
4
+ by engines that disagree at the edges. Prefer the direct, boring form.
5
+
6
+ ## Anchoring
7
+
8
+ | Reference | Moves when filled | Use for |
9
+ | --- | --- | --- |
10
+ | `C8` | both ways | A grid where every cell looks at its own row and column |
11
+ | `C$4` | down only | A header row of periods |
12
+ | `$A8` | right only | A column of item labels |
13
+ | `$B$3` | never | One rate, a case selector, a fixed constant |
14
+
15
+ `=SUMIFS(Amount,Month,C$4,Item,$A8)` reads the period from the header and the item
16
+ from the label. `=C8*$B$3` multiplies by one fixed rate; `=C8*C$3` multiplies by
17
+ the rate in that column. Getting this backwards is the most common silent error in
18
+ a copied model — check one filled row and one filled column by hand.
19
+
20
+ ## Aggregation
21
+
22
+ - `SUM` for a column, `SUMIFS` for filtered totals, `COUNTIFS` for counts,
23
+ `AVERAGEIFS` for averages.
24
+ - Use `SUMIFS`/`COUNTIFS` even with one criterion; they read the same and extend
25
+ cleanly.
26
+ - `SUMPRODUCT(qty_range, rate_range)` for paired multiplication; it does not
27
+ collapse to a single cell the way array arithmetic would.
28
+ - `PRODUCT` for multiplied factors, but it skips blanks: a missing input silently
29
+ removes a factor. Check the inputs before trusting the result.
30
+ - `INDEX`/`MATCH`, `VLOOKUP(...,FALSE)`, or `XLOOKUP` — all fine. Keep the exact
31
+ match flag explicit. A matched key is not a filled value: check the returned
32
+ cell for blank as well as for the key existing.
33
+ - Long category lists belong in a mapping table with a lookup, not in a nested
34
+ `IF` chain.
35
+
36
+ ## Dates and periods
37
+
38
+ - Use the date the source gives; `TODAY()` only when the workbook should track
39
+ today, and label it when it does.
40
+ - Store real dates with a date number format. Text dates do not calculate.
41
+ - Derive period filters from one header row so a new month is a copy, not an edit:
42
+ `=SUMIFS(Amount,Date,">="&C$4,Date,"<"&EDATE(C$4,1),Item,$A8)`.
43
+ - Week starting on the first business day (Monday unless the business says
44
+ otherwise); say which in the header.
45
+ - Time scales run left to right coarse to fine: Annual, Quarterly, Monthly,
46
+ Weekly — with narrow unfilled spacer columns, not merged headers over hidden
47
+ helpers.
48
+
49
+ ## Missing data
50
+
51
+ - A real zero is zero. A missing value is blank, or honestly `n.a.`/`N/A` where
52
+ the reader expects text. Never substitute one for the other to make a chart look
53
+ complete.
54
+ - `IFERROR` only around a failure you understand, never wrapped around a whole
55
+ block to hide it. `IFERROR(x,0)` turns a broken reference into a plausible zero.
56
+ - Guards must not swallow the failure they guard against: `=IF(C8=0,"n.a.",B8/C8)`
57
+ is a clear answer, `=IFERROR(B8/C8,0)` is a lie.
58
+ - Missing keys in a lookup should show as missing (`=IFNA(...,"not found")`), not
59
+ as zero.
60
+
61
+ ## Things not to write
62
+
63
+ - `LET`, dynamic arrays/spills, `MAP`, `REDUCE`, `LAMBDA`: the engines that read
64
+ these files implement them unevenly, and openpyxl stores text without
65
+ validating it.
66
+ - `INDIRECT` for everything. It breaks traceability and does not update when a
67
+ sheet is renamed. Keep it only where the existing file already relies on it.
68
+ - Volatile functions (`TODAY`, `NOW`, `RAND`, `OFFSET`) in a calculation grid:
69
+ every keystroke recalculates them, and the value changes when the file is
70
+ reopened.
71
+
72
+ ## Circular references
73
+
74
+ Avoid unintended cycles; Excel flags them for a reason. If a cycle is deliberate
75
+ (interest on average balance, iterative allocation), document it in the sheet,
76
+ confirm iteration is enabled, and verify the result converges to the expected
77
+ number. A model that only "works" because the last recalc happened to stop at
78
+ iteration 100 is not verified.
@@ -0,0 +1,69 @@
1
+ # Style guidelines
2
+
3
+ Precedence: the user's request, then the workbook or template being edited, then a
4
+ domain convention, then these defaults. Defaults apply to new sheets, or to
5
+ restyling the user asked for — never to a sheet you were not asked to touch.
6
+
7
+ ## Page furniture
8
+
9
+ - One blank row at the top, then a short unfilled title, left aligned, in a size
10
+ clearly larger than the body. No full-width filled banner.
11
+ - A thin rule under the title separates it from the table; nothing else.
12
+ - Notes sit in ordinary cells to the right of the data, at least one blank column
13
+ away from the last header fill, with wrap off.
14
+ - No footer blocks, no "Prepared by" furniture, no date stamps nobody asked for.
15
+ - Gridlines off on new sheets. Do not change gridlines on a sheet you are editing
16
+ for another reason.
17
+
18
+ ## Structure
19
+
20
+ - Freeze the header row of any table that scrolls, plus the identifying column(s)
21
+ when horizontal scrolling hides them. Freeze the smallest useful area and never
22
+ move content to make freezing convenient.
23
+ - Header row: bold, centred horizontally and vertically, dark fill with white text
24
+ or a light grey band, thin white separators between header cells.
25
+ - Section bands: one continuous fill with a single outline around the block; no
26
+ internal borders inside the band, no full-cell grid.
27
+ - Spacer columns are genuinely empty (no values, no borders), not narrow columns
28
+ of spaces.
29
+ - Keep working titles, headers, and input areas unmerged. Center Across Selection
30
+ gives the look of a merge without breaking sorting, filtering, and references.
31
+
32
+ ## Numbers, inputs, calcs
33
+
34
+ - Typed values with explicit number formats — see `recipes/number-formats.md`.
35
+ - Left-align text, right-align numbers, centre headers, middle-align vertically;
36
+ top-align long wrapped descriptions.
37
+ - Distinguish inputs from calculations consistently: inputs get a light fill or a
38
+ colour cue the sheet already uses, calculations stay white. Yellow/amber marks an
39
+ input that still needs updating; keep that cue separate from calculated warnings.
40
+ - When cases exist, show `Case Selected:` plus the linked value near the top of
41
+ each output and build tab, in a restrained dashed outline, and highlight the
42
+ active row in the driver table.
43
+ - Technical helper rows above a model are italic and grouped/collapsed.
44
+
45
+ ## Conditional formatting as a cue
46
+
47
+ - Bound the rule to the data, and use it for status, risk, variance, and
48
+ exceptions — not decoration.
49
+ - Critical errors: light red fill, bold dark red text. Valid checks stay neutral;
50
+ no static green "PASS" paint over a range.
51
+ - Reuse the sheet's existing rule style before inventing a new colour. Two similar
52
+ reds in one workbook look like two different severities.
53
+
54
+ ## Typography
55
+
56
+ - One family across the workbook: Helvetica Neue, Helvetica, Arial, then Aptos.
57
+ - Two sizes at most (three counting the title). Largest size no more than six
58
+ points above body. A 10pt body with an 11pt header and a 14pt title is enough.
59
+ - Bold for headers and totals, italic for short context notes — never both on
60
+ the same line, never on every row.
61
+ - Tighten row heights after content fits; do not shrink the font to win an
62
+ argument with a column width.
63
+
64
+ ## Tab colours
65
+
66
+ Use colours only to help navigation: a darker brand tone on output tabs, one
67
+ level lighter on assumptions, a distinct muted tone on a ReadMe, a divider tab
68
+ coloured while its children stay plain. Financial Actuals tabs are traditionally
69
+ light tan. Never recolour tabs during an unrelated edit.
@@ -0,0 +1,60 @@
1
+ # Workbook structure
2
+
3
+ Tab roles describe what a sheet *does*, not what tabs a workbook must have. A
4
+ simple workbook can carry several roles as labelled blocks on one sheet; a large
5
+ one separates them because the readers, the refresh cadence, or the audit trail
6
+ differ.
7
+
8
+ | Role | Holds | Feeds |
9
+ | --- | --- | --- |
10
+ | Input / Sources | Pasted exports, system extracts, unchanged source tables | Builds |
11
+ | Assumptions | Rates, drivers, the case selector, global inputs | Builds |
12
+ | Build | The visible calculation steps between inputs and outputs | Outputs |
13
+ | Output / Summary | Consolidated results, the numbers a reader came for | Nothing downstream |
14
+ | Check / Audit | Diagnostics that compare what should reconcile | Nothing — terminal and read-only |
15
+ | Cover / ReadMe | A short front page, or an explanation of method and joins | Nothing |
16
+
17
+ Rules that keep a workbook readable a year later:
18
+
19
+ - Inputs flow forward: source → build → output. A source tab never reads a
20
+ downstream result back in; when it seems to, the value belongs on a build tab.
21
+ - Builds show the steps: quantity × rate, capacity used ÷ capacity available,
22
+ opening + movements = closing. One dense formula that jumps from input to final
23
+ number cannot be reviewed and will be wrong the first time an input changes.
24
+ - Outputs consolidate and roll up. They do not repeat a calculation that already
25
+ exists on a build tab; they reference it.
26
+ - Checks are terminal. Nothing depends on a check tab, and a passing check stays
27
+ neutral — a wall of green PASS fills trains the reader to ignore the one red
28
+ cell that matters.
29
+ - Combine roles when the workbook is small. Three assumption cells do not need a
30
+ Settings tab; a short labelled block beside the calculation is easier to audit.
31
+ - Add a tab when the reader differs, the data grain differs, the refresh
32
+ boundary differs, or the user asks. Not because a template had one.
33
+
34
+ ## Physical order
35
+
36
+ For multi-tab workbooks, left to right: outputs, the assumption control panel
37
+ (usually just after the primary output so a reader sees the switch), builds,
38
+ then inputs and sources, with a divider tab such as `Internal >>` guarding the
39
+ tail. The reader gets the answer first; the machinery lives to the right.
40
+
41
+ Separate schedules when inputs, grain, or owners differ (revenue vs cost bases,
42
+ monthly vs weekly). A single mega-sheet that mixes both grains is where
43
+ double-counting starts.
44
+
45
+ ## Names
46
+
47
+ Short, descriptive, and about the content: `Pipeline`, `Revenue build`,
48
+ `Monthly forecast`. Not `Sheet3`, not `Data2`, not a sentence. A forecast review
49
+ tab is `Forecast review`, `Forecast variance`, or `Sensitivity`; never `Movement`,
50
+ which says nothing about what moved.
51
+
52
+ Divider tabs are labels, not data: `Inputs >>` with its children uncoloured reads
53
+ as a section marker; a coloured tab on every sheet reads as noise.
54
+
55
+ ## The case selector
56
+
57
+ When a workbook has cases, there is exactly one editable selector, on the cover
58
+ or assumptions tab, with its current value shown as `Case Selected:` near the top
59
+ of every output and build tab. Two selectors, or a selector plus hard-coded
60
+ per-tab values, is how a model ends up reporting two different cases.
@@ -0,0 +1,36 @@
1
+ # Google Sheets
2
+
3
+ This runtime works on local files with openpyxl. There are no Google credentials,
4
+ no Drive API, and no live link to a browser sheet, so "update my Google Sheet"
5
+ cannot be done directly.
6
+
7
+ ## What to do instead
8
+
9
+ 1. Say plainly what is possible: the work is done on a local `.xlsx` file the user
10
+ opens in Sheets, or the user exports the sheet and shares the file.
11
+ 2. Ask for an export when the source lives in Sheets: `File > Download >
12
+ Microsoft Excel (.xlsx)`. A single-sheet CSV loses formulas and formats, so ask
13
+ for `.xlsx` unless the sheet is truly one flat table.
14
+ 3. Do the work on the exported file with this skill, then hand back the `.xlsx`.
15
+ The user re-uploads or imports it. State that formulas recalculate when Sheets
16
+ opens the file.
17
+ 4. Do not promise a round trip that keeps Sheets-specific features. Named
18
+ functions, Apps Script, and connected sheets do not survive export.
19
+
20
+ ## When writing formulas for a file that will go back to Sheets
21
+
22
+ - Keep to functions both engines share: `SUMIFS`, `INDEX/MATCH`, `XLOOKUP`,
23
+ `IFERROR`, `EDATE`, `TEXT`.
24
+ - Newer functions that Excel stores with an `_xlfn.` prefix are a common source of
25
+ `#NAME?` after a round trip. If a function is genuinely new, say so.
26
+ - Array formulas and dynamic spills behave differently in the two engines. Avoid
27
+ them in shared files.
28
+ - Number formats, conditional-formatting rules, and charts mostly survive an
29
+ export; images and pivot tables often do not. Check them after the round trip
30
+ with `container_tools/inspect_workbook.py`.
31
+
32
+ ## Links
33
+
34
+ A sheet shared by URL is not a workbook on this machine. Do not fetch a Google
35
+ Sheets URL and pretend it is a file: without a published CSV or an export, there
36
+ is nothing to read.
@@ -0,0 +1,37 @@
1
+ # Answer questions about an existing workbook
2
+
3
+ This is a read-only job. Do not "improve" the file, do not recalculate it in
4
+ place, and do not save a copy unless the user asked for one.
5
+
6
+ ## Procedure
7
+
8
+ 1. `"<python>" "<cli>" xlsx read --input "<book.xlsx>" --json-out "<dump>.json"` —
9
+ sheet names, used ranges, and cell contents.
10
+ 2. Read **values**, not formulas, when the question is about numbers:
11
+ `xlsx read --data-only` returns cached results. If the cache is empty (the
12
+ file was last written by a script), say so and quote the formula instead of a
13
+ number, or recalculate into a copy first (`tasks/recalc-and-check.md`).
14
+ 3. `container_tools/inspect_workbook.py --input "<book.xlsx>" --sample 8` when the
15
+ question touches structure: which cells are formulas, merged ranges, totals
16
+ rows, conditional formats, defined names, hidden rows, extra sheets.
17
+ 4. Answer with the cells you used: "`Pipeline!B4:B13`, the Amount column, sums to
18
+ `812,000`" beats "the pipeline is about 800k". Name the sheet, the range, and
19
+ the figure.
20
+
21
+ ## Judgement calls
22
+
23
+ - A number that disagrees with the user's expectation is usually a real answer,
24
+ not an error to fix. Explain the calculation and stop.
25
+ - Pre-existing `#REF!`/`#DIV/0!` cells are part of the answer. Point at them, say
26
+ what breaks, and wait for instructions.
27
+ - Two tabs with the same title and different numbers: report both and ask which
28
+ one the user means before averaging or picking.
29
+ - Empty cells are not zeros. "Not reported" and "zero" are different answers, and
30
+ a missing month in a total usually means a filter, not a zero.
31
+ - If the workbook has a case selector, check it. The answer depends on it.
32
+
33
+ ## Output shape
34
+
35
+ Short answer first (one line with the figure), then the cells behind it, then any
36
+ caveat about missing data or stale cache. No file changes, no new workbook, no
37
+ chart unless asked.
@@ -0,0 +1,51 @@
1
+ # Resolve an `office-annotations` region to cells
2
+
3
+ The user drags a box on the rendered workbook and types a requirement. The
4
+ payload for spreadsheets is:
5
+
6
+ ```json
7
+ {
8
+ "target": "book.xlsx",
9
+ "fileName": "book.xlsx",
10
+ "text": "",
11
+ "requirement": "make this column currency",
12
+ "region": { "left": 0.42, "top": 0.51, "width": 0.18, "height": 0.30 },
13
+ "imageName": "book-page-1.png",
14
+ "unmapped": true
15
+ }
16
+ ```
17
+
18
+ There is no `page`, no `sheet`, and no `range`: the preview client cannot turn a
19
+ pixel rectangle into a cell address and will not fake one. Read it as "look at
20
+ this area of this sheet and do this", not as a cell reference.
21
+
22
+ ## Procedure
23
+
24
+ 1. `read_image` the attachment named `imageName`. It is the screenshot of the
25
+ area the user marked: read the visible headers, row labels, and the numbers
26
+ around the box.
27
+ 2. `"<python>" "<cli>" xlsx read --input "<book.xlsx>" --json-out "<dump>.json"`,
28
+ then match the visible values to sheets and A1 addresses. Values repeat across
29
+ sheets more often than you expect — match on a combination (a row label plus
30
+ two numbers), not on one number.
31
+ 3. When the numbers are ambiguous, `container_tools/inspect_workbook.py` adds
32
+ formulas, merges, tables, and names; formats often disambiguate ("the only
33
+ column with a `%` format").
34
+ 4. State what you resolved, in the user's terms: "`Pipeline!D2:D13`, the Amount
35
+ column — 12 rows of whole dollars". Ask only if two sheets or two columns
36
+ match equally well.
37
+ 5. Apply the smallest change that satisfies `requirement` with the mutation tools
38
+ (`tasks/edit-workbook.md`), re-render, and confirm the region now looks right.
39
+
40
+ ## What not to do
41
+
42
+ - Do not compute a cell from the rectangle's coordinates. Header width, frozen
43
+ panes, hidden columns, and zoom all move cells under the same pixel.
44
+ - Do not edit the first sheet because it was the default. The visible page may be
45
+ the fourth sheet.
46
+ - Do not re-style the whole sheet to make the marked area consistent. Match the
47
+ surrounding convention and change the cells the requirement names.
48
+ - If the region covers a chart or a picture, the change is to the object, not the
49
+ cells behind it — check `charts`/`images` in the inspect output first.
50
+ - If nothing on the page carries the values in the screenshot, say which image
51
+ you looked at and what you could not locate. Do not guess a sheet.