# office.* verbs (F106) -- when + how

The `office.*` namespace gives the brain four cross-platform native
verbs for Excel + Word IO. They are NOT visible in the lazy manifest
stub (slice 1) -- the brain fetches THIS doctrine via
`yujin.doctrine.discover` when the user mentions Excel / Word / .xlsx
/ .docx / spreadsheet / hoja / planilla / Word document.

## Catalog

| Verb | Use when | Policy |
|---|---|---|
| `office.excel.read`  | User wants to peek into or compute over an .xlsx / .xls / .csv | project-root-gated |
| `office.excel.write` | User wants a fresh workbook (new file). Refuses to overwrite by default | approval-required |
| `office.word.read`   | User wants text + heading outline from a .docx                  | project-root-gated |
| `office.word.write`  | User wants a fresh .docx (new file). Refuses to overwrite by default | approval-required |

## Args -- quick reference

```
office.excel.read   {path, sheet?, range?, header_row?}
office.excel.write  {path, sheet?, rows[<obj|arr>], overwrite?}
office.word.read    {path, include_outline?}
office.word.write   {path, sections:[{kind:'heading'|'paragraph', level?, text}], overwrite?}
```

## Picking the right verb

The brain MUST NOT use `yujin.fs.read-file` for .xlsx / .xls / .docx
-- they are binary, the file content will look like noise + waste
tokens. Always pick `office.excel.read` or `office.word.read`.

The brain MUST NOT shell out to `python -c "import openpyxl..."` or
similar. The verbs are cross-platform; no shell needed.

## Excel patterns

- **Read with header row (default):**
  `office.excel.read({path:'data/x.xlsx'})` returns
  `rows: [{col_a: 1, col_b: 2}, ...]`.
- **Read raw cells (no header):**
  `office.excel.read({path, header_row: false})` returns
  `rows: [['col_a','col_b'], [1,2], ...]`.
- **Specific sheet + range:**
  `office.excel.read({path, sheet: 'Q1', range: 'A1:D50'})`.
- **Write from objects:**
  `office.excel.write({path:'out.xlsx', rows:[{name:'Ana', age:30}]})`.
- **Write raw cells:**
  `office.excel.write({path:'out.xlsx', rows:[['a','b'],[1,2]]})`.
- **Overwrite existing file:**
  pass `overwrite: true`. Refuses by default to avoid accidental
  destruction.
- **Output format:**
  driven by extension. `.xlsx` -> xlsx, `.csv` -> csv, `.xls` -> biff8.

## Word patterns

- **Read text:**
  `office.word.read({path:'docs/notes.docx'})` returns `text +
  outline` (best-effort heading list).
- **Outline-only mode:**
  the outline is best-effort -- mammoth may miss some heading
  styles. If empty, do not assume the doc has no headings.
- **Write a structured doc:**
  ```
  office.word.write({
    path: 'docs/notes.docx',
    sections: [
      { kind: 'heading',   level: 1, text: 'Title' },
      { kind: 'paragraph', text: 'Intro paragraph.' },
      { kind: 'heading',   level: 2, text: 'Section A' },
      { kind: 'paragraph', text: 'Body.' },
    ]
  })
  ```
- **Only .docx output.** The verb refuses `.doc` (legacy binary
  format -- not in the cross-platform path).
- **Refuses overwrite by default.** Use `overwrite: true` for
  destructive writes.

## Hard caps

- Read: 16 MB file size.
- Excel write: 1,000,000 rows; 1024 fields per row.
- Word write: 50,000 sections; 100,000 chars per section text.

## Don't

- Don't read .xlsx via `yujin.fs.read-file` (binary garbage).
- Don't shell out to python/openpyxl/docx (cross-platform path
  already covers it).
- Don't claim "written" without invoking `office.*.write` first.
- Don't overwrite without confirming or passing `overwrite: true`.

## Do

- Use `office.excel.read` -> pizarron tab for tabular data >10 rows.
  Chat-pasting Excel rows is noisy.
- Use `office.word.read` -> chat summary + pizarron full text if
  the doc is long.
- Pair `office.*.write` with R8 modal (policy=approval-required;
  user confirms before disk write).

## Opening vs reading (desktop.open vs office.*.read)

There are TWO different intents -- pick by what the user wants:

- **OPEN / SHOW it to the user** ("abri la planilla", "open this Excel",
  "show me the doc") -> `desktop.open({target})`. It launches the file
  in its native app (.xlsx -> Excel, .docx -> Word, .pdf -> the viewer)
  or, for a Google Sheets/Docs URL or a `.gsheet`/`.gdoc` shortcut, in
  the browser. This is the right move when the human wants to SEE it.

- **READ / COMPUTE over the data** ("what's the total", "extract the
  rows", "summarize the doc") -> `office.excel.read` / `office.word.read`
  / `office.pdf.read`. These return structured data for YOU to reason
  over.

NEVER open a binary office file as a pizarron editor tab
(`forge_open_file_for_editor` / fs.read-file): the bytes render as
noise and FREEZE the panel (PND-102). The open-file verb now refuses
office/binary extensions and points here.
