dsh-office-kit 0.4.2 → 0.4.3

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 (113) hide show
  1. package/assets/skills/excel-live-control/SKILL.md +119 -152
  2. package/assets/skills/excel-live-control/domain_guidance/financial_models.md +42 -58
  3. package/assets/skills/excel-live-control/domain_guidance/healthcare.md +36 -42
  4. package/assets/skills/excel-live-control/domain_guidance/marketing_advertising.md +33 -34
  5. package/assets/skills/excel-live-control/domain_guidance/scientific_research.md +34 -36
  6. package/assets/skills/excel-live-control/references/office-automation.md +127 -153
  7. package/assets/skills/excel-live-control/references/read_only_qna.md +40 -45
  8. package/assets/skills/excel-live-control/style_guidelines.md +56 -64
  9. package/assets/skills/excel-live-control/workflows/create_workflows.md +40 -47
  10. package/assets/skills/excel-live-control/workflows/edit_workflows.md +37 -45
  11. package/assets/skills/office-documents/SKILL.md +24 -60
  12. package/assets/skills/office-documents/examples/end_to_end_smoke_test.md +5 -5
  13. package/assets/skills/office-documents/ooxml/comments.md +8 -8
  14. package/assets/skills/office-documents/ooxml/hyperlinks_and_fields.md +10 -10
  15. package/assets/skills/office-documents/ooxml/rels_and_content_types.md +7 -7
  16. package/assets/skills/office-documents/ooxml/tracked_changes.md +12 -12
  17. package/assets/skills/office-documents/references/docx-structure.md +11 -13
  18. package/assets/skills/office-documents/references/template-elicitation.md +11 -13
  19. package/assets/skills/office-documents/references/writing-quality.md +20 -20
  20. package/assets/skills/office-documents/tasks/accessibility_a11y.md +16 -22
  21. package/assets/skills/office-documents/tasks/captions_crossrefs.md +23 -26
  22. package/assets/skills/office-documents/tasks/clean_tracked_changes.md +17 -22
  23. package/assets/skills/office-documents/tasks/comments_manage.md +9 -11
  24. package/assets/skills/office-documents/tasks/compare_diff.md +10 -16
  25. package/assets/skills/office-documents/tasks/create_edit.md +14 -16
  26. package/assets/skills/office-documents/tasks/equations.md +17 -28
  27. package/assets/skills/office-documents/tasks/fields_update.md +14 -16
  28. package/assets/skills/office-documents/tasks/fixtures_edge_cases.md +19 -33
  29. package/assets/skills/office-documents/tasks/footnotes_endnotes.md +12 -14
  30. package/assets/skills/office-documents/tasks/forms_content_controls.md +16 -20
  31. package/assets/skills/office-documents/tasks/headings_numbering.md +17 -22
  32. package/assets/skills/office-documents/tasks/images_figures.md +15 -25
  33. package/assets/skills/office-documents/tasks/multi_doc_merge.md +13 -23
  34. package/assets/skills/office-documents/tasks/navigation_internal_links.md +18 -21
  35. package/assets/skills/office-documents/tasks/privacy_scrub_metadata.md +16 -22
  36. package/assets/skills/office-documents/tasks/protection_restrict_editing.md +19 -21
  37. package/assets/skills/office-documents/tasks/read_review.md +9 -20
  38. package/assets/skills/office-documents/tasks/redaction_anonymization.md +17 -18
  39. package/assets/skills/office-documents/tasks/sections_layout.md +16 -23
  40. package/assets/skills/office-documents/tasks/style_lint_normalize.md +18 -22
  41. package/assets/skills/office-documents/tasks/tables_spreadsheets.md +21 -31
  42. package/assets/skills/office-documents/tasks/templates_style_packs.md +17 -21
  43. package/assets/skills/office-documents/tasks/toc_workflow.md +10 -14
  44. package/assets/skills/office-documents/tasks/verify_render.md +11 -23
  45. package/assets/skills/office-documents/tasks/watermarks_background.md +11 -15
  46. package/assets/skills/office-documents/troubleshooting/libreoffice_headless.md +6 -6
  47. package/assets/skills/office-documents/troubleshooting/run_splitting.md +12 -12
  48. package/assets/skills/office-pdf/SKILL.md +36 -51
  49. package/assets/skills/office-pdf/references/form-fields.md +20 -20
  50. package/assets/skills/office-pdf/references/geometry.md +10 -10
  51. package/assets/skills/office-pdf/references/layout-quality.md +13 -13
  52. package/assets/skills/office-pdf/references/limits.md +12 -56
  53. package/assets/skills/office-pdf/tasks/annotations.md +5 -5
  54. package/assets/skills/office-pdf/tasks/create.md +6 -9
  55. package/assets/skills/office-pdf/tasks/edit-pages.md +14 -14
  56. package/assets/skills/office-pdf/tasks/forms.md +15 -15
  57. package/assets/skills/office-pdf/tasks/read.md +9 -9
  58. package/assets/skills/office-pdf/tasks/redact.md +18 -18
  59. package/assets/skills/office-pdf/tasks/stamp.md +10 -10
  60. package/assets/skills/office-presentations/SKILL.md +71 -91
  61. package/assets/skills/office-presentations/container_tools/render_slides.py +1 -1
  62. package/assets/skills/office-presentations/domain_guidance/finance.md +16 -16
  63. package/assets/skills/office-presentations/recipes/README.md +24 -35
  64. package/assets/skills/office-presentations/recipes/autolayout.md +12 -13
  65. package/assets/skills/office-presentations/recipes/connectors.md +11 -12
  66. package/assets/skills/office-presentations/recipes/images.md +11 -12
  67. package/assets/skills/office-presentations/recipes/master-and-layouts.md +18 -18
  68. package/assets/skills/office-presentations/recipes/native-charts.md +8 -8
  69. package/assets/skills/office-presentations/recipes/rich-text-runs.md +13 -13
  70. package/assets/skills/office-presentations/recipes/speaker-notes.md +7 -7
  71. package/assets/skills/office-presentations/recipes/tables.md +14 -14
  72. package/assets/skills/office-presentations/recipes/textbox-at-coordinate.md +9 -9
  73. package/assets/skills/office-presentations/recipes/theme-tokens.md +7 -8
  74. package/assets/skills/office-presentations/references/authoring-workflow.md +38 -43
  75. package/assets/skills/office-presentations/references/finalization.md +10 -12
  76. package/assets/skills/office-presentations/references/native-bullets.md +14 -18
  77. package/assets/skills/office-presentations/references/native-evidence.md +18 -32
  78. package/assets/skills/office-presentations/references/style-guidelines.md +27 -32
  79. package/assets/skills/office-presentations/references/templates.md +31 -60
  80. package/assets/skills/office-presentations/routing/google_slides.md +4 -4
  81. package/assets/skills/office-presentations/tasks/apply-annotations.md +17 -17
  82. package/assets/skills/office-presentations/tasks/charts-tables-notes.md +20 -23
  83. package/assets/skills/office-presentations/tasks/create-deck.md +19 -19
  84. package/assets/skills/office-presentations/tasks/edit-deck.md +14 -16
  85. package/assets/skills/office-presentations/tasks/inspect-and-verify.md +31 -34
  86. package/assets/skills/office-presentations/tasks/template-following.md +23 -28
  87. package/assets/skills/office-spreadsheets/SKILL.md +32 -39
  88. package/assets/skills/office-spreadsheets/recipes/charts.md +8 -26
  89. package/assets/skills/office-spreadsheets/recipes/conditional-formats.md +9 -26
  90. package/assets/skills/office-spreadsheets/recipes/data-tables.md +7 -23
  91. package/assets/skills/office-spreadsheets/recipes/named-ranges.md +8 -21
  92. package/assets/skills/office-spreadsheets/recipes/number-formats.md +7 -23
  93. package/assets/skills/office-spreadsheets/recipes/sparklines.md +7 -20
  94. package/assets/skills/office-spreadsheets/references/charts.md +15 -34
  95. package/assets/skills/office-spreadsheets/references/domain/financial-models.md +10 -25
  96. package/assets/skills/office-spreadsheets/references/domain/healthcare.md +8 -19
  97. package/assets/skills/office-spreadsheets/references/domain/marketing.md +8 -22
  98. package/assets/skills/office-spreadsheets/references/domain/research.md +8 -19
  99. package/assets/skills/office-spreadsheets/references/formula-patterns.md +20 -50
  100. package/assets/skills/office-spreadsheets/references/style-guidelines.md +19 -44
  101. package/assets/skills/office-spreadsheets/references/workbook-structure.md +12 -35
  102. package/assets/skills/office-spreadsheets/routing/google-sheets.md +11 -26
  103. package/assets/skills/office-spreadsheets/tasks/analyze-workbook.md +10 -25
  104. package/assets/skills/office-spreadsheets/tasks/annotations.md +12 -30
  105. package/assets/skills/office-spreadsheets/tasks/create-workbook.md +5 -5
  106. package/assets/skills/office-spreadsheets/tasks/diff-workbooks.md +2 -2
  107. package/assets/skills/office-spreadsheets/tasks/edit-workbook.md +5 -5
  108. package/assets/skills/office-spreadsheets/tasks/inspect-workbook.md +9 -9
  109. package/assets/skills/office-spreadsheets/tasks/recalc-and-check.md +10 -30
  110. package/assets/skills/shared/annotation-recipes.md +98 -120
  111. package/assets/skills/shared/common-workflow.md +70 -80
  112. package/dist/index.js +1 -1
  113. package/package.json +1 -1
@@ -1,8 +1,6 @@
1
1
  # Native charts
2
2
 
3
- A native chart is an object bound to cell ranges: the numbers stay in cells, the
4
- chart reads them, and editing a cell moves the bar. That is the whole reason to
5
- add one instead of pasting an image.
3
+ A native chart is an object bound to cell ranges: the numbers stay in cells, the chart reads them, and editing a cell moves the bar. That is the whole reason to add one instead of pasting an image.
6
4
 
7
5
  ```python
8
6
  import sys
@@ -30,37 +28,21 @@ wb.save(sys.argv[2])
30
28
 
31
29
  ## Orientation is the bug people hit
32
30
 
33
- A one-row source is **one series of many points**, not many series of one point.
34
- `Reference` cannot express that; `add_data(..., from_rows=True)` can. Without it
35
- a monthly row becomes twelve invisible one-point series and the chart renders
36
- blank. The mirror case is a one-column source added with `from_rows=True`: you
37
- get one series per cell row, so only pass it when the data really is row-wise.
31
+ A one-row source is **one series of many points**, not many series of one point. `Reference` cannot express that; `add_data(..., from_rows=True)` can. Without it a monthly row becomes twelve invisible one-point series and the chart renders blank. The mirror case is a one-column source added with `from_rows=True`: you get one series per cell row, so pass it only when the data really is row-wise.
38
32
 
39
33
  ## What the helper CLI gives you
40
34
 
41
- `xlsx create` accepts `charts[]` with `type`, `title`, `anchor`,
42
- `height`/`width`, `data {min_col,min_row,max_col,max_row,titles_from_data}` and
43
- `categories`. `container_tools/add_chart.py` adds the same chart to an existing
44
- file and handles the row-oriented case. Neither sets axis number formats, series
45
- colours, legend position, data labels, or gap width — if the chart needs those,
46
- use a snippet like the one above.
35
+ `xlsx create` accepts `charts[]` with `type`, `title`, `anchor`, `height`/`width`, `data {min_col,min_row,max_col,max_row,titles_from_data}` and `categories`. `container_tools/add_chart.py` adds the same chart to an existing file and handles the row-oriented case. Neither sets axis number formats, series colours, legend position, data labels, or gap width — if the chart needs those, use a snippet like the one above.
47
36
 
48
37
  ## Checks before you present
49
38
 
50
39
  - Series count matches the intent (one line per series, not one per point).
51
40
  - Categories are labels, not values and not an empty axis with `1, 2, 3, …`.
52
- - Bar, column, and area axes start at zero; line and scatter may narrow the
53
- range, but the scale must be visible and stated.
54
- - No default legend on a single series; name each series in `titles_from_data`
55
- or the legend reads `Series 1`.
56
- - Axis number formats are set on the chart, not inherited from cells — a source
57
- column formatted as `0.0%` does not format the axis.
58
- - The chart sits near its data, over empty cells, and is not clipped by the
59
- print area when the sheet is printed.
41
+ - Bar, column, and area axes start at zero; line and scatter may narrow the range, but the scale must be visible and stated.
42
+ - No default legend on a single series; name each series in `titles_from_data` or the legend reads `Series 1`.
43
+ - Axis number formats are set on the chart, not inherited from cells — a source column formatted as `0.0%` does not format the axis.
44
+ - The chart sits near its data, over empty cells, and is not clipped by the print area when the sheet is printed.
60
45
 
61
46
  ## Editing a chart in an existing file
62
47
 
63
- Read the object first (`inspect_workbook.py` lists type, anchor, and series
64
- refs), change the smallest property, and re-render. Moving a chart a few cells is
65
- fine; rebuilding it with different styling when the user asked for one number to
66
- be added is not.
48
+ Read the object first (`inspect_workbook.py` lists type, anchor, and series refs), change the smallest property, and re-render. Moving a chart a few cells is fine; rebuilding it with different styling when the user asked for one number to be added is not.
@@ -1,9 +1,6 @@
1
1
  # Conditional formats
2
2
 
3
- Conditional formatting paints a cell from a rule, not from a value. The rule is
4
- stored separately from the cell format, so a cell can look red while its stored
5
- fill stays default — and a reader who copies the value out gets the number, not
6
- the colour.
3
+ Conditional formatting paints a cell from a rule, not from a value. The rule is stored separately from the cell format, so a cell can look red while its stored fill stays default — and a reader who copies the value out gets the number, not the colour.
7
4
 
8
5
  ```python
9
6
  import sys
@@ -39,31 +36,17 @@ ws.conditional_formatting.add(
39
36
  wb.save(sys.argv[2])
40
37
  ```
41
38
 
42
- - `ColorScaleRule`/`DataBarRule` take a range, not a cell; one rule covers the
43
- rectangle and scales against the values inside it.
44
- - `CellIsRule` compares one cell: `operator` in `lessThan`, `greaterThan`,
45
- `between`, `equal`, `notEqual`, …
46
- - `FormulaRule` evaluates a formula with the range's **top-left** cell, so write
47
- the reference for that cell (`$F2` with row relative) and Excel fills the rest.
48
- - A whole-column range (`D:D`) is cheap to write and slow to render; bound the
49
- range to the current data (`D2:D13`) and extend it when rows are added.
50
- - Rules stack in priority order; later rules win only when the earlier ones do
51
- not match. `stopIfTrue=True` ends the chain — use it when a second rule must
52
- not repaint an already-flagged cell.
53
- - Do not also set a static fill on the same cells: the static fill shows through
54
- where no rule matches and the sheet looks half-formatted.
39
+ - `ColorScaleRule`/`DataBarRule` take a range, not a cell; one rule covers the rectangle and scales against the values inside it.
40
+ - `CellIsRule` compares one cell: `operator` in `lessThan`, `greaterThan`, `between`, `equal`, `notEqual`, …
41
+ - `FormulaRule` evaluates a formula with the range's **top-left** cell, so write the reference for that cell (`$F2` with row relative) and Excel fills the rest.
42
+ - A whole-column range (`D:D`) is cheap to write and slow to render; bound the range to the current data (`D2:D13`) and extend it when rows are added.
43
+ - Rules stack in priority order; later rules win only when the earlier ones do not match. `stopIfTrue=True` ends the chain — use it when a second rule must not repaint an already-flagged cell.
44
+ - Do not also set a static fill on the same cells: the static fill shows through where no rule matches and the sheet looks half-formatted.
55
45
 
56
46
  ## Editing an existing file
57
47
 
58
- Replace a rule, do not add a near-duplicate: iterate
59
- `ws.conditional_formatting` and rebuild the collection for that range. Two rules
60
- covering `D2:D13` with slightly different thresholds give a result nobody can
61
- explain. Keep the rule count per sheet small — a dashboard with forty rules is a
62
- compatibility minefield in other engines.
48
+ Replace a rule; do not add a near-duplicate. Iterate `ws.conditional_formatting` and rebuild the collection for that range — two rules covering `D2:D13` with slightly different thresholds give a result nobody can explain. Keep the rule count per sheet small: a dashboard with forty rules is a compatibility minefield in other engines.
63
49
 
64
50
  ## Verify
65
51
 
66
- Read the file back with `container_tools/inspect_workbook.py`; it lists each
67
- range and rule type. Then render and look: a data bar that never appears is
68
- usually a range that does not overlap the data, and a rule that fires on the
69
- wrong cells is almost always an absolute/relative mistake in `FormulaRule`.
52
+ Read the file back with `container_tools/inspect_workbook.py`; it lists each range and rule type. Then render and look: a data bar that never appears is usually a range that does not overlap the data, and a rule that fires on the wrong cells is almost always an absolute/relative mistake in `FormulaRule`.
@@ -1,10 +1,6 @@
1
1
  # Data tables (Excel Tables)
2
2
 
3
- An Excel Table (`ListObject`) is the object behind `Insert > Table`: a name, a
4
- structured reference, banded rows, an optional totals row, and header filters.
5
- It is not a pivot table and not a formatted range. Use it when the user asks for
6
- a table, when a filter row helps, or when formulas should read
7
- `Sales[Amount]` instead of `$D$2:$D$900`.
3
+ An Excel Table (`ListObject`) is the object behind `Insert > Table`: a name, a structured reference, banded rows, an optional totals row, and header filters. It is not a pivot table and not a formatted range. Use it when the user asks for a table, when a filter row helps, or when formulas should read `Sales[Amount]` instead of `$D$2:$D$900`.
8
4
 
9
5
  ```python
10
6
  import sys
@@ -23,19 +19,14 @@ wb.save(sys.argv[2])
23
19
 
24
20
  Rules that survive contact with the user:
25
21
 
26
- - `displayName` has no spaces, no hyphens, and must be unique in the workbook — it
27
- becomes the structured reference name (`PipelineTable[Amount]`).
28
- - `ref` must start at the header row and match the real data rectangle. A table
29
- whose `ref` is off by one row shows a phantom header and breaks the filter.
22
+ - `displayName` has no spaces, no hyphens, and must be unique in the workbook — it becomes the structured reference name (`PipelineTable[Amount]`).
23
+ - `ref` must start at the header row and match the real data rectangle. A table whose `ref` is off by one row shows a phantom header and breaks the filter.
30
24
  - One table per rectangle, one header row, no merged cells inside it.
31
- - Give it a style from `TableStyleLight*`/`Medium*`/`Dark*`; a bare table with no
32
- style looks like an unstyled range and the user asks why the stripes went away.
25
+ - Give it a style from `TableStyleLight*`/`Medium*`/`Dark*`; a bare table with no style looks like an unstyled range and the user asks why the stripes went away.
33
26
 
34
27
  ## Appending rows to a table
35
28
 
36
- Rows written below the last table row are outside the table until you widen
37
- `ref`. Widen it in the same pass, or the new records will not filter, will not
38
- band, and structured references will not see them.
29
+ Rows written below the last table row are outside the table until you widen `ref`. Widen it in the same pass, or the new records will not filter, will not band, and structured references will not see them.
39
30
 
40
31
  ```python
41
32
  table = ws.tables["PipelineTable"]
@@ -44,15 +35,8 @@ new_last = first_row + added
44
35
  table.ref = f"A1:G{new_last}" # same columns, one row longer
45
36
  ```
46
37
 
47
- Copy the format and any formula from the previous row (`ws.cell(r, c)._style =
48
- ws.cell(r - 1, c)._style`) before widening, so the new row does not appear as a
49
- plain block inside a banded table.
38
+ Copy the format and any formula from the previous row (`ws.cell(r, c)._style = ws.cell(r - 1, c)._style`) before widening, so the new row does not appear as a plain block inside a banded table.
50
39
 
51
40
  ## Totals row
52
41
 
53
- `table.totalsRowCount = 1` declares a totals row, and `totalsRowFunction` on a
54
- column sets `sum`, `count`, `average`, `min`, `max`, `stdDev`, or `var`. The row
55
- below the last data row must exist and hold the header labels Excel expects; a
56
- totals row declared without that row renders oddly in some engines. If the user
57
- only wants a bold total line under a table, a formula row outside the table is
58
- simpler and less fragile.
42
+ `table.totalsRowCount = 1` declares a totals row, and `totalsRowFunction` on a column sets `sum`, `count`, `average`, `min`, `max`, `stdDev`, or `var`. The row below the last data row must exist and hold the header labels Excel expects; a totals row declared without that row renders oddly in some engines. If the user only wants a bold total line under a table, a formula row outside the table is simpler and less fragile.
@@ -1,8 +1,6 @@
1
1
  # Named ranges
2
2
 
3
- A defined name is a saved reference with a readable name: `Q4Growth` instead of
4
- `Model!$B$6`. Formulas that use names survive row and column insertion, and the
5
- user can select the name box instead of hunting for a cell.
3
+ A defined name is a saved reference with a readable name: `Q4Growth` instead of `Model!$B$6`. Formulas that use names survive row and column insertion, and the user can select the name box instead of hunting for a cell.
6
4
 
7
5
  ```python
8
6
  import sys
@@ -15,27 +13,16 @@ wb.defined_names["PlanRevenue"] = DefinedName("PlanRevenue", attr_text="Model!$B
15
13
  wb.save(sys.argv[2])
16
14
  ```
17
15
 
18
- - The name must be a legal identifier: letters, digits, underscore, not a cell
19
- address (`Q4` alone is a column, so it is refused).
20
- - `attr_text` is a full sheet reference; quote sheet names with spaces:
21
- `'Case A'!$B$4`.
22
- - Workbook-level names live in `wb.defined_names`. To scope a name to one sheet,
23
- put it on the worksheet: `wb["Model"].defined_names["Rate"] = ...` — the same
24
- name can then mean different cells on different sheets, which confuses readers
25
- unless the workbook already works that way.
26
- - Read the existing names with `container_tools/inspect_workbook.py` before
27
- adding one; a duplicate name is a repair prompt in Excel.
28
- - Never point a name at a range whose size changes (`A2:A900`) unless the sheet
29
- really ends there; prefer the exact used range, or a table reference.
16
+ - The name must be a legal identifier: letters, digits, underscore, not a cell address (`Q4` alone is a column, so it is refused).
17
+ - `attr_text` is a full sheet reference; quote sheet names with spaces: `'Case A'!$B$4`.
18
+ - Workbook-level names live in `wb.defined_names`. To scope a name to one sheet, put it on the worksheet: `wb["Model"].defined_names["Rate"] = ...` — the same name can then mean different cells on different sheets, which confuses readers unless the workbook already works that way.
19
+ - Read the existing names with `container_tools/inspect_workbook.py` before adding one; a duplicate name is a repair prompt in Excel.
20
+ - Never point a name at a range whose size changes (`A2:A900`) unless the sheet really ends there; prefer the exact used range, or a table reference.
30
21
 
31
22
  ## Using names in formulas
32
23
 
33
- `=PlanRevenue*Q4Growth` is more readable than `=Model!$B$11*Model!$B$6`, and it
34
- does not break when a row is inserted. Paste the formula text exactly as the user
35
- would type it; openpyxl never resolves the name.
24
+ `=PlanRevenue*Q4Growth` is more readable than `=Model!$B$11*Model!$B$6`, and it does not break when a row is inserted. Paste the formula text exactly as the user would type it; openpyxl never resolves the name.
36
25
 
37
26
  ## Removing a name
38
27
 
39
- Deleting a name leaves every formula that used it as `#NAME?`. Find the users
40
- first (formula text is visible in `inspect_workbook.py` output and
41
- `xlsx read`), then delete, then recalculate and check the gate.
28
+ Deleting a name leaves every formula that used it as `#NAME?`. Find the users first (formula text is visible in `inspect_workbook.py` output and `xlsx read`), then delete, then recalculate and check the gate.
@@ -1,9 +1,6 @@
1
1
  # Number formats
2
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.
3
+ A number format changes how a stored value is displayed, never the value. The cell keeps full precision; `0.0%` shows `6.4%` for `0.06398`. Never store a rounded number to make a column look tidy, and never store `"6.4%"` as text — it stops summing.
7
4
 
8
5
  ```python
9
6
  import sys
@@ -42,31 +39,18 @@ wb.save(sys.argv[2])
42
39
  | Date | `yyyy-mm-dd` | Sortable, unambiguous; store a real date, not text |
43
40
  | Text passthrough | `@` | For codes and account numbers with leading zeros |
44
41
 
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.
42
+ Do not use `0,0%` or `#.##0`. They are not locale-independent and render differently on an engine that reads `,` as a decimal separator.
47
43
 
48
44
  ## Signs, units, and scale
49
45
 
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%`.
46
+ - State the unit once, in the header or title (`Revenue (USD)`), and keep the stored value in that unit. A column that stores thousands but is headed "USD" is a defect waiting to be discovered.
47
+ - If the presentation needs thousands (`$K`) while the data is in units, format does not divide — either store thousands and say so, or divide with a formula in a helper column. Never scale twice.
48
+ - Percentages: store `0.064`, never `6.4`. A stored `6.4` formatted `0.0%` reads `640.0%`.
58
49
 
59
50
  ## Widths and `###`
60
51
 
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.
52
+ `###` means the column is narrower than the formatted number. Widen that column (`ws.column_dimensions["B"].width = 14`) or shrink the font for the block. Never shorten the number, switch to text, or reduce decimals just to fit — and do not autofit every column in the sheet to fix one.
65
53
 
66
54
  ## Dates
67
55
 
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.
56
+ Write a real date (`datetime.date(2026, 1, 31)`) and set `yyyy-mm-dd`. An ISO string sorts alphabetically and calculates as text: `EDATE`, `SUMIFS` on dates, and `YEAR()` all fail silently or with `#VALUE!`. A date typed as a number of days since 1899-12-30 is a serial — fine for formulas, unreadable for people, so format it.
@@ -1,8 +1,6 @@
1
1
  # Sparklines and in-cell bars
2
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.
3
+ openpyxl writes no sparkline object (and neither does the helper CLI), so a "sparkline" here is one of two things: a formula that draws a bar inside the cell, or a tiny native chart. Pick by what the user needs to read.
6
4
 
7
5
  ```python
8
6
  import sys
@@ -21,23 +19,12 @@ ws.row_dimensions[4].height = 14
21
19
  wb.save(sys.argv[2])
22
20
  ```
23
21
 
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.
22
+ - `REPT` needs a repeat count that fits the column. A 9-character column shows about 7 blocks at 9pt; anything longer spills into the neighbouring cell and the bars look like one continuous smear. Scale to the column width, not to 22.
23
+ - Keep the count formula-driven (`value / MAX(values) * width`), so a changed number moves the bar.
24
+ - The block character is text. Sorting the column sorts blocks, and a reader who copies the cell gets `████`, not a number — never put these in the column the numbers should be read from.
25
+ - Guard blanks with `IF(cell="","",…)`, otherwise a blank reads as zero and every missing month draws a minimum bar.
26
+ - An in-cell native chart is not the same as a chart object: if the user needs a hover tooltip, axis, or legend, build a real chart with `recipes/charts.md` instead of stretching a formula.
37
27
 
38
28
  ## When a real chart is the right answer
39
29
 
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.
30
+ A sparkline is context for one number, in a dense table. If the sheet has space and the point is the trend itself, a small `LineChart` (height 4, width 9, no legend, no gridlines) reads better and stays editable. Two or three word-sized charts beat a wall of block characters.
@@ -1,8 +1,6 @@
1
1
  # Chart choice and audit
2
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.
3
+ A chart earns its place when it makes a comparison, a trend, a distribution, or a relationship easier to see than a table would. If the reader only needs the figures, a compact table or a KPI tile is better — and cheaper to check.
6
4
 
7
5
  | Question | Default | Notes |
8
6
  | --- | --- | --- |
@@ -16,48 +14,31 @@ figures, a compact table or a KPI tile is better — and cheaper to check.
16
14
 
17
15
  ## Honesty
18
16
 
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.
17
+ - Series must read from cells, not typed arrays, and derived values must be formulas. A chart of pasted numbers drifts the moment the table updates.
18
+ - Include zero on bar, column, and area axes. Narrowing a line or scatter axis is acceptable when the limits are stated; a truncated bar axis turns a 5% change into a dramatic cliff.
24
19
  - 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.
20
+ - One period, one baseline, one unit across comparable charts. Disclose omitted periods and missing values; do not let a blank read as zero.
21
+ - Sort categories deliberately (by value, or by the natural order of time), and keep the same order across charts that will be compared.
29
22
 
30
23
  ## Formatting rules that catch reviewers' eyes
31
24
 
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.
25
+ - Title states subject and unit: `Profit by category ($m)`, `Monthly revenue (USD)`. No placeholder titles, no legend standing in for a title.
26
+ - Units in the title or on the axis: `($K)`, `($M)`, `(USD thousands)`. Never `($000)`.
27
+ - Axis number formats are set on the chart. Source cell formats do not carry over, and a `0.0%` column can still produce `0.06, 0.08` ticks.
28
+ - Few series: direct labels. More series: legend above the plot area, consistent colours per series across the workbook.
29
+ - Single series: no legend, one colour. A reference or target line is neutral or dashed so it does not read as another data series.
30
+ - Selective data labels only — the last point, the maximum, the one the user asked about. Ten labels on a twelve-point line is a table with extra steps.
44
31
  - Line strokes are set explicitly; a fill alone may not colour an exported line.
45
32
 
46
33
  ## Placement
47
34
 
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.
35
+ Charts sit next to the data they describe, aligned to a column edge, over empty cells, and never over the data, the controls, or a note. Size them to their content; a chart stretched across the page with two bars looks unfinished.
51
36
 
52
37
  ## Verifying a chart
53
38
 
54
39
  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?
40
+ 2. Render and look at the image: are there categories, is anything clipped, is the legend empty, do the bars reach the values in the table?
57
41
  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.
42
+ 4. Re-open in the app the user will use if the colours matter — an authoring preview is not proof that a filled series exports as a visible line.
60
43
 
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.
44
+ If the chart cannot be delivered as asked (the engine has no box plot, the data cannot be rearranged), say so and use the closest clear alternative. Never claim a requirement was met by a chart that does not show it.
@@ -1,29 +1,15 @@
1
1
  # Financial models
2
2
 
3
- Applies to forecasts, budgets, cash-flow plans, and valuation work in any
4
- industry.
3
+ Forecasts, budgets, cash-flow plans, and valuation work in any industry.
5
4
 
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.
5
+ - Periods run left to right with the scale in the header (Actual, Forecast, Budget). Do not mix a monthly column with a quarterly total in the same row unless a labelled total column sits at the edge.
6
+ - Drivers first, results second: volume × price, headcount × cost, days × rate. A revenue row that jumps straight to a number cannot be reviewed.
7
+ - State units once and keep them: thousands and millions in the same block is the classic reconciliation failure. Put the unit in the title, not a footnote.
8
+ - Three statements or none: if the workbook has a balance sheet and a cash flow, the balance sheet must balance and closing cash must equal the cash-flow ending balance. Make these checks, not comments.
9
+ - Working capital moves: receivables from revenue × DSO, payables from cost × DPO, inventory from COGS × days. Hard-coded working-capital changes break when drivers move.
10
+ - Scenarios: one selector, one set of case inputs per driver, and an explicit `Case Selected:` on each output. Two switch cells report two answers.
11
+ - Never hard-code a total that exists as a sum, and never sum a column that already holds a total — double counting is silent.
12
+ - Sensitivity: one or two variables, closed-form or a small grid with the base case marked. A native What-If Data Table is a different object with its own limits; build one only when the user asks and the engine supports it.
27
13
 
28
14
  ## Checks a finance reader looks for first
29
15
 
@@ -36,5 +22,4 @@ industry.
36
22
  | Case consistency | Every tab shows the same selected case |
37
23
  | No hard-coded totals | Total cells are formulas |
38
24
 
39
- Put them on a check tab, terminal and read-only, and make a failing check visually
40
- obvious without painting passing checks green.
25
+ Put them on a check tab, terminal and read-only. Make a failing check visually obvious without painting passing checks green.
@@ -1,22 +1,11 @@
1
1
  # Healthcare operations
2
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.
3
+ Workbooks here usually track volume, capacity, cost, and quality — not patients. Treat patient-level data as sensitive: do not paste identifiers into a workbook unless the user asks, do not copy rows from files you were shown into new sheets, and prefer aggregates.
7
4
 
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.
5
+ - Name the denominator in every rate: per patient day, per discharge, per 1,000 members, per available bed. A percentage without its base is not a metric.
6
+ - Volume and capacity are different columns. Utilization is `used ÷ available`, and both sides need the same period and unit (bed-days, not beds).
7
+ - Payer mix, service line, and site of care are separate dimensions. One table with a column per dimension beats four tables that must be read together.
8
+ - Quality and safety metrics keep their own definitions; do not average a rate across sites without stating whether it is volume-weighted.
9
+ - Claims and remittance data arrive with gaps. Show missing as blank or `n.a.`, never as zero, and state the coverage period.
10
+ - Do not invent clinical thresholds, targets, or compliance claims. Use the targets the source file provides, and cite the sheet and cell.
11
+ - Money and counts follow the finance conventions: units stated once, totals as formulas, checks that detail rows reconcile to the summary.
@@ -1,28 +1,14 @@
1
1
  # Marketing and advertising
2
2
 
3
- Campaign work is a funnel plus a spend table, and both are easy to make look
4
- better than they are.
3
+ Campaign work is a funnel plus a spend table, and both are easy to make look better than they are.
5
4
 
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.
5
+ - One row per campaign, channel, or creative, with that grain named in the header. Mixing a channel total into a campaign table double counts.
6
+ - The funnel reads spend → impressions → clicks → conversions → revenue in that order, with rates derived by formula: `CTR = clicks / impressions`, `CVR = conversions / clicks`, `CPA = spend / conversions`, `ROAS = revenue / spend`.
7
+ - Never compute a ratio from cells that are not the same period and the same population. A monthly CTR built from a quarterly spend and a weekly click count is a number nobody can defend.
8
+ - Attribution is a claim: say which model the numbers use (last click, platform reported, blended) and keep it constant across the table. Do not add platform conversions together and call it incremental revenue.
9
+ - Missing spend or reach for a period is blank, not zero — a zero-spend month shows an infinite CPA and a fake collapse in the trend.
10
+ - Benchmarks and targets belong in a labelled row or column with their source, not in a note the reader has to find.
22
11
 
23
12
  ## Charts for marketing
24
13
 
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.
14
+ Put spend and return on one chart only when both use the same unit; otherwise two aligned charts. Sort channel bars by spend or by return, not alphabetically, and keep the same order across charts. A conversion trend belongs on a line chart with the campaign launch dates marked, not on a pie.
@@ -1,22 +1,11 @@
1
1
  # Scientific and research data
2
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.
3
+ The spreadsheet is usually a working copy of measurements, not the record of truth. Keep it that way: never edit raw measurements in place, and never let a formatted view replace the values behind it.
6
4
 
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`).
5
+ - Raw data on its own sheet, untouched, in columns whose headers carry the unit (`time (s)`, `temp (C)`, `conc (mg/L)`). Derived columns live on a build sheet and reference the raw cells.
6
+ - One row per observation, one column per variable. Do not encode a variable in a colour, a note, or a header font.
7
+ - Replicates are separate rows with a replicate id and a group id; mean and standard deviation are formulas over those rows, never typed.
8
+ - Keep the precision the instrument gives you and let the format hide extra decimals. Rounding stored values destroys the ability to recompute a mean.
9
+ - State uncertainty, detection limits, and excluded points in labelled cells. An excluded point stays in the data with a reason column — deleting it hides the decision.
10
+ - Smoothing, binning, and unit conversion are formulas with visible inputs, or they do not exist. A trend line drawn over noisy points is not an analysis.
11
+ - Distribution, trend, and relationship: choose the chart by the question, and check that axis ranges and bins are stated (`references/charts.md`).