# Canonical Office selectors

`office.selector/1` is the format-explicit, parseable address model shared by
the unified Office facade. It describes addresses only. Parsing a selector does
not claim that a command can resolve, query, or mutate the addressed content.

## Grammar

```text
selector      = "/" format "/" segment ("/" segment)*
format        = "docx" | "xlsx"
segment       = name [selection]
selection     = "[" positive-index "]"
              | "[" key "=" json-string "]"
name          = lower-alpha (lower-alpha | digit | "-" | "_")*
key           = lower-alpha (lower-alpha | digit | "-" | "_")*
```

JSON strings are used deliberately: quotes, backslashes, control characters,
slashes, brackets, equals signs, and Unicode round-trip through one familiar
escaping rule. Rendered selectors always use the compact canonical JSON string
form.

DOCX selectors begin with a story segment:

```text
/docx/body/p[1]/r[2]
/docx/header[1]/p[1]
/docx/footer[1]/tbl[1]/tr[2]/tc[1]/p[1]
/docx/footnotes/note[id="7"]/p[1]
/docx/comments/comment[id="review/one]=\"ready\""]
```

`body`, `footnotes`, `endnotes`, and `comments` are unselected story roots.
`header` and `footer` require a 1-based positional selector. Descendant segment
names remain visible in the typed AST so format adapters can validate allowed
child kinds without parsing the path again. DOCX named selectors use the `id`
key; arbitrary XPath predicates, regexes, and expressions are not part of this
grammar.

XLSX selectors address the workbook singleton or a sheet by stable name or
snapshot-relative position. Sheet selectors may end in a typed A1 coordinate:

```text
/xlsx/workbook
/xlsx/sheet[name="Data"]/cell[A1]
/xlsx/sheet[name="O'Brien / Q1"]/range[A1:C12]
/xlsx/sheet[2]
```

Cell columns are canonical uppercase `A` through `XFD`; rows are `1` through
`1048576`. Range endpoints are normalized to top-left then bottom-right. `$`
absolute markers, whole-row/whole-column references, formulas, unions, and
cross-sheet formula syntax are intentionally outside selector syntax.

## Stability

Every parsed selector reports one of two address classes:

- `Stable`: it contains only singleton roots and named keys. For example,
  `/docx/comments/comment[id="7"]` and `/xlsx/sheet[name="Data"]`.
- `SnapshotRelative`: it contains any positional selector or A1 coordinate.
  Inserts, deletes, reordering, or sheet edits can move the addressed content.

The classification describes the address, not the lifetime of the underlying
object: a stable key can still disappear or be renamed.

## Resource limits and diagnostics

Parsing is total and bounded:

- input length: 2048 Unicode scalar values;
- depth: 32 segments after the format root;
- selector count: 32;
- segment/key length: 32 characters;
- positional index width: 9 digits;
- named value length: 256 Unicode scalar values.

Failures use a structured error code, a zero-based Unicode-scalar offset, a
bounded input echo, and a bounded message. Cross-format shapes are rejected by
their explicit root and cannot silently fall through to the other format's
rules. Input and decoded JSON values must contain well-formed UTF-16; isolated
surrogates fail before an AST is created or UTF-8 encoding is attempted.

## Adapter and capability boundary

`selector_from_docx_projection_path` converts the existing `/body/...`,
`/header[n]/...`, and annotation paths emitted by the DOCX tools. The
`selector_for_xlsx_workbook`, `selector_for_xlsx_sheet`,
`selector_for_xlsx_cell`, and `selector_for_xlsx_range` construct the canonical
XLSX shapes, quote worksheet names, and validate A1 coordinates. These helpers
adapt syntax only: they do not open a package or establish that an addressed
object exists.

Format records in `office help --json` expose the selector schema, root,
examples, and the resolver status for that format. Both DOCX and XLSX are
`read-resolved`: `office outline`, `get`, `text`, and `query` dispatch from the
validated package format and resolve the applicable bounded projection. A
selector with the other format's root fails with a format-specific mismatch;
it never falls through to the other resolver.

The DOCX resolver covers body, header, footer, footnote, endnote, and comment
stories plus paragraphs, runs, tables, rows, cells, hyperlinks, and images.
Unique non-empty note/comment ids within selector limits emit stable
`note[id="..."]` / `comment[id="..."]` paths. Missing, duplicate, or
unrepresentable ids emit positional paths and a bounded warning instead;
asking for a duplicated stable id fails as ambiguous. Every descendant of an
annotation item remains snapshot-relative because it contains positional
segments.

The XLSX resolver covers the workbook singleton, worksheets, chart sheets, and
worksheet cell/range coordinates. A positional sheet selector such as
`/xlsx/sheet[2]` is accepted as snapshot-relative input, but successful output
uses the stable name-keyed sheet path. Coordinate endpoints are normalized to
canonical uppercase A1 form; their paths remain snapshot-relative. Chart
sheets can be resolved and inspected as sheets, but reject cell/range
descendants. See [office-xlsx-read.md](office-xlsx-read.md) for scan ordering,
query predicates, and resource limits.
