---
name: spreadsheet-xlsx
description: 'File-type spec: Excel spreadsheet (.xlsx / .xlsm). Used by the KB ingest pipeline to classify + extract + render.'
title: Excel spreadsheet
category: file-types
status: active
author: system
metadata:
  priority: 0
---
## What this skill is

One entry in the **File-Type Registry** ([kb/file_types.py](../../../../server/src/agntspace/kb/file_types.py)). Declares how `.xlsx` and `.xlsm` files flow through the three ingest layers:

1. **Classify** — extensions `.xlsx` / `.xlsm` + MIME `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` (or `.ms-excel.sheet.macroEnabled.12` for `.xlsm`) + magic `PK\x03\x04` (zip archive); source_type `spreadsheet`; LLM prompt focuses on sheet structure, key columns, notable rows, aggregates, and data-quality observations.
2. **Extract** — `xlsx_openpyxl@1.0.0` walks worksheets in workbook order, emitting one heading (depth=1) per sheet with the sheet name, then either a structured `table` element (when the first row reads as a complete header AND every subsequent row has at least one non-empty cell) or one `paragraph` element per non-empty row (sparse / config / freeform sheets). Cell values are the **computed result** — `data_only=True` reads cached values, never formula text. Embedded images are recorded as `MediaRef`s with semantic pointers (`<filename>#sheet=N&img=J`); a vision pass is not yet wired (mirrors the pptx Phase 5 v2 deferral). Document-level metadata (title / author / subject / company / created / modified) comes from `workbook.properties`.
3. **Render** — table canvas type with thumbnail preview; ui_icon `Table` (Lucide).

Legacy `.xls` (binary format) is **not supported** — openpyxl reads OOXML only. Convert via Excel (Save As → .xlsx) or LibreOffice headless before ingesting. `.xlsm` (macro-enabled) reads as `.xlsx` but **VBA macros are NOT extracted** — deliberate security perimeter; macros never enter the agent's world.

The actual strategy lives in [config/file-type.yaml](config/file-type.yaml) — edit it to customise. The registry reloads on next KB service init.

## Why this exists as a skill (not just Python)

Rule 12 — strategy lives in skills, never in code. The Python bundled default ([file_types_defaults.py](../../../../server/src/agntspace/kb/file_types_defaults.py)) is a degraded-mode fallback so the pipeline is never empty on first boot. The YAML here is the canonical source, and your vault's copy at `system/skills/file-types/spreadsheet-xlsx/config/file-type.yaml` overrides both (overwrite=True — user vault wins).

## Extending

- Want a different extraction strategy for this file type? Change the `extractor:` field to another registered extractor id (`text_utf8`, `pdf_pdfplumber`, `vision_llm`, `csv_tabular`, `json_pretty`, `vtt_srt`, `html_parser`, `docx_python_docx`, `pptx_python_pptx`, `xlsx_openpyxl`, `epub_markitdown`, `audio_whisper`, `video_ffmpeg_vision` — plus any plugin-registered extractors).
- Want to tune the LLM's focus? Edit `prompt_focus:`. It propagates to the classify prompt in the next KB service init.
- Want vision descriptions on embedded charts/images? Not yet — Phase 5 v2 only wires that for PDFs. The xlsx extractor records `MediaRef`s for embedded images (best-effort) but does not currently call vision. A future extension can mirror the PDF `extract_embedded_images` opt-in.
- Want formulas extracted as text instead of computed values? Override the extractor and pass `data_only=False` to `openpyxl.load_workbook`. Default is computed values because the LLM reasons about results, not implementations — and stale formula caches are a known gotcha. Consider whether you really want formula text before flipping this.
- Want to change how the file is filed under `library/`? That's the **library layout** concern (thematic / type-date / flat) — see KB-level SCHEMA.md, NOT this file.