Excel AI Bridge — alpha

Work back and forth

This is the tutorial where you actually use the thing. Claude reads the sheet, changes it, formats it, sorts it, filters it and explains it — and the cells you marked stay yours. Not "the model was asked nicely not to look": the values are replaced before the reply leaves your machine, and every call is written to a log you own.

The deal, in one line: **you mark, the bridge masks, and the refusals are recorded.** By the end you will have seen sixteen tools run and four refusals land, and you will know the one place the guard cannot reach — which matters more than any of the rest, because a reader who does not know it will get burned.

Prerequisites

ribbon.

  python docs\tutorials\make_demo_workbook.py

That writes TutorialDemo.xlsx beside the script: a Budget sheet with a header row, ten team rows (3-12), a Total row (13), a deliberate #DIV/0! in G12, and two cells this tutorial makes private — B14, a director's salary, and B15, a payroll account number. Every value in it is invented.

AI Bridge - connected. Nothing here touches any other workbook.

Everything below happened in one recorded run against that workbook. Nothing is mocked up.

Part 1 — what you mark, and where the mark lives

  1. Select `B14:B15` and press `Mark private`. The toggle is the whole

interface: one press always leaves the selection wholly private, a second press leaves it wholly public. There is no half-marked state to reason about, which is deliberate — a partial mark is the kind of thing you misremember later. Watch the Status group on the right of the tab: it now reads 2 private cells on 1 sheet, and it is reading the workbook, not remembering what you clicked.

The AI Bridge tab with Mark private pressed and the status group showing two private cells

  1. Press `Select all`. It selects every private range on the sheet. This is

the answer to "wait, what did I mark three weeks ago?" — you do not have to remember, and you do not have to trust a highlight being switched on.

Select all has selected the two marked cells

  1. Press `Highlight private`. It shades the marked cells so they stand out

while you work. It is a toggle, it is remembered per workbook, and it is off by default — because a shared file should not announce which cells are sensitive to everyone who opens it. It is conditional formatting only: press it again and your own fill, font and borders are exactly as they were.

Highlight private shading the two marked cells

  1. The marks live in the file, not in the bridge. Each marked range is

stored as a workbook Name beginning AI_PRIVATE_, scoped to its sheet and derived from the sheet and the range's own address. Two consequences worth knowing. First, the marks travel inside the .xlsx: mail the file, open it on another machine, run no bridge at all, and the cells are still marked. Second, the names are created hidden — which is why the Name Manager below (Ctrl+F3) shows no AI_PRIVATE_ entries even though two ranges are marked. They stay out of the Name Box and the Paste Name list so they cannot clutter a formula you are typing.

Excel's Name Manager, showing none of the hidden AI_PRIVATE names

  1. What the guard follows, stated plainly. Before marking, this workbook

got a second sheet, Summary, whose B1 holds =Budget!B14. Marking B14 lists that cell — the add-in scans the other sheets for direct references into the range you are marking. Summary!B1 is masked: the guard reads formula text on every sheet and masks any cell that references a private one, chains included, so putting the formula one tab over does not get around it. The dialog offers to mark those cells outright as well; say Yes and they stay covered even if someone later pastes the formula as a value. What the guard does not follow, because it reads text and not Excel's calculation engine: INDIRECT, OFFSET arithmetic, a reference that reaches the range through a defined name, and a value you already copied and pasted somewhere else as a constant. Mark those yourself.

The cross-sheet dialog naming Summary!B1 and listing what the scan cannot find

Part 2 — letting Claude read

  1. Ask for a range that contains a marked cell. Read A1:E13 and A14:B15

in one prompt. The first range comes back whole. The second comes back with [redacted] where the values were, a masked list naming B14 and B15, and a one-line masking_note: *"2 cells masked by AI_PRIVATE names: B14, B15. Values shown as [redacted]; do not infer them."* Note what the reply does and does not say. It names addresses, never contents — the mask tells Claude where the hole is so it can work around it, and nothing more. The substitution happens on the way out of the bridge process, before the reply is serialised, which is the only place it is worth doing: instructing a model to disregard a value it has already been sent is not privacy, it is etiquette.

Claude reporting the redacted cells and the masking note

  1. **Ask what is protected right now: list_private_cells and

privacy_report.** The first lists every masked address on every sheet. The second adds the audit — and note that privacy_report answers with the same guard.private_cells call that does the masking, so the list and the mask cannot drift apart. Addresses and counts only, again.

Claude listing the private cells and the privacy report

  1. The audit trail is not a feature you switch on. Every tool call — every

read, every write, every refusal — is appended to %LOCALAPPDATA%\ExcelAIBridge\audit\<key>.jsonl, where <key> is a hash of the workbook path, with the timestamp, the tool, the identity of the caller (the Google account that signed in, token:<id> for a bearer token, or local), the sheet and range, how many cells were masked, and whether the call was refused and why. It is on this machine and it is append-only. The refusals in Part 3 all show up here, which is how you can answer "did it ever try?" with a file instead of an opinion.

The recent audit entries in the privacy report

  1. `analyze_sheet` — the shape of one sheet. A structural map of the active

sheet: where the header is, which blocks are data, which are formulas, which are inputs. Cheaper than reading every cell and it is the thing to ask for before a big question, so Claude reasons about the model rather than guessing from a corner of it. Private cells are masked here too, so the map is shaped correctly around a hole it cannot see into.

Claude&#x27;s structural map of the Budget sheet

  1. **analyze_workbook — every sheet at once, and the guard following the
formula across tabs.** It maps each sheet, counts the private cells per
sheet, and draws the edges: which sheet feeds which, read out of the
formula text. Here it reports two sheets, `Budget` with two private cells
and `Summary` with one, and an edge from `Summary` to `Budget`. `Summary!B1`
comes back as `[redacted]` even though nobody marked it and we answered
**No** in step 5: the guard read `=Budget!B14` and treated the cell as
private because of what it reads. That is the boundary of a cell-level
mask stated honestly — it follows what is written in the formula, and
nothing that is not.

![analyze_workbook showing both sheets, the edge between them, and Summary!B1 masked](img/work-back-and-forth-10-analyze-workbook.png)
  1. `trace` — what feeds a cell, and what it feeds. Trace E12 and you get
its precedents from the formula text and its dependents from Excel. Trace
`B14`, the private one, and the trace stops there: the node is returned by
address, marked private, with `[redacted]` where its value and formula
would be. You can see the shape of the model right up to the boundary and
not one cell past it. One asymmetry to know: precedents are read from
formula text and so cross sheets, while dependents come from Excel and do
not — the reply says so itself.

![trace showing precedents and a private node stopping the walk](img/work-back-and-forth-11-trace.png)
  1. `find_errors` — every error cell with what feeds it. The demo workbook
ships one on purpose: `G12` is `=D12/C12` on the Contractors row, whose
headcount is `0`, so it shows `#DIV/0!`. `find_errors` finds it and carries
its direct precedents along, so the cause — the zero in `C12` — is usually
in the same reply as the symptom.

![find_errors reporting the deliberate #DIV/0! and its precedents](img/work-back-and-forth-12-find-errors.png)
  1. `get_instructions` — your standing orders for this workbook. Claude
reads them from a plain markdown file beside the journal:
`%LOCALAPPDATA%\ExcelAIBridge\instructions\<key>.md`, the same
workbook key as the audit. Ask for `get_instructions` and the reply tells
you the exact path. **You edit that file by hand — no tool writes it**, and
that is a deliberate design choice, not an omission: text a model can write
and then later obey is exactly the loop this bridge is built to avoid. Put
house rules there ("never touch column G", "figures are in thousands") and
every conversation starts with them.

![get_instructions returning the instructions file and its path](img/work-back-and-forth-13-instructions.png)

Part 3 — letting Claude change things, and taking it back

  1. `write_cells` — a normal write. Ask for a note in G4, an empty cell.
It lands, and the reply is an acknowledgement — the addresses written and a
journal index — never an echo of the data. Before the cell is touched, the
prior contents are appended to a write journal on this machine. That order
matters: journal first, then write, so the worst case is a record for a
write that never happened, rather than a write no record ever saw.

![write_cells acknowledging the write to G4](img/work-back-and-forth-14-write-cells.png)
  1. **The same write over a non-empty cell — refused, and it says what is
there.** Ask to put the note in `A13` and the batch is refused with
`A13 already holds 'Total'; pass overwrite=true to replace it`. Nothing is
written and nothing is journalled. The refusal exists because the expensive
mistake is not a wrong value, it is a *silently destroyed* one: an assistant
filling in what it believes is an empty template, over a populated sheet, is
a plausible accident with an unbounded blast radius. Making it say the
quiet part out loud — *there is already something here, it says `Total`,
do you still want this?* — costs one round trip and turns a data loss into a
decision. The whole batch is refused, not the one cell, so you never get a
half-applied write.

![write_cells refusing to overwrite A13 and naming what it holds](img/work-back-and-forth-15-write-refused.png)
  1. Say yes and it goes in. Repeat with overwriting allowed and the write
lands. You made the call; the journal has the old value; step 27 puts it
back.

![The same write applied with overwrite allowed](img/work-back-and-forth-16-write-overwrite.png)
  1. `format_cells` — number format, bold, fill. One call sets a thousands
separator on `D3:D12`, makes it bold, and fills it. Formatting is journalled
exactly like a write, with the prior number format, bold state and fill
colour of every cell, so it is reversible for the same reason.

![format_cells applying a number format, bold and a fill](img/work-back-and-forth-17-format-cells.png)
  1. And in the sheet. This is the point of a bridge to a live workbook
rather than an upload: the change is in front of you, in Excel, while you
are still in the conversation.

![The formatted column in Excel](img/work-back-and-forth-18-format-in-excel.png)
  1. `undo_last_write` — the safety net. It restores the cells changed by
the most recent un-reverted change and reports what it put back. It is not
Excel's `Ctrl+Z`: a change made through the bridge does not enter Excel's own
undo stack at all, so this journal is the only way back. It refuses rather
than guesses in four cases — a different workbook, a missing sheet, a cell
that has been marked private since, and a cell that no longer holds what the
bridge wrote (someone edited it in the meantime; restoring would destroy
their edit and report success). A refusal leaves that record at the front of
the queue and says so, because it then blocks the undo of everything
beneath it.

![undo_last_write restoring the formatting](img/work-back-and-forth-19-undo-format.png)
  1. `sort_range` over a range containing a private cell — refused. Ask to
sort `A2:E15` by monthly cost and the guard refuses, naming `B14` and `B15`.
This is the headline of the whole tutorial, so it is worth being precise
about *why*, because "it's a write, and writes to private cells are blocked"
is only half of it. A sort does not read a value out to anybody — but the
order it leaves behind *is* the comparison. Sort a range that contains a
hidden number and the row it lands on tells you how that number compares to
every other number in the range; run it a few times on different keys and
you have bracketed the value you were never allowed to see. So the guard
treats a sort as touching **every cell in the range**, not just the ones it
would rewrite, and refuses the whole operation. Nothing is journalled,
because nothing happened.

![sort_range refused, naming the private cells](img/work-back-and-forth-20-sort-refused.png)
  1. And the sheet did not move. Same rows, same order. The refusal is not a
message shown after the fact — the guard runs before Excel is asked to do
anything at all.

![The Budget rows in Excel, unchanged after the refused sort](img/work-back-and-forth-21-sort-unchanged.png)
  1. `sort_range` on a clean range works, and undoes. Sort just the data
rows, `A2:E12`, by monthly cost descending: it sorts. A sort is journalled
as the whole prior grid rather than cell by cell — the operation moved every
cell, so there is no per-cell "is this still ours?" test to make — and
`undo_last_write` puts the entire block back exactly as it was, the same way
Excel's own undo of a sort behaves.

![sort_range applied to the clean range and then undone](img/work-back-and-forth-22-sort-clean.png)
  1. `apply_filter` — a view, not an edit. Filter the data rows to one team.
Rows hide; no cell changes; nothing is journalled. Be precise here, because
it is the one native operation that does **not** consult the guard: a filter
is not refused over a range containing a private cell, because it neither
reads a cell nor writes one, and the reply carries no row contents. (The
repository README's blanket "each refuses a range that contains a private
cell" overstates this one; the code is the authority and it does not check.)

![apply_filter hiding all but one team](img/work-back-and-forth-23-filter-applied.png)
  1. `clear_filter` removes it — undo does not. Because the filter was never
journalled, `undo_last_write` knows nothing about it. The pair to reach for
is `apply_filter` / `clear_filter`, and the reply from the first says so in
a note.

![clear_filter putting every row back in view](img/work-back-and-forth-24-filter-cleared.png)
  1. **add_conditional_format and add_validation_list — both refuse a range
containing a private cell.** A conditional format over `A2:E15` is refused
naming `B14` and `B15`; a validation dropdown on `B14` is refused the same
way. Both refusals are for the same reason as the sort: a rule that colours
a cell when it exceeds a threshold, or a dropdown that rejects what does not
match a list, is a channel that reports on a value nobody was allowed to
read. Private means untouchable, not merely unreadable.

![Both refused over the range holding the private cells](img/work-back-and-forth-25-cf-validation-refused.png)
  1. On a clean range, both apply — and both are journalled. A conditional
format that reddens `D3:D12` above a threshold, and a Yes/No dropdown on
`G3`. Each is journalled with what it wrote, so `undo_last_write` deletes
exactly the rule this bridge added and leaves rules you made yourself alone.

![A conditional format and a validation list applied to clean ranges](img/work-back-and-forth-26-cf-validation-applied.png)
  1. Undo everything outstanding, and the sheet is back. Ask Claude to keep
calling `undo_last_write` until it answers that there is nothing left to
undo. The validation goes, the conditional format goes, the overwrite of
`A13` goes, the note in `G4` goes. What remains is what you did with your
own hands: the two private marks and the highlight.

![The Budget sheet in Excel after every bridge change has been undone](img/work-back-and-forth-27-all-undone.png)

What the AI can never do

inside the bridge process, before the reply is built. Same for the formula text, and same for any cell on that sheet that is computed from it.

because the resulting order is itself a comparison of the hidden value.

same reason.

and names what is in the way; a private target refuses one step earlier and is never quoted at all.

audit journal on your machine with who asked, what they asked for, and how many cells were hidden.

And the two things it can do that you should keep in mind, because they are limits of the mechanism rather than bugs in it:

dependents on the same sheet. A formula on another tab, a value pasted elsewhere, a reference through INDIRECT, OFFSET or a defined name — none of those are followed. Step 10 shows exactly this happening. Keep private values and everything derived from them on one sheet, or mark the copies too.

not refused. Do not read the sort refusal as "every native operation is guarded" — read it as "every operation that could carry a value out, is".

Reproducing this run

Every screenshot above came from one unattended run of scenarios/capture-work-back-and-forth.ahk, which drives both sides — Excel's ribbon and the Claude composer — and writes a journal of frames and window rectangles that tutorial_tool.py binds to the committed images by hash. The coverage claim in this tutorial is not checked by reading its prose: the gate reads the bridge's own audit journal and requires an entry for every tool inside the run's time window. A tool named here but never actually called fails the build.