opentakeoff-mcp
The opentakeoff-mcp server exposes the OpenTakeoff flooring takeoff engine, allowing an AI agent to open plan PDFs, set scales, trace rooms, measure areas/lines, and export quantity reports.
Load a plan (
load_plan): Open a PDF from disk, replacing the current session; returns page count, per-sheet dimensions, sheet numbers, and any detected scale notes.Inspect sheet details (
sheet_info): Get dimensions, vector segment count, scale status, and committed shape count for a specific sheet.Set the scale (
set_scale): Configure a sheet's scale via a standard label (e.g.1/4" = 1'-0"), a raw units-per-pixel value, a two-point calibration from a known dimension, or by adopting the auto-detected scale.One-Click Area trace (
one_click): Click a point inside a room and the engine flood-fills and traces the polygon from vector linework; returns area (SF) and perimeter (LF), optionally committing the shape to a named finish condition or deduct role.Measure a custom polygon (
measure_polygon): Supply closed polygon vertices to get area (SF) and perimeter (LF), with optional commit to a condition.Measure a polyline (
measure_line): Supply an open polyline to get its length (LF), with optional commit as a linear shape (base, transitions, feature strips).Get takeoff summary (
takeoff_summary): Retrieve per-condition totals — floor/wall/border SF, LF, EA, SY — with and without waste, plus grand totals.Export the full takeoff (
export_takeoff): Return the complete annotations payload (importable back into the browser app), optionally writing it to a JSON file on disk.Delete a shape (
delete_shape): Remove a previously committed shape by its ID.Read sheet text (
read_sheet_text): Extract text with positions from a full sheet or a specified region, useful for parsing title blocks, room labels, finish schedules, and scale notes.
OpenTakeoff
The measurement engine for building plans—built so an AI agent can drive it, and so an estimator wants to.
A takeoff is the act of measuring quantities off a construction drawing. OpenTakeoff does it two ways over one engine: 47 MCP tools for an agent, and a browser canvas for a person. Agents and people share the takeoff document and quantity calculations. Each sheet carries its calibration; measurements carry geometry, method and authorship. Recalibration updates quantities together, incompatible imports report scale conflicts, and agent measurements carry an explicit review status. See the Phase 1 test guide.
For agents · Try the canvas · The engine's contract · For the person at the canvas · The data layer · Research · Fork it · Contribute
The two manuals: agent manual · user manual
Read this in: 日本語 · 한국어 · 简体中文
Watch it: an autonomous agent runs a takeoff, live, no cuts (2:47) · hospital finish plan → report in about a minute (1:14) · canvas walkthrough (1:10) · One-Click Area (0:51)
One-Click Area is temporarily gated. The flood engine is being re-validated against a wider plan corpus. Until that finishes the One-Click tool is off the canvas rail (
Oreports the gate) and theone_click/detect_roomsMCP verbs are not registered (a default build ships 52 tools). Trace rooms with Area (A) in the canvas andmeasure_polygonover MCP; every other tool, sweep and derivation is unchanged. A build lifts the gate withVITE_ONE_CLICK=1(canvas) /OPENTAKEOFF_ONE_CLICK=1(server). Sections and videos below that show One-Click describe the engine as it returns — seedocs/design/ONE_CLICK_GATE.md.
Start here
You are | Go here |
An estimator with a bid due | Open the canvas—drag in a plan, no account, nothing uploads. The user manual gets you from a blank tab to an exported takeoff in five minutes, and its working order is the sequence to run on a real bid set. |
An AI agent—or the person wiring one up |
|
A developer building on the engine |
|
A crew that wants its own copy | Fork it—your own instance on your own URL in a few minutes, Apache-2.0, nothing phones home. Same path if you're going to send a pull request. |
Windows, macOS, Linux — all of it
OpenTakeoff is a client-only browser app, so the canvas runs the same on Windows, macOS, ChromeOS and Linux in any current Chrome, Edge, Firefox or Safari. Nothing installs, nothing uploads, and no feature is gated on an operating system.
Shortcuts are platform-aware. The app labels modifiers for the keyboard in front of you —
Ctrl/Alt/Shifton Windows and Linux,⌘/⌥/⇧on a Mac — and the handlers have always treated⌘andCtrlas the same key. Press?in the canvas for the current list.The MCP server is tested on Windows.
npx -y opentakeoff-mcpruns on Windows, macOS and Linux, and CI runs the full MCP suite — typecheck, tests, build and the packaged smoke test — onwindows-latestas well asubuntu-lateston every change.Optional extras. The bundled capture server is stdlib Python 3 and runs anywhere Python does — on Windows invoke it with
python capture\capture_server.py selftest(or thepylauncher) rather thanpython3. Neither it nor the optionalserver/AI sandbox is needed to use the canvas.Locked-down enterprise fleets (MSIX packaging, Windows Sandbox, Intune silent deploy) are tracked in #226 and not yet built.
Related MCP server: quote-mcp-server
What this is
Measuring quantities off a plan is the input to every construction bid—how much floor, how much wall, how many fixtures, at what scale, on which sheet. It happens thousands of times a day. Until OpenTakeoff there was no open-source takeoff engine at all, web-based or otherwise, and nothing an autonomous agent could call.
OpenTakeoff is that engine, with two front ends over identical geometry:
A stdio MCP server—
npx -y opentakeoff-mcp, 52 tools, on the official MCP registry. An agent opens a plan, reads the title block, sets the scale, floods the rooms, checks its own work on a rendered overlay, and hands back a marked-up planset PDF.A browser canvas—no backend, no account, no upload. An estimator drags in a plan set and traces it, using One-Click room detection, CAD hatches, roll-goods seam layout, a materials buy list, and exports.
Neither is a wrapper around the other. The MCP server imports
web/src/lib/{oneclick,sheets,geometry,totals} directly, so a shape committed by an agent is
field-identical to one committed by a hand at the canvas—same flood mask, same corner snap,
same waste math, same refusal messages.
Provenance is the load-bearing part. Every shape records the scale it was measured at, the method that produced it (vector flood, raster trace, hand-drawn, agent-proposed), whether a human corrected it, and the machine's original boundary frozen beside the correction. Downstream, that's an audit trail a PM can read. Upstream, it's a labeled (geometry → finish) pair—the training signal takeoff models have never had at scale. That second use is not a side effect; see the data layer.
Recently shipped
Stitched sheets—a floor split across a match line becomes one working surface; a room that crosses the seam traces as one shape, One-Click included (#161)
PDF layer roles—CAD-exported sheets state what their ink is, so One-Click reads the layer tree instead of inferring boundaries from hatch, with a Layers panel on the canvas and scored corpus IoU (#85)
The sheet graph—an agent asks "what finish is in room 134, and how do you know" and gets the schedule row with a citation per cell, across continuation sheets, rotated headers, and multi-building keys:
sheet_graph/resolve_tag/find_schedule(#87)Roll goods—opt a condition into broadloom or sheet material and the engine figures the seams: lanes, multi-roll splits, cuts drawn to scale over their rooms in cutting order, a to-scale roll diagram with drag-to-reorder, and order footage beside the measured quantities (#136)
Transitions, at the canvas—⟂ Transitions… in the Takeoffs panel derives the line where two finishes meet from rooms you already measured;
derive_transitionsdoes the same over MCP. Flood-traced rooms don't share edges, so what's actually there is proximity in two flavors that mean different things: finishes changing inside one open space commit as a butt joint, while rooms parallel across a partition come back withheld as questions—the transition is a threshold in a doorway, and nothing in a trace record says where the doorway is. Committing 34 LF of threshold because two rooms share 34 LF of wall would be a wrong bid with a machine's confidence behind it (#202)symbol_sweep—every instance of a repeated symbol from one marqueed example, crossing scales only by a stated ratio, never a searched onemark_verdict/delete_verdict—an agent signs its own work as a graphiteAGENTdiamond; only a human hand mints the greenAPPROVEDsealOne-Click accuracy wave—face extraction and gap tolerance from RFC #60 (contributed by @knmurphy), hairline runs shadowing a heavier wall classified as annotation rather than boundary, and an in-swing door's sector taken behind the leaf instead of the arc
Voice takeoff (gated off the toolbar by default;
VITE_COMMAND_BOX=1builds it back)—holdMand saycarpet one, waste seven; recognition is whisper-tiny.en in WebAssembly on your machine, audio never leaves the browser (docs/VOICE.md)
Full history: CHANGELOG.md · every capability mapped to its code: FEATURES.md
For agents — start here
Point any stdio MCP client at the published package. Node 20+, no clone, no build:
{
"mcpServers": {
"opentakeoff": {
"command": "npx",
"args": ["-y", "opentakeoff-mcp"]
}
}
}If you carry a GitHub identity, fork the repository as well: that's where an engine change you make gets tested—every pull request from a fork runs the full CI with a read-only token—and tested pull requests from agents merge under the same bar as anyone else's. The contributor path is the same one.
Claude Code: claude mcp add opentakeoff -- npx -y opentakeoff-mcp. Claude Desktop users can
double-click the opentakeoff-mcp.mcpb bundle from the
latest release instead—it excludes the
optional native canvas on purpose, so every JSON tool works everywhere and the rendering
surfaces (view_sheet, the sheet-image resource) say exactly what's missing where they can't
run. Docker and a local clone are both supported: mcp/README.md.
A real run (3× speed): the agent takes off patient rooms 161–163 on a federal finish plan, exporting after each commit. Every shape lands in the app as a dashed pencil proposal and becomes ink only when the operator clicks Accept. The full run, live and uncut, is on YouTube (2:47).
The tools
Group | Tools |
Open and orient |
|
Scale |
|
Measure |
|
Repeat and derive |
|
Read the drawing set |
|
Edit and audit |
|
Mark and sign |
|
Ask |
|
Hand off |
|
Plus browsable sheet resources (takeoff://sheets) so an agent can see the working set, not
only act on it. Multi-document sessions are first-class: a bid set is plans plus schedule
plus addenda, and load_plan --merge adds a document without disturbing existing scales,
conditions, or shapes—the sheet graph then spans the whole set, so a room tag on one file
resolves to a schedule row in another. edit_condition reaches the waste %, the ×N multiplier,
and roll_setup, so an agent's takeoff doesn't ship with net === gross.
The agent's manual is docs/AGENT_GUIDE.md—the counterpart to the
estimator's: the operating model in six facts, the standard finish every takeoff ends with, the
withheld-is-the-answer doctrine, what has no agent verb and why, and a refusal-to-next-move table.
Tool-by-tool reference: mcp/README.md. The same surface in prose, with the
sheet-graph and sweep behavior in depth: docs/MCP.md.
The contract that makes it drivable
Most measurement APIs are hostile to an agent because they let it be confidently wrong. These are the rules that make this one safe to hand a model, and why each one exists:
One coordinate frame, stated everywhere. Image pixels at render scale 2.0—PDF points × 2, origin top-left, y down, the browser canvas's native space. Every sheet payload carries dims in both px and pt. No tool takes a coordinate in units it has to guess.
Scale is a gate, not a default. The drawn scale note is read off the sheet but never applied silently; adopting it is always an explicit
set_scale. Measuring an unscaled sheet refuses. Pixels × a wrong scale² is every number wrong at once, so the engine would rather stop than guess. Disagreeing scale notes inside a measured region raise a warning rather than a silent pick.The engine traces; the model doesn't invent.
one_clickreturns the ring the wall network produced from a seed point you name. A model cannot hand back a polygon it imagined and have it counted.Every record carries how it was made. Method, seed point, whether hatch filtering engaged, whether it came off scan pixels, confidence factors, and the machine's original ring if a human later moves it.
Agent work is pencil until a person inks it. Exports land in the canvas as dashed proposals.
mark_verdictlets an agent sign its own work as a graphiteAGENTdiamond; the greenAPPROVEDseal has exactly one code path and it is the toolbar button under a human hand. No MCP call, no import, mints one.The deliverable is a marked-up planset, not JSON.
export_marked_pdfburns the work into the drawings as drawn—condition colors, hatches, quantity chips, count markers—behind a legend cover with totals and a tally of how much of the set a person has actually reviewed. A takeoff nobody can check is not a takeoff.Refusals are actionable strings. "That space isn't enclosed on the plan linework—the fill spilled" tells a model what to do next. A silent zero doesn't. Tools that can't answer withhold with a stated reason rather than returning a plausible number.
Prove it — OpenTakeoff Academy
aec.kentucky-ai.com is a standalone open benchmark and
certification arena for agents that do takeoff. Bring any model and your own harness; you are
scored on operating a real takeoff tool against geometry you don't control—a wrong
calibration yields a wrong area—not on emitting a plausible-looking number. Runs emit a
signed bundle with full provenance of every tool call, scoring is against held-out ground truth
and a human Senior Estimator baseline, and clearing a tier earns a credential that's
independently verifiable. The Certified path drives this engine (opentakeoff-mcp) behind the
task tools. Repo:
Kentucky-ai/opentakeoff-academy.
For the person at the canvas
The agent path exists because the human path is real. Everything below is the production measuring engine carved out of a commercial Division 9 estimating system—not a demo reimplementation.
cd web
npm install
npm run dev # http://localhost:5173Or open the live demo. Drag in
demo/sample-plan.pdf, accept the detected scale, choose a condition, press A (Area)
and click the room's corners. (One-Click Area is temporarily gated — see the note at the top.) Open Report for the breakdown and the exports. That whole loop on
video: walkthrough (1:10) ·
One-Click Area (0:51). The complete
zero-to-exported walkthrough is the user manual.
What a real bid looks like on it, in the order an estimator works one:
Drag in the whole
.zipoff the bid platform—plans, finish schedule, addenda.Set the scale on every sheet you'll measure, and check a dimension (
K) on each. Ten seconds a sheet, and it's the only mistake that gets every number at once.Pull your conditions off the architect's finish schedule instead of typing them, and set waste and materials before you trace.
Stitch anything split at a match line, align it, and only then start measuring.
Trace the floors room by room — Area (
A) while One-Click is gated. Derive base and transitions off the rooms you just traced rather than measuring them a second time—and read what the derivation reports and never counts, because those are doorway thresholds you still owe.Walk the set and look at what landed, fix with the grips, save a revision.
Export both: the Report for pricing, the Marked set PDF for whoever has to check you.
The full version of that sequence, with the section for each step, is the manual's working order. What every term above means is in its glossary.
Open anything, instantly
A plan PDF, an image (scan, screenshot, photo), or a whole .zip plan set straight
off a bid platform. Zips are unpacked and images wrapped to PDF in your browser—multi-page,
multi-file, up to 4 sheets side-by-side, with hostile-archive guards so a malformed zip
fails cleanly instead of ballooning the tab. No upload step, no conversion service, no account.
A real measuring engine
One-Click Area is the headline — temporarily gated while the flood engine is re-validated (see the note at the top); this is what it does when it is on: click inside a room, the linework bounds a flood fill, the polygon traces itself, the vertices snap to true corners. Hatching and poché don't fool it—tile grids, plank lines, and section fills classify as pattern rather than wall, and the escalation is conservative enough that a misread can never come out worse than the strict fill. Scanned sheets work too: with no vector linework the engine reads rendered pixels—adaptive thresholding, polarity detection for blueprint negatives, a gap-bridging pass for faded ink—and badges the result so you verify the edges before committing. On CAD exports that publish a layer tree, One-Click reads the declared roles instead of inferring them.
Plus the full manual kit—Area, Rectangle, Linear, Curved Line, Surface Area (walls), Count, and Cut Out deducts—and a Zone check that answers "what's in this wing?" without touching the takeoff.
⟂ Transitions derives the line where two finishes meet, from rooms you already measured. Finishes changing inside one open space commit as a dashed butt-joint run you accept; rooms parallel across a wall are reported and never counted, because that transition is a threshold in a doorway no trace can locate—you get its length, the wall thickness, and a link that puts it on screen.
Drafting aids that behave like drafting aids
45°/90° angle lock: come within a few degrees of square or diagonal and the segment locks
to the axis—the click commits the exactly on-axis point, so walls come out dead square
(hold ⇧ to force it at any angle). On the canvas the crosshair is the cursor: the OS
pointer hides, a star marks the crossing, in-progress work draws in the instrument's own
cobalt, committed shapes wear their condition color. The lock reads quietly—the star swells,
the preview thickens, a chip shows the locked angle and the live segment length. Snap
(beta) pulls onto true PDF endpoints, and a corner beats an axis.
Scale that matches real plan sets
Auto-detects the drawn scale note, or calibrate from any known dimension. Scale is
remembered per sheet, because plan sets are never one uniform scale and tools that assume
they are get the numbers wrong. Check a dimension (K) is calibrate's read-only twin: pick
a printed dimension string, type what the drawing says, and get a graded verdict (green within
1%, amber within 5%, red past it) plus a one-click Recalibrate to this. Every scale
acceptance drops an ephemeral calibrated ruler bar on the sheet, so a 2×-off scale is obvious
before anything gets traced. Imperial or metric (m²/m, 1:50-style ratios) is a display toggle—takeoffs
are stored unit-agnostically, so flipping it never changes a measurement.
Conditions, materials, and the buy list
A condition is one finish (LVP, carpet, tile, base…), carrying a line/fill color, a CAD hatch pattern so the canvas reads like the real drawing, a per-condition waste %, an ×N multiplier, a default wall height, and a thickness that turns a linear run into border SF. Import from schedule parses the architect's finish table off the sheet into conditions behind a verify dialog—you approve what becomes a condition, and the product spec rides along as read-only report columns.
Supporting Materials is the layer most takeoff tools punt on: per condition, a labor type and a subfloor type, plus the consumables that actually go on the order—adhesive, sealer, thinset, grout, cove-base adhesive—each with a coverage rate and a basis (floor SF / linear LF / each / figured seam LF). Order quantity derives automatically: measured ÷ coverage, rounded up to whole units. Adhesive and mortar lines get coverage presets; grout lines get a calculator that derives SF/bag from tile size, thickness, joint width, and bag weight. Preset values are industry-typical round numbers—always verify against the product data sheet.
Roll goods — the seams, figured
Opt a condition into broadloom or sheet material (material class, roll width, max roll length,
seam and wall allowances, direction, sell unit) and the engine lays out the cuts: lanes, seam
placement, multi-roll splits, and order footage. Cuts draw to scale over their own rooms in
material-true colors, numbered in cutting order, and slide or resize in an edit mode that's on
the undo stack. The docked Roll panel shows those cuts nested on the roll with dimensions
and drag-to-reorder re-packing, and Roll Order LF, Rolls, and Seam LF ride the
Report, CSV, and Excel next to the measured quantities. Seam LF is the weld-rod / seam-tape
quantity read straight off that layout—counted between adjacent lanes of the same room, net
of the wall overage, only where two lanes actually face each other—so a supporting-materials
line on the seam LF basis prices the rod off where the cuts meet instead of off a share of
the perimeter. A 20-ft-wide room off a 12-ft roll seams once down its length; the same square
footage as two separate 10-ft rooms seams not at all, and no factor on area can tell those
apart. Available headlessly too, through roll_setup on edit_condition. (The roll-layout engine
was contributed by Michael Hartman.)
Multi-sheet reality
Stitching: a floor split across a match line becomes one working surface—align the joint
by picking the same drawn point on both sheets, then trace straight across the seam.
Levels group a multi-floor set. A visual gallery (G) is where you choose and open sheets, and
Regroup restores a side-by-side composition in one click. A trace can't span two grouped
sheets—the gap between panels isn't real distance, so the commit refuses and points you at
stitching.
Reports, exports, and revisions
A per-condition breakdown—Floor / Wall / Border SF, LF, EA, total SF, SY, with and without waste—plus a combined materials buy list. Waste applies only in the report's order quantity, never to the live measured number, so the takeoff and the buy list stay honest about which is which. Export CSV, JSON, a real Excel workbook (Summary / By-sheet / Materials / Shapes-audit / By floor × room, full-precision cells, formula-shaped names kept inert text), print, or Marked Set PDF—a distribution-ready planset built entirely in your browser for a GC who will never install anything.
When the addendum lands, Revisions makes it data instead of archaeology: save a named revision at each bid revision, then compare any two as quantity deltas per condition, per sheet, and on the buy list, with a compare CSV. The compare is deliberately quantity-level rather than geometric—it tells you which numbers moved, not which wall did. Restore banks the live takeoff first, so it's never a one-way door.
Markups, seals, and RFIs
A separate layer the totals never count: revision clouds, callouts, text notes, highlighter
ink, images (upload a PNG/JPEG, or marquee a region of the plan to drop it back as a
floating screenshot—move, resize, and it burns into the marked set), and reusable stamps
(plank direction, seam direction, pattern origin—build your own, or import an .svg). Approval seals are the estimator's ink: click a committed takeoff to
approve it, and the Marked Set's cover gains a tally line—N estimator-approved · N
agent-marked — so a PM knows exactly how much of the set a person has looked at. The RFI
register turns any markup into a tracked question with status, priority, ball-in-court, and
cost/schedule impact flags, exporting as CSV/JSON and as an RFI schedule page in the marked set.
The Agent panel, in the browser
The same proposer/reviewer split as MCP without leaving the canvas: describe a takeoff in a
sentence and a model—yours, on your key, from your browser—works the sheet with the
app's own deterministic tools and stages dashed proposals you accept, correct, or reject. It
cannot invent geometry (propose_shapes rejects anything uncited) and it cannot set a scale.
To watch the loop with no AI account at all, run the keyless deterministic mock server in
scripts/.
A vector-sharp canvas
Past ~1.15× zoom times your display's pixel ratio, the visible region re-renders straight from the PDF vectors at your current zoom rather than magnifying a fixed bitmap, so fine callouts and hatching never blur—and it engages after a pause in the gesture, so a continuous zoom stays on the fast base layer while you're still moving. It overlays only what's on screen, so there's no full-sheet bitmap to hold. Dark view (☾) inverts the sheet pixels themselves—a true negative print, white linework on black, not a CSS filter—with hatches retuned, and exports follow it.
Yours, locally
Every drawing, scale, condition, markup, and RFI autosaves to your browser (IndexedDB + localStorage). Nothing is uploaded, there's no account, and there's no server in the default build. The flip side is stated plainly in the manual: storage is per browser, per origin, and clearing site data clears your work.
Everything above is the default and it's unchanged: open the page and you're an anonymous,
local-only user. A team on Google Workspace can optionally sign in to unlock a shared mode
instead: projects live as folders in the team's own Google Drive, the project list is
deep-linked from an existing Glide app, and material costs come from a synced
pricing.json. It's strictly additive—set nothing and it doesn't exist. The security posture
stays honest: still a plain static site, no secrets in the bundle, team-only because the
Google OAuth app is Internal to your domain, and the data sits in your own Drive. See
docs/GOOGLE_SETUP.md and
docs/GLIDE_INTEGRATION.md. A cloud deployment can also opt into
local-first sync (VITE_CLOUD_SYNC=1): annotations stay canonical in the browser and sync
to Drive in the background, so the canvas is instant and survives a flaky network—docs/SYNC_ARCHITECTURE.md.
OpenTakeoff can ask a vision model you provide to read things off the plan—starting with the drawn scale when a sheet's text doesn't state one (scans, rotated notes, image title blocks). Click AI in the toolbar and point it at an OpenAI-style endpoint (the default; local runtimes on your own machine speak it and need no key) or an Anthropic-style one, plus a vision-capable model id.
What's sent, and only when you click an AI button: one snapshot of the sheet region in question, plus the question—to your endpoint. Never the whole plan file, file names, project names, or your takeoff.
Nothing configured = nothing exists. Unconfigured builds add zero UI beyond the button and make zero AI network calls. No telemetry either way.
The answer is only ever a suggestion, landing in the same confirm-to-apply flow as a text-detected scale, with the calibrated guide bar shown on acceptance.
The key is stored in this browser's localStorage—use one you can revoke. Deployers:
VITE_AI_ENDPOINT/VITE_AI_MODEL/VITE_AI_PROVIDERbake team defaults, but never setVITE_AI_KEYon a public deploy—Vite inlines it into the shipped bundle.
What's in the box
Area | What you get |
Ingest | PDF, image, or |
Scale | Auto-detect the drawn note, calibrate from a known dimension, or verify one with a graded check—per sheet |
Measure | One-Click Area (vector flood + raster fallback — temporarily gated), Area, Rectangle, Linear, Curved Line, Surface Area, Count, Cut Out deducts, ⟂ Transitions, Zone check—imperial or metric |
Drawing aids | 45°/90° angle lock with |
Conditions | Color + CAD hatch per finish, waste %, ×N multiplier, wall height, border thickness, schedule import, browser-wide library |
Supporting Materials | Labor + subfloor type, coverage rate × basis (incl. figured seam LF) → rounded order quantities, trowel/roller presets, grout calculator |
Roll goods | Per-condition roll setup → lanes, seams, multi-roll splits, to-scale cuts with drag-to-reorder nesting, Roll Order LF + Rolls + figured Seam LF on every export |
Multi-sheet | Sheet gallery, tabs and side-by-side groups, Regroup, levels, stitching across a match line, PDF layer roles |
Report | Per-condition Floor/Wall/Border SF, LF, EA, SY with and without waste, plus the combined buy list; columns, grouping, saved templates |
Export | CSV, JSON, Excel (.xlsx), print, Marked Set PDF, RFI CSV/JSON |
Revisions | Save at each bid revision, compare quantity deltas per condition/sheet/buy list, guarded restore |
Markups | Clouds, callouts, notes, highlighter, images (upload or marquee screenshot), stamps, approval seals, RFI register—separate layer, never counted |
Voice | Push-to-talk takeoff commands, recognized on-device in WebAssembly; audio never leaves the browser — gated off the toolbar by default ( |
View | Light or dark (negative print)—sheet pixels inverted at draw time, exports follow |
Storage | IndexedDB + localStorage—client-only, nothing uploaded |
MCP server | 52 tools + browsable sheet resources on stdio, multi-document sessions ( |
Provenance | Every shape records its scale, its method, its confidence, and whether a person or an agent made it |
Capture (opt-in) | Bundled capture server banks each contributed takeoff as (geometry → label) training rows |
Deploy | One static build—Netlify, Vercel, GitHub Pages, Cloudflare Pages, S3, any static host |
The data layer — why this engine exists
Every finished takeoff is a set of expert decisions: this region gets this finish, at this waste, yielding these quantities. Done once, that's a bid. Recorded every time, it's a labeled dataset that does not currently exist—plan geometry paired with the finish an expert assigned it, which is the exact raw material for training a model that can do takeoff. Today that data evaporates the moment the bid goes out.
The thesis, stated so it can be attacked: markup is label. Professional takeoff software already stores every drawn region as vector geometry, and reconstructing those polygons reproduces the recorded quantities exactly—so two decades of estimating work is an exact, verifiable corpus rather than a noisy one. That claim is what the whole research program tests, and it's patent pending.
OpenTakeoff is the instrument that produces the corpus, with the collection path opt-in and auditable:
The Contribute button in the Report builds a derived-only payload—condition labels, shape roles, quantities, geometry normalized 0-to-1 against the sheet, and per-shape provenance (hand-traced versus machine-proposed, and whether a human corrected it, with the machine's original ring beside the fix). The builder is ~150 audited lines (
web/src/lib/contribute.js); the normative wire contract isdocs/CONTRIBUTION_SPEC.md.Never sent, enforced by a whitelist in the builder: the PDF or any render of it, file or sheet names, project/client names, markup text, absolute coordinates, scale values (only the scale's provenance—calibrated, detected, or standard), and edit timing beyond a creation stamp. One linkage is deliberate and disclosed: shapes carry opaque, locally-minted IDs so a re-contribution after an addendum supersedes rather than duplicates.
The bundled capture server (
capture/)—one stdlib-only Python file, no pip install—receives it on localhost and banks one training row per labeled shape, hash-gated so re-contributions never duplicate. v2 rows distinguish what the machine got right from what an expert had to fix, which is the signal that actually teaches a takeoff model. Point it at a synced folder with--mirrorand the corpus rides existing company storage sync, atomically.
python3 capture/capture_server.py # then, in the app's browser console:
# localStorage.opentakeoff_contribute_endpoint = "http://localhost:8787/contribute"Run OpenTakeoff as-is and none of this exists for you—nothing is captured, nothing leaves
your machine. Install it and every takeoff you choose to contribute compounds into an asset
you own. This is the open edition of the capture layer inside
Spline, the commercial Division 9 estimating system OpenTakeoff
was carved from, where capture runs ambient on autosave and commit instead of behind a button.
The row schema and the training angle are in capture/README.md.
The research program
OpenTakeoff is the open half of an applied-research program run by a working commercial flooring estimator who builds the AI his own department uses (Kentucky AI). The open-core boundary is the same one the better open scientific software draws: the measurement engine—rendering, scale, geometry, exports, the MCP server—is Apache-2.0 and stays open. The models trained on our own estimating archive are proprietary. You get a real tool with no seat licenses; the part only our data can build stays ours.
The research side is run as a lab, and the receipts are the point:
Parameter-efficient tuning, not pretraining. QLoRA adapters on open-weights bases (~0.1% of parameters trained), specialized from a verified bid archive—cheap enough to retrain when the data says retrain, small enough to ship. The flagship adapter predicts bid totals at 12.3% median absolute percentage error on a 51-project temporal holdout, against 62.8% for the untuned base; full method and honest caveats on the model card.
Verified labels in. Before a historical bid becomes training data it passes a dual-document verification gate: totals must reconcile between the bid workbook and the separately filed proposal, change orders only count when corroborated by an actual change-order document, and line-item arithmetic is recomputed and forensically checked. Unverifiable projects don't train.
Verifiable rulers out. Models are scored against temporally held-out projects—future bids, not a random split—with a geometry scorer whose own error floor is measured (0.4%), so a number can be attributed to model error versus measurement error.
Multi-seed replication. No result is promoted from a single training run; promotion requires seed replication with paired bootstrap confidence intervals, and the cross-seed spread gets published alongside the best seed.
Negative results are kept. The experiment ledger records what failed and why—an unfreeze recipe that destroyed detection, a vertical-specialist model that lost to the generalist's cross-vertical transfer—next to what worked.
Leak-audited before release. Identifiers are replaced before training, so the weights never see a real name, and every public artifact passes a differential red-team: adversarial extraction probes against the tuned model with the untuned base as control.
Sanitized artifacts—model cards, benchmark specs, papers—publish as they clear review: Hugging Face · kentucky-ai.com. The agent-side evaluation lives in OpenTakeoff Academy.
Run it / deploy it
To use it, all you need is a browser. To self-host, it's one static build you can drop anywhere—no backend, no database, no environment to stand up.
cd web
npm install
npm run build # → web/dist/ (static; host it anywhere)The repo ships a root netlify.toml, so the button is genuinely one-click. The same
web/dist/ works on Vercel, GitHub Pages, Cloudflare Pages, S3—anywhere that serves
static files. Running your own reverse proxy—nginx, Docker, Tailscale? Check
docs/SELF_HOSTING.md first—there's one MIME-type gotcha worth
knowing about. Deployment notes and the optional AI backend:
docs/DEPLOYMENT.md.
Fork it
Apache-2.0: fork it, change it, ship it—for your own crew or as the base of your own product. A fork is the unit of ownership here, and it's the unit of contribution: the same three steps give you a private instance and a branch to send back.
Fork on GitHub, then clone your fork.
Run it:
cd web && npm ci && npm run dev— the canvas is atlocalhost:5173, andnpm run checkis the exact CI gate (typecheck, lint, test, build).Put it on your own URL: the repo carries its
netlify.toml(baseweb, publishdist), so importing your fork into Netlify deploys with no settings; any static host works, anddocs/SELF_HOSTING.mdnames the one nginx gotcha. Your instance keeps every plan local exactly as the public one does.
Pull requests from a fork run the full CI with no secrets and a read-only token
(docs/DEPLOYMENT.md), so a green check on your fork is a green check here.
The codebase is deliberately small and readable, and the geometry libraries are pure so you can
lift them straight out:
What | Where |
Flood fill, face extraction, corner snap, raster fallback |
|
Scale detection, sheet helpers, polygon area |
|
Waste, square-yard, coverage → order quantity | |
Roll-goods lane and seam layout |
|
Persistence (IndexedDB + localStorage) | |
PDF / image / zip ingest | |
The canvas (one large component, ~90% of the app) | |
MCP server (imports the same libs) | |
Design tokens—source of truth for color and spacing |
Third-party integrations and downstream forks run on this engine today.
cd web && npm run check is the exact CI gate—typecheck, lint, test, build. Keep
oneclick.ts and sheets.ts free of React and DOM; that purity is what makes them reusable and
testable. Never commit real construction plans. See CONTRIBUTING.md and
AGENTS.md—the repo's own instructions for coding agents—plus the
user manual.
Contributing
The open work is architectural, and it's posted as RFCs with a stated finish line rather than a manufactured chore list. Currently open:
RFC #60—make One-Click Area genuinely great: face extraction, gap tolerance, confidence. Partially landed—a first slice merged in #179, contributed by @knmurphy and credited in the release notes—and the accuracy ceiling is still open.
RFC #87—the sheet graph: resolve room tags, schedules, legends, and detail callouts into one queryable graph with a citation per answer. Two phases shipped; revision clouds and detail-callout chains are open.
Anything labeled
rfcorflagship—a flagship is an open design-and-build challenge where multiple entries are welcome and the best one merges with credit.Smaller, fully-specified entry points are labeled
good first issue—they name the exact files. Claim one in a comment and go.
Ground rules are in CONTRIBUTING.md. The bar is a green npm run check plus
a test for anything touching the geometry libraries; tested PRs merge fast. CI also holds two
lines npm run check doesn't: every relative link and anchor in the docs must resolve
(node scripts/check-doc-links.mjs runs it locally), and web/bench/results.json must match
what the engine actually produces—an engine change carries its bench delta in the same PR. External
contributions are credited by name in the commit and the release notes—and because
opentakeoff-mcp publishes to npm off a mcp-v* tag, engine work you land ships to every
agent that pulls the package.
Found something exploitable? Report it through private vulnerability reporting rather than a public issue. SECURITY.md states the threat model up front—worth a read before reporting, since it explains what the trust boundary actually is for a client-only app and a local stdio MCP server, and what that does and doesn't make a vulnerability.
Tech stack
Frontend: React 18 + Vite 6, plain JSX
Drawing: raw HTML5 Canvas + SVG—no charting or canvas frameworks
Geometry: TypeScript (
oneclick.ts,sheets.ts), pure and unit-testedPDF rendering: pdf.js
Plan-set ingest: fflate (zip) + pdf-lib (image → PDF), lazy-loaded
Speech: transformers.js, whisper-tiny.en (q8 encoder + uint8 decoder) in a Web Worker—benchmarked against the alternatives in
docs/VOICE.mdMCP: TypeScript stdio server importing the web engine's own libraries
Storage: IndexedDB + localStorage—no backend required
Tests:
node --test+tsxNo paid dependencies. See THIRD-PARTY-NOTICES.md.
Status
A working tool used on real commercial bids, not a preview. The measuring engine is the production engine carved out of a commercial estimating system, and the same engine answers to a person at the canvas or an agent over MCP with the same math, the same scale gate, and the same provenance record. Named limits, so you don't find them the hard way: Snap is beta, revision compare is quantity-level rather than geometric, and the translated READMEs lag the English one. Issues and pull requests are welcome.
Who's building this
I run estimating for a commercial flooring company and build the AI that runs my department. OpenTakeoff is the open half of that work: the measuring engine, given to anyone—human or agent—who needs to read a building. The models trained on our own estimating archive stay ours, and the boundary is drawn in public so it can be held to account.
What makes the data worth anything is that it comes from bids that were actually submitted, won or lost, and reconciled against a separately filed proposal. That's also why the engine had to be free: a corpus is only as good as the number of real takeoffs that flow through the instrument producing it.
— Michael · Kentucky AI
Contact: research collaborations, data questions, press, or anything that is not a bug — research@kentucky-ai.com. Bugs and feature requests go in issues; security reports follow SECURITY.md.
License
Apache License 2.0—use it, fork it, ship it, build on top of it. See NOTICE for attribution.
Available Tools
42 toolsannotateA
Place an annotation on a sheet — a note ABOUT the work, never a measurement of it. Types: cloud and highlight take rect:[[x0,y0],[x1,y1]] (a revision cloud around an area, a highlight box over it), text takes at:[x,y], callout takes at:[x,y] plus target:[x,y] (the point its leader aims at), arrow takes from:[x,y] and to:[x,y] (tail and head — plank/seam direction, the markup flooring drawings use most; #150), bubble takes at:[x,y] plus optional r (a keynote/detail circle carrying centered text), dimension takes from:[x,y] and to:[x,y] (its two measured endpoints) and labels itself with the length between them at the sheet's scale — drawn as a dimension line with end ticks and the measurement centered. A dimension states a REAL length, so it is the one annotation the scale gate applies to: on an unscaled sheet it refuses exactly like the measure tools (set_scale first) rather than dressing a px figure up as feet. It still touches no quantity — a dimension is a note about a distance, not a takeoff line item.
Pass condition to attach the note to a finish tag, which is what makes it part of that SCOPE rather than a floating remark: it then wears the condition's colour on the canvas and in the marked-set PDF, and travels with it into the report. The tag is minted on first touch like one_click/measure_polygon, so you can annotate CPT-1 before anything is traced for it. Omit condition for a note about the sheet itself.
No review gate: the pencil-not-ink rule exists to stop an agent inventing geometry, and a cloud reading "verify substrate" is not geometry. It touches no quantity. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| r | No | Bubble radius (image px); omitted → the canvas default (2% of sheet width) | |
| at | No | Anchor point (image px) — text, callout, and bubble (the circle's center) | |
| to | No | Arrow head / dimension end (image px) | |
| from | No | Arrow tail / dimension start (image px) | |
| rect | No | Corners (image px) — cloud and highlight | |
| text | No | The note. A cloud with no text still reads as 'look here'; a bubble's text draws centered in the circle; a dimension appends it after the measured length | |
| type | Yes | cloud/highlight need rect; text/callout/bubble need at; callout also needs target; arrow and dimension need from + to | |
| sheet | Yes | Sheet name or number, as sheet_info reports it | |
| target | No | What a callout's leader line points at (image px) | |
| condition | No | Finish tag to attach this note to, e.g. 'CPT-1' (minted on first use). Omit for an unattached sheet note |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| note | Yes | |
| text | Yes | |
| type | Yes | |
| sheet | Yes | |
| condition | Yes | |
| length_lf | No | Dimension only: the measured length (real feet) the annotation will label itself with |
| condition_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries full responsibility for disclosing behavior, and it does so richly. It reveals the no-review-gate policy and the pencil-not-ink rationale, the coordinate system ('image px at render scale 2.0: PDF pt × 2, origin top-left, y down'), the scale-gate refusal for dimensions on unscaled sheets, and tag minting on first touch. This goes far beyond typical descriptions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence is purposeful: it covers the core purpose, each annotation type's specific parameters, behavior under scale gate, condition attachment semantics, and coordinate conventions. It is front-loaded with the main purpose and progressively adds detail in a logical flow. No redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complexity of 7 annotation types, 10 parameters, and interaction with scale/condition/review gates, this description is remarkably complete. It addresses all parameter roles, edge cases (unscaled dimension refusal, tag minting, condition color), coordinate system, and the tool's relationship to quantity takeoff. An output schema exists, so return values need not be spelled out, and the description is sufficient for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Despite 100% schema coverage, the description adds substantial meaning beyond the raw schema. It explains the geometric roles of rect for cloud/highlight, target for callout, from/to for arrow and dimension, and at for text/callout/bubble. It also clarifies that dimension labels itself with the measured length and appends text after it, which is not evident from the schema alone. This elevates the semantics significantly.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear verb and resource: 'Place an annotation on a sheet' and immediately distinguishes its purpose: 'a note ABOUT the work, never a measurement of it.' This contrasts with sibling measurement tools like measure_line and measure_surface. It also enumerates the specific annotation types, leaving no ambiguity about what the tool produces.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: annotations are for notes, not measurements, and the dimension type is singled out as the one annotation subject to the scale gate, with instructions to set_scale first. It also explains when to omit condition ('for a note about the sheet itself') and how condition attachment ties to a finish tag, referencing sibling tools like one_click/measure_polygon for tag minting. This gives clear decision support.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
apply_rulesA
Re-run the correction rules the takeoff arrived with (#207) — the lessons an estimator TAUGHT the canvas (#88): "every room like this loses the mechanical chase." A rule is a deterministic predicate (enclosed linework islands under a size cap, inside the rule's condition's rooms), never a re-prompt. Evaluation is the same pure rules.ts engine the canvas Preview runs; the commit is the one batch the canvas's Apply makes — ONE journal entry, undo_last takes the whole batch back. Everything lands reviewed: false (this server has no review gate), and the reply's per-rule disclosure — what each rule produced, what was skipped, with ids — IS your preview: read it, then view_sheet overlay:true. Idempotent by construction: any candidate an existing deduct already covers is dropped by the engine, so re-running after new rooms commit is the intended workflow and never double-deducts. Rules arrive ONLY via import_takeoff (minting a new rule is an estimator's correction and stays behind the canvas's human Preview→Apply gate); with none imported this refuses. Pass sheet to scan one sheet; omit it to scan every sheet holding the rules' rooms. Uncalibrated and scanned-raster sheets come back in skipped_sheets, named.
| Name | Required | Description | Default |
|---|---|---|---|
| sheet | No | Scan only this sheet (default: every sheet holding the rules' conditions' rooms) |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | Yes | |
| rules | Yes | |
| committed | Yes | Deducts committed across all rules — 0 is a result (idempotence) |
| skipped_rules | Yes | Rules not evaluated, named — never silently dropped |
| skipped_sheets | Yes | Sheets that could not be scanned (uncalibrated, or scanned raster with no linework mask) |
| total_deduct_sf | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description carries full burden. It discloses key behavioral traits: deterministic (not a re-prompt), uses the same rules.ts engine as Preview, commits as a single batch reversible by undo_last, marked reviewed:false, includes per-rule disclosure in the reply, and is idempotent. It also notes refusal when no rules are imported and skipped_sheets for uncalibrated/raster sheets.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence contributes essential behavioral context for a tool with no annotations and nuanced semantics. It is front-loaded with the core purpose then layers details about evaluation, commit, idempotency, import gate, and skipped sheets, making it dense but not wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description fully covers the tool's behavior for its single-parameter interface: it explains the default scope, output behavior (per-rule disclosure serving as preview), error conditions (refuses without imports), and edge cases (skipped sheets). An output schema exists, and the description complements it by describing the meaning of skipped_sheets and the preview nature of the reply.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the tool has only one optional 'sheet' parameter. The description restates the schema ('Pass sheet to scan one sheet; omit it to scan every sheet holding the rules' rooms') without adding new meaning, so it stays at the baseline for high schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool re-runs correction rules, identifying them as deterministic predicates ('enclosed linework islands under a size cap') and ties them to the canvas's Apply action. It distinguishes from siblings by noting rules arrive only via import_takeoff and that undo_last can reverse the batch.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use guidance is provided: re-running after new rooms commit is the intended workflow, and it is safe due to idempotency. The description also contrasts with alternatives: rules only via import_takeoff, preview via view_sheet, and undo via undo_last.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
count_marksA
The COUNT TAKEOFF in one deterministic call — no seeds, no model, seconds: census every VALUE-ANNOTATED mark tag on the plan-role sheets, counted per schedule mark, committed as EA markers when asked. The identity rule is the annotated-device drafting pattern: a device is drawn as its mark tag with a value under it ("S1" over "200" — CFM on air devices, GPM on fixtures, a rating on equipment), so a tag WITH a paired value counts, a tag inside a schedule table's own region is a row label (excluded, tallied), and every other occurrence is WITHHELD with a reason and coordinates — a tag amid linework but unvalued may be a real device (view_sheet it), a bare tag is probably a note mention. Marks default to the set's schedule row keys (a compound row "R1 / E1" answers for R1 AND E1; each mark cites its row), or state them: {marks: ["S1","R1"]}. The complement to sweep_schedule_row: THAT tool is for marks drawn ON their marker with no value (finish tags in bubbles) and matches geometry; this one is for annotated devices and needs no fingerprint at all. Refusal-honest: scans refuse (no text layer), a set with no mark-shaped rows refuses unless marks are stated, non-plan sheets are skipped with the role that excused them. commit: true commits every counted occurrence under its mark's own tag — ONE undo step for the whole census, schedule citation on origin. Counts are scale-free (EA) — no set_scale needed. Then AUDIT: view_sheet {overlay: true} where the markers landed, and read every withheld entry — a withheld item you ignore is a hole in the bid. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| marks | No | The marks to census, e.g. ["S1", "R1"] — omit to take them from the schedule tables' row keys | |
| commit | No | Commit every counted occurrence as one EA count marker under its mark (withheld/excluded never commit) |
Output Schema
| Name | Required | Description |
|---|---|---|
| marks | Yes | |
| total | Yes | All counted instances across every mark |
| skipped | Yes | |
| complete | Yes | |
| per_sheet | Yes | |
| excluded_in_tables | No | Tag occurrences inside a schedule table's own region — row labels, never instances |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Since no annotations are provided, the description fully carries the burden of behavioral disclosure. It explains deterministic behavior (no seeds, no model), counting rules (tags paired with values count, schedule table row labels are excluded), withholding logic with reasons, coordinate system (image px at render scale 2.0), commit behavior (one undo step), and audit steps. This exceeds minimal transparency expectations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is verbose but every sentence contributes value—covering counting rules, exclusions, refusal conditions, coordinate systems, and audit instructions. It is front-loaded with the primary purpose and flows logically through usage, behavior, and parameters. While not ultra-concise, the density justifies the length; a 4 reflects that it could be tightened without losing critical information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is complete for an agent to invoke the tool correctly. It covers what counts, what's excluded, refusal scenarios, coordinate systems for marker placement, commit semantics, and follow-up audit steps. Given the presence of an output schema (not detailed here) and the complexity of the tool, the description leaves no critical gaps an agent would need to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although the schema includes descriptions for both parameters (marks and commit), the tool description adds substantial meaning. It explains the default behavior for marks (taken from schedule row keys), how compound rows answer for multiple marks, and clarifies that commit commits every counted occurrence under its mark. This enriches the schema guidance, providing deeper semantic context than the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: it 'census every VALUE-ANNOTATED mark tag' and counts per schedule mark, distinguishing it from sibling tools like sweep_schedule_row. The verb 'census' and resource 'value-annotated mark tags' are specific and actionable, making it immediately clear what the tool does and how it differs from alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly contrasts with sweep_schedule_row, explaining that this tool is for annotated devices with values, while the sibling is for marks without values. It also clarifies when to provide explicit marks versus relying on defaults from schedule row keys, and notes refusal conditions (no text layer, no mark-shaped rows). This gives the agent clear guidance on when to use this tool versus others.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
cut_outA
Cut a REAL hole in a committed floor_area shape (#206) — the way the canvas cuts one (#137): the same lib/cutout.js boolean subtract, so the two surfaces can never disagree about what a hole holds. The parent keeps its outer ring plus the reconciled hole(s) (verts_norm_holes), its computed nets for real — N cuts compose, overlap between cuts never double-deducts (set subtraction), a hole ADDS perimeter — and the deduct commits carrying cuts_shape_id so the report and legend read the reconciled number, never a second arithmetic pass. This is the verb for a column, a floor drain, an island of casework INSIDE a room; an independent measure_polygon role:"deduct" stays the tool for a deduction that isn't a hole in one parent. Refusal over guessing: the ring must sit FULLY inside the parent's outer ring (an edge-crossing cut is a boundary correction — edit_shape the parent instead), and a cut that would erase the parent or split it in two refuses whole (trace the pieces as rooms). One journal entry — undo_last restores parent and hole together; delete_shape on the deduct later reverts the cut too (a multi-cut parent rebuilds from the chain's pristine snapshot minus the survivors). AN OPEN RUN IS CLIPPED, NOT SUBTRACTED: wall tile (surface_area) and base/transitions (linear) are polylines traced in plan, so the ring removes the stretch it covers, the run keeps its id and takes what survives, and a cut through the MIDDLE leaves the far side as its own shape (same condition, same height) — quantities ride the surviving length, which is exact, since wall SF is LF × height and a border's SF is LF × thickness. No deduct is minted for a run: there is no area for one to sit on, and a deduct's SF counts against the FLOOR total a run never fills. A ring that misses the run, one that swallows it whole (delete_shape it), and a curved run (its verts are control points) all refuse. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| verts | Yes | The ring, image px — fully inside the parent for an area; over the stretch to remove for a run | |
| parent_shape_id | Yes | A committed floor_area shape id, or an open run (surface_area / linear) to clip (list_shapes) |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | Yes | |
| holes | No | Area parent: holes the parent now carries |
| pieces | No | Run parent: every stretch that survived the cut — more than one when the ring fell in the middle |
| hole_sf | No | Area parent: what this cut actually removed from the parent's net — 0 when the ring fell entirely inside an existing hole |
| shape_id | No | Run parent: the run that was clipped — it keeps its id and takes the first surviving stretch |
| parent_net | No | |
| removed_lf | No | Run parent: length the cut took out |
| removed_sf | No | Run parent: the SF that rode on that length |
| measure_role | No | Run parent: surface_area or linear |
| deduct_shape_id | No | Area parent: the reconciled deduct — carries cuts_shape_id; totals skip it (the parent nets the hole) |
| parent_shape_id | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden, and it delivers richly: it explains the boolean-subtract mechanism, composition of cuts, set subtraction preventing double-deduct, perimeter additions, journaling behavior (undo_last restores parent and hole together), and the reversal via delete_shape. It also discloses coordinate conventions and run-clipping behavior, giving an agent deep insight into the tool's side effects and constraints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is quite long, but every sentence carries technical nuance essential for correct invocation, such as composition rules, journaling, and refusal conditions. It is front-loaded with the core purpose, though the parenthetical references (#206, #137) and some repetition about runs could be trimmed without losing meaning. Minor over-verbosity prevents a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, no annotations, and an existing output schema, the description is remarkably complete. It covers all invocation parameters, coordinate systems, behavioral rules, edge cases, and refusal conditions, so an agent can correctly determine when and how to call it without needing external information.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
While the schema covers both parameters, the description adds substantial meaning: verts are specified as image px at render scale 2.0 with origin top-left, y down, and parent_shape_id can be either a committed floor_area or an open run. It also explains how the ring interacts with runs (clipped, not subtracted) and lists refusal scenarios, going well beyond the schema's short descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Cut a REAL hole in a committed floor_area shape' and explicitly contrasts with measure_polygon's 'deduct' role for deductions that are not holes in a parent. It also distinguishes from edit_shape for edge-crossing boundary corrections. This makes the tool's purpose unambiguous and separates it from siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use this tool: for a column, floor drain, or island of casework inside a room; and when to use alternatives: measure_polygon role 'deduct' for independent deductions, edit_shape for boundary corrections, and delete_shape for cuts that swallow a run whole. It also covers the clipped-run case and refusal conditions, providing clear routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_shapeB
Remove a committed shape by the id returned when it was committed. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| shape_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | Cutout interplay (#206), when it applies: the parent's cut was reverted, could not be rebuilt, or reconciled deducts were orphaned by a parent delete |
| deleted | Yes | The removed shape's id |
| shape_count | Yes | Committed shapes remaining |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It does not disclose destructive nature beyond 'Remove', nor does it mention reversibility, permissions, or error behavior. The unrelated coordinate information does not contribute to behavioral transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The first sentence is concise and effective, but the second sentence about coordinates is unrelated to the tool's parameters and adds noise, violating the 'every sentence earns its place' principle.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having an output schema, the description doesn't explain operation outcomes, idempotency, or side effects. The tool is simple but the description is incomplete and includes irrelevant coordinate context that distracts from core functionality.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description adds meaning to shape_id by specifying it is the id returned at commit time, which is not in the schema. However, the coordinate sentence is irrelevant and could confuse parameter expectations.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states the verb 'Remove', the resource 'committed shape', and the identifier 'id returned when it was committed'. It distinguishes from siblings like edit_shape by focusing on deletion of committed shapes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool versus alternatives. The description provides no context about when deletion is appropriate, prerequisites, or exclusions relative to sibling tools like cut_out or edit_shape.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_verdictA
Lift an agent verdict mark by id (mark_verdict's reply, or list_annotations verdicts[]). Agent marks only: the estimator's APPROVED seal is human ink and is refused — the same line edit_shape holds on reviewed shapes. Journaled like every mutation, so undo_last re-seats a lifted mark exactly where it was.
| Name | Required | Description | Default |
|---|---|---|---|
| verdict_id | Yes | Record id from mark_verdict or list_annotations verdicts[] |
Output Schema
| Name | Required | Description |
|---|---|---|
| deleted | Yes | The lifted record's id |
| verdicts_remaining | Yes | Approval-family records still on the takeoff (both actors) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the burden of behavioral disclosure. It states the operation is a mutation ('Journaled like every mutation'), reveals that it refuses human APPROVED seals, and explains that undo_last can re-seat a lifted mark. This is substantial and goes beyond the basic schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences with no filler. It front-loads the action, then packs in the essential details: source of the ID, restriction on human seals, and undo/redo behavior. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with an output schema and no annotations, the description is remarkably complete. It covers purpose, valid ID sources, exclusivity of agent marks, the mutation/journaling behavior, and reversibility via undo_last. Nothing critical is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already fully documents verdict_id with the same source reference, but the description adds important semantic context by clarifying that only agent marks are valid and that human APPROVED seals are refused. This helps the agent understand which IDs are acceptable.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource: 'Lift an agent verdict mark by id', and immediately tells the reader where to find valid IDs ('mark_verdict's reply, or list_annotations verdicts[]'). This clearly distinguishes it from sibling tools like delete_shape or mark_verdict.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage context: only agent verdict marks can be lifted, while the estimator's APPROVED seal is refused. It also mentions the undo_last journaling behavior, giving the reader practical when-to-use and what-to-expect guidance, though it does not explicitly name alternative tools for comparison.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
derive_baseA
Mint the wall base from committed rooms (#148) — the estimator's most mechanical derivation: base LF = room perimeter − stated door openings. For every floor_area shape of source_condition, commits ONE linear shape under condition (e.g. 'RB-1') tracing that room's boundary, quantified NET of the openings you state per room. The openings are YOUR claim to make — look at the doors with view_sheet, state {shape_id, lf} per room (repeat a shape_id to stack openings); the tool never guesses, and your claim is recorded on origin.derived (from_shape_id, gross_lf, openings_lf). All-or-nothing: an unknown shape_id, a negative lf, or openings meeting a room's whole perimeter refuses the call before anything commits. The whole derivation is ONE undo step. Deriving onto the source condition is refused — base lands on its own tag.
| Name | Required | Description | Default |
|---|---|---|---|
| openings | No | Stated openings per room — omit for gross perimeters | |
| condition | Yes | Finish tag the base commits under (minted on first use), e.g. 'RB-1' | |
| source_condition | Yes | Finish tag whose floor_area rooms the base derives from, e.g. 'CPT-1' |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | Yes | |
| rooms | Yes | |
| total_lf | Yes | Sum of net_lf across rooms |
| committed | Yes | |
| condition | Yes | The tag the base committed under |
| source_condition | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must fully disclose behavior. It does: commits one shape per room, records the claim on origin.derived (from_shape_id, gross_lf, openings_lf), never guesses, all-or-nothing atomicity, one undo step, and refusal to derive onto source condition. This is exceptionally transparent and leaves no ambiguity about side effects.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence earns its place. It front-loads the core purpose and formula, then methodically covers workflow, data recording, validation, undo behavior, and constraints. There is no fluff or repetition; the structure mirrors the natural sequence of using the tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex tool with 3 parameters, no annotations, and an output schema (not shown), the description is highly complete. It covers the full lifecycle: prerequisites (committed rooms), input specification (openings per room), side effects (commit, recording on origin.derived), failure modes (all-or-nothing refusal), and constraints (own tag). Nothing necessary is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Despite 100% schema coverage, the description adds significant meaning beyond the schema: it explains how to stack openings by repeating shape_id, states that openings meeting a room's whole perimeter are refused, and clarifies the 'claim' semantics (your claim to make, recorded on origin.derived). This enriches the raw parameter definitions substantially.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description precisely states the tool's function: 'Mint the wall base from committed rooms (#148)' with a clear formula (base LF = room perimeter − stated door openings) and details that it commits one linear shape per room. This distinguishes it from sibling tools like measure_polygon or list_shapes by emphasizing the derivation/commit workflow and the 'base lands on its own tag' behavior.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear workflow context: use view_sheet to inspect doors, then state openings per room. It also states when not to use it (deriving onto the source condition is refused) and describes all-or-nothing validation. However, it doesn't explicitly name alternative tools for manual measurement or other derivation types, so it stops short of full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
derive_transitionsA
Mint the transition where two finishes MEET (#202) — the derivation that follows derive_base, and the line an estimator draws by hand on every job. Pass the two finish tags and the tag the transition commits under (e.g. condition_a 'CPT-1', condition_b 'PT-1', condition 'T-1'), and every committed room of each is compared against every committed room of the other.
WHAT THE GEOMETRY ACTUALLY IS, because it decides what you get back: flood-traced rooms DO NOT SHARE EDGES. A trace fills to the wall linework, so two rooms across a partition are separated by four to eight inches of nothing — testing for a shared edge finds zero transitions on a real planset. What is there is proximity, in two flavours that mean completely different things:
• BUTT JOINT — the two rings run together inside ONE open space (a lobby that changes from carpet to tile with no wall between). The transition IS that run, and it commits as a linear shape under your tag, origin.derived naming both parent shapes and the measured gap.
• WALL-SEPARATED — the rings run parallel across a partition. The rooms are adjacent, but the transition is NOT the shared wall: it is a threshold, in the doorway, and NOTHING in the trace record says where the doorway is (the flood engine seals openings and reports how MUCH boundary it synthesised, never where). Committing 34 LF of threshold because two rooms share 34 LF of wall would be a wrong bid with a machine's confidence behind it. These come back in withheld — measured, with their length, their gap in inches, and an at point — as questions you answer by LOOKING (view_sheet at at, then measure_line or place_count the threshold yourself). The symbol_sweep doctrine: a near-match is never a silent commit and never a silent drop.
Tuning: max_gap_in (default 12) is how far apart two rings can be and still count as adjacent at all — raise it for thick walls, and every extra inch turns more of the plan into wall_separated questions, never into committed LF. min_run_in (default 12) drops corner artifacts. The butt-joint threshold is fixed at one inch and is not a knob: "these two finishes touch" is not a judgement call.
All-or-nothing, like derive_base: an unknown tag, a transition landing on either source tag, the same tag twice, or a sheet without a scale refuses the whole call before anything commits. The whole sweep is ONE undo step. After it, LOOK — view_sheet {overlay: true} over each run — before trusting total_lf. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| condition | Yes | Finish tag the transitions commit under (minted on first use), e.g. 'T-1'. Must differ from both sources | |
| max_gap_in | No | How far apart two rings can be and still count as adjacent, in inches (default 12 — a thick partition). Wider only produces more wall_separated QUESTIONS, never more committed LF | |
| min_run_in | No | Shortest run worth reporting, in inches (default 12) — below this is a corner where two rooms clip, not a transition | |
| condition_a | Yes | First finish tag, e.g. 'CPT-1' — its committed rooms are walked, and runs are traced along their boundaries | |
| condition_b | Yes | Second finish tag, e.g. 'PT-1' |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | Yes | |
| runs | Yes | |
| between | Yes | The two finish tags |
| total_lf | Yes | Sum of committed run lengths — butt joints only |
| withheld | Yes | Adjacency across a wall: real, measured, and NOT committed — the transition there is a threshold at a doorway this cannot locate |
| committed | Yes | |
| condition | Yes | The tag the transitions committed under |
| withheld_lf | Yes | Shared-wall length held back — never part of total_lf |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and goes far beyond it: it discloses all-or-nothing atomicity ('refuses the whole call before anything commits'), the single-undo-step behavior, the committed vs. withheld output split, coordinate system specifics (image px at scale 2.0, origin top-left, y down), and failure conditions. It also explains the critical geometric nuance that flood-traced rooms do not share edges, which materially affects what the tool returns.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long, but every paragraph earns its place for a tool with complex geometric behavior. It opens with a one-sentence purpose, then systematically covers geometry, tuning, failure modes, and post-conditions. The structure uses clear signposts ('WHAT THE GEOMETRY ACTUALLY IS', 'Tuning:', 'All-or-nothing') to make the length navigable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is remarkably complete for a complex tool, covering input semantics, output semantics (committed vs. withheld, total_lf), coordinate system, failure conditions, undo behavior, and recommended follow-up actions. Since an output schema exists, it doesn't need to enumerate return fields, but it still explains the meaning of those fields and the operational context, exceeding the minimum needed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds substantial meaning beyond the schema: it explains that condition is 'minted on first use', elaborates on max_gap_in's trade-off ('Wider only produces more wall_separated QUESTIONS, never more committed LF'), clarifies min_run_in as a corner artifact filter, and contextualizes condition_a/condition_b with examples. This is exactly the kind of semantic enrichment that helps an agent choose correct parameter values.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens by naming the exact operation: 'Mint the transition where two finishes MEET (#202)', which is specific and action-oriented. It also distinguishes itself from siblings by explicitly calling out that it 'follows derive_base' and by referencing the 'symbol_sweep doctrine' to differentiate its behavior from symbol_sweep.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly establishes when to use this tool—after derive_base, when two finish tags require a transition—and gives practical tuning guidance ('raise it for thick walls'). It also directs users to alternatives for follow-up actions ('view_sheet at `at`, then measure_line or place_count the threshold yourself'), but it does not provide explicit 'when not to use' exclusions, so it falls short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
detect_roomsA
Batch room detection: reads every room-number label off the sheet's text layer (e.g. "134", "OFFICE 101") and runs One-Click at each — one call instead of read_sheet_text + reasoning + N one_click calls. An OCR'd scan (text layer, no vector linework) floods the rendered pixels instead (#154), disclosed per room and on origin as raster_traced. A seed is only reported as a room once it survives three gates, and everything skipped is counted and reasoned in withheld — never dropped silently, because a room the tool tells you it skipped is a question you can ask, while one it hides is a hole in a bid. The gates: a flood that leaked or landed in dense linework never becomes a region; two labels flooding the SAME region commit once (the extra labels ride on merged_labels — double-counting an area is the worst failure an estimating tool has); and a flood that is enclosed and clean but smaller than min_area_sf is a room-number bubble, a door swing, or a wall cavity rather than a room. Every room floods through the SAME sealed engine a single one_click runs (RFC #60 — feet-true gap sealing, door-swing wedges, the minimum-passage rule), so a batch detection and a click at the same seed measure the same square footage; each room carries the engine's account of its own trace (confidence + confidence_factors, gap_sealed_px, door_wedges, min_pass_px/min_pass_delta), and the same account rides origin on everything committed. Confidence is a review prioritizer, never a verification — a low-confidence room is a view_sheet {overlay: true} audit prompt, not a fact to bid from. With the sheet's scale set, returns area_sf/perimeter_lf per room. Every committed room carries the room number it was traced from as the shape's label, so a sweep arrives already sliced by room — that field is what the Report's per-room grouping and the workbook's floor × room tab read, and it is the one thing about a batch that cannot be recovered downstream if it is dropped. TO COMMIT, choose the honest source of the finish tag: assign_from_schedule: true routes every room through its OWN room-finish schedule row and commits each under the FLOOR finish that row states — when a schedule exists in the set, THIS is the default move, because one agent-chosen tag across N rooms flattens real finish variety into a wrong bid; condition commits every room under that one stated tag (only right when the rooms genuinely share it; role "deduct" makes them subtract). Without a scale, returns px-only quantities per room and commits nothing — the plausibility floor needs real units, so it only applies once a scale is set. A batch commit is NOT finished until you have LOOKED at it: view_sheet {overlay: true}, audit every ring against the walls, fix misses with edit_shape / delete_shape — before the totals mean anything. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| role | No | floor_area | |
| sheet | Yes | ||
| layers | No | Override the sheet's classified layer roles for THIS call (see sheet_info.layers) | |
| condition | No | Finish tag to commit every detected room under (minted on first use). Mutually exclusive with assign_from_schedule | |
| min_area_sf | No | Plausibility floor: enclosed non-bubble regions smaller than this are withheld as cavities, not rooms. Default 5 SF — below any real finished space (a broom closet is ~10 SF). Lower it to inspect what was skipped. | |
| sensitivity | No | Fill sensitivity, the same knob the canvas has: 0 strict (hatch/light linework always blocks), 0.5 balanced (default), 1 aggressive (crosses more hatch, tolerates more growth). Raise it when a flood stops short at hatching INSIDE the room; verify the grown ring with view_sheet overlay before committing | |
| return_verts | No | Include each traced polygon's vertices (image px) | |
| assign_from_schedule | No | Commit each room under the FLOOR finish its OWN room-finish schedule row states (resolve_tag's chain, per room): the citation rides origin.assignment, and rooms the schedule cannot answer for — no row, no FLOOR cell, a compound cell like "CPT-1/VCT-1" — are returned in unresolved[] with reasons and seeds instead of committed under a guess. Needs the sheet's scale and a room-finish schedule in the working set (merge the schedule sheet in with load_plan first). Mutually exclusive with condition |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | Human-readable summary of what was withheld, when anything was |
| rooms | Yes | |
| warning | No | Preview mode (no scale): why quantities are unavailable and what to do |
| detected | Yes | Count of cleanly-detected rooms — may be fewer than the labels found on the sheet |
| withheld | Yes | What detection skipped and why — a withheld room is a question the caller can ask; a silently dropped one is a hole in a bid |
| unresolved | No | Assign mode only, empty array included: [] is the positive claim that every detected room resolved against its own schedule row |
| multiple_scales | No | Several DISTINCT scale notes on this sheet (#153) — rooms inside an enlarged viewport may be figured at the wrong scale |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses extensive behavioral traits: internal gates, withheld reasoning, merged_labels, 'never dropped silently', the same sealed engine, coordinate space, and commit requirements. It also clarifies confidence as a review prioritizer, not verification.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is an extremely long, single paragraph with many parentheticals and rhetorical asides (e.g., 'double-counting an area is the worst failure'). Although front-loaded with purpose, it is not appropriately sized; each sentence does not strictly earn its place due to verbose explanatory philosophizing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex tool with 8 params, nested layers, and output schema, the description covers input source, detection process, error handling, commit semantics, scale dependence, post-commit audit, and output coordinates. Output schema handles return values, so no gap in completeness.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Even with 75% schema coverage, the description adds significant meaning beyond the schema: role 'deduct' makes them subtract, min_area_sf plausibility floor, sensitivity usage, assign_from_schedule vs condition mutual exclusivity, and scale requirement for sheet. It compensates for missing schema descriptions and enriches parameter understanding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Batch room detection: reads every room-number label off the sheet's text layer' and explicitly contrasts with 'read_sheet_text + reasoning + N one_click calls', clearly distinguishing it from siblings. The verb+resource+scope is specific and immediately clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use guidance: 'when a schedule exists in the set, THIS is the default move' vs condition, and notes 'Without a scale... commits nothing'. It also instructs to audit with view_sheet and fix misses, providing clear context for use cases and exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
duplicate_conditionA
Twin a condition — the same finish measured somewhere else, with its own supporting materials. One finish in two areas is not two conditions and it is not one either: the same sheet goods over a slab and over a raised deck take the same field material and different preparation underneath (one wants a moisture barrier, the other a primer and a different adhesive). The twin arrives carrying the original's whole materials list and keeps FOLLOWING it — change a coverage rate on the original and every twin that has not touched that row gets it; edit a row on the twin and only THAT row stops following. label is REQUIRED and becomes the tag suffix ('CPT-1' + 'Level 2' → 'CPT-1 – Level 2'), because every tool in this server resolves a condition by finish tag and takes the FIRST match: two conditions sharing a tag would make one permanently unreachable, and a takeoff re-import collapses them last-wins. A label already in use is refused rather than de-collided. No takeoffs come along — measure the new area against the returned condition_id. Reversible with undo_last; use split_condition to end the inheritance permanently.
| Name | Required | Description | Default |
|---|---|---|---|
| label | Yes | What makes this one different, usually the area: 'Level 2', 'Building B', 'Phase 2' | |
| condition | Yes | Finish tag of the condition to twin, e.g. 'CPT-1' |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | Yes | |
| condition | Yes | The twin's finish tag — base tag + the label, e.g. 'CPT-1 – Level 2' |
| family_id | Yes | Shared by every variant of this finish — survives a split |
| variant_of | Yes | The condition whose material rows this one follows |
| condition_id | Yes | The TWIN — measure the new area against this |
| variant_label | Yes | |
| inherited_rows | Yes | Material rows copied, all still following the original |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Despite no annotations, the description thoroughly discloses behavioral traits: the inheritance propagation rule ('change a coverage rate on the original and every twin that has not touched that row gets it'), the label uniqueness requirement with rationale (first match wins, duplicate tags cause unreachable conditions), refusal of duplicate labels, and reversibility. This far exceeds a simple 'duplicates a condition'.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is lengthy but every sentence provides valuable information, covering concept, examples, constraints, and alternatives. It is logically structured, starting with core purpose and layering detail, with no redundant or filler content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
It addresses why the tool exists, the inheritance behavior, labeling pitfalls, what is not carried over ('No takeoffs come along'), and how to reverse the action. It also indicates the return value (condition_id) and is complemented by an output schema, making it complete for a complex tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
While the schema defines 'label' and 'condition' with brief descriptions, the description adds critical semantics: label becomes a tag suffix with a concrete example ('CPT-1' + 'Level 2' → 'CPT-1 – Level 2') and must be unique. It also clarifies that condition is the finish tag the twin follows, providing much deeper meaning than the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Twin a condition — the same finish measured somewhere else, with its own supporting materials,' providing a specific verb and resource. It clearly distinguishes from siblings like split_condition, which ends inheritance, and edit_condition, which modifies a condition.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives a concrete scenario for when to use the tool ('One finish in two areas... same sheet goods over a slab and over a raised deck...') and explicitly names alternatives: 'Reversible with undo_last; use split_condition to end the inheritance permanently.' It also instructs the user to 'measure the new area against the returned condition_id,' guiding post-invocation steps.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
edit_conditionA
Set a condition's quantity knobs — waste %, multiplier, height_ft (the H knob measure_surface quantifies against), and/or roll_setup (the roll-goods opt-in: seams and order footage figured from the committed rooms, #147). takeoff_summary emits waste-adjusted *_net order quantities and a per-condition multiplier, and every export carries both, but conditions minted through the measure tools start at waste 0 / multiplier 1 — without this tool an agent's takeoff always ships net === gross (#131). waste_pct is the estimator's cut-waste percentage (carpet commonly 5–10); multiplier scales every quantity on the condition (×N identical floors — takeoff_summary applies it before waste). condition must resolve to an EXISTING finish tag — a typo'd tag errors rather than minting an empty condition (the edit_materials remove/patch rule, not its add rule: these knobs mean nothing on a condition that doesn't exist yet). No review gate — quantity config, not traced geometry; undo_last reverses a call in one step (both knobs snapshotted together, restored verbatim).
| Name | Required | Description | Default |
|---|---|---|---|
| condition | Yes | Finish tag of an existing condition, e.g. 'CPT-1' | |
| height_ft | No | Wall height in feet — the canvas's H knob; measure_surface quantifies traced LF × this | |
| waste_pct | No | Waste percentage applied to net order quantities, e.g. 10 for 10% | |
| multiplier | No | Quantity multiplier (×N identical areas). Note: the canvas treats 0 as 1, so 0 is rejected here rather than silently meaning 'off' | |
| roll_setup | No | Roll-goods opt-in (#147): presence of a setup is what makes the condition roll goods — seams figured, cuts packed, order footage beside the measured quantities. Same-material partial edits patch the existing setup; null opts out. The reply echoes the figured order (cuts, order_lf, rolls, order_qty) whenever floor shapes exist on scaled sheets, and export_report's roll_goods block carries the same rows |
Output Schema
| Name | Required | Description |
|---|---|---|
| roll | No | The figured order (same row export_report's roll_goods carries) — present when the roll-goods condition has floor shapes on scaled sheets |
| condition | Yes | The finish tag passed in |
| height_ft | No | The condition's wall height after this write — present once set (measure_surface multiplies traced LF by it) |
| waste_pct | Yes | The condition's waste % after this write |
| multiplier | Yes | The condition's quantity multiplier after this write |
| roll_setup | No | The condition's roll-goods setup after this write — present while opted in |
| condition_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully carries the behavioral transparency burden. It discloses downstream effects (takeoff_summary emits waste-adjusted *_net quantities, exports carry both), error behavior on typo'd tags, roll_setup presence as opt-in with null opt-out, reply echoing figured orders, and undo_last reversing with a snapshot. No contradiction with structured data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence carries value. It is front-loaded with the primary purpose, then elaborates on each parameter, edge cases, and interactions. Examples like 'carpet commonly 5–10' and issue references add specificity without padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
It covers prerequisites, side effects, output behavior (reply echoes figured order), interaction with undo_last, and roll_setup nested options. Even with an output schema present, the description provides essential context about when and how quantities are applied, making it comprehensive for an agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds significant semantic meaning: waste_pct is the estimator's cut-waste percentage (carpet 5-10), multiplier scales before waste, height_ft is the H knob measure_surface quantifies against, and roll_setup presence is opt-in with partial edits patching the existing setup. This goes far beyond the raw schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Set a condition's quantity knobs' and enumerates the specific knobs (waste %, multiplier, height_ft, roll_setup), clearly identifying the resource and action. It also distinguishes from siblings like edit_materials by referencing its remove/patch rule and from measure tools by noting the default starting values.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states when to use the tool: after measuring, to adjust quantity knobs that otherwise default to waste 0/multiplier 1, making takeoff net===gross. It also provides prerequisites (condition must resolve to an existing finish tag) and mentions alternatives like undo_last for reversal and contrasts with edit_materials add rule.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
edit_materialsA
Add, remove, or patch supporting-materials rows on a condition — the coverage-rate lines that turn a measured area/length/count into an order quantity (adhesive at N sf/gal, grout at N lf/bag, …), matching the canvas's per-condition Supporting Materials panel. Each row is {name, per, basis, unit, round, note}: quantity = the condition's basis total (area/linear/count/seam_lf) ÷ per, rounded up to whole purchase units unless round:false. basis "seam_lf" is the one basis that is FIGURED rather than measured: it is the length where two cuts meet on the floor, read off the condition's roll layout (set roll_setup with edit_condition), which is what a heat-weld rod or a carpet seam tape is bought by. A 20-ft-wide room off a 12-ft roll seams once down its length; the same square footage as two 10-ft rooms seams not at all, and no percentage of the area or the perimeter can tell those two jobs apart. Without a roll_setup — or with no committed floor shapes to lay out — a seam_lf row reads 0, which is the honest state rather than a guess. condition names an existing OR NEW finish tag (minted on first touch, same as one_click/measure_polygon) — add alone is enough to seed materials on a condition before you've traced anything. remove/patch target existing row ids from this reply or export_takeoff (takeoff_summary strips materials for a compact quantities-only reply); a bad id 404s the WHOLE call before anything is written, and referencing an id on a tag with no condition yet errors rather than silently minting an empty one. No review gate here — materials rows are quantity config, not traced geometry, so this edits directly; undo_last reverses a call in one step (the condition's whole materials array, snapshotted before the write, restored verbatim).
| Name | Required | Description | Default |
|---|---|---|---|
| add | No | New rows to add | |
| patch | No | Field changes on existing rows | |
| remove | No | Existing row ids to remove | |
| condition | Yes | Finish tag, e.g. 'CPT-1' |
Output Schema
| Name | Required | Description |
|---|---|---|
| changed | Yes | |
| condition | Yes | The finish tag passed in |
| materials | Yes | The condition's full materials array after this write |
| condition_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden and delivers: it discloses the no-review-gate direct edit behavior, whole-call 404 on bad ids, the error on missing condition, and the seam_lf reading 0 without roll_setup. It even includes a concrete example (20-ft vs 10-ft rooms) to explain the figured basis.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence earns its place: it packs the purpose, formula, edge cases, error behavior, and tool relationships into one dense paragraph that is front-loaded with the action verb.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (add/remove/patch with six fields and a special basis), the description is remarkably complete: it covers formulas, edge cases, failure modes, and integration with edit_condition, undo_last, and export_takeoff. With an output schema present, return values need no explanation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although the schema already covers all parameters at 100%, the description adds the calculation formula (basis ÷ per, rounded up), the meaning of round:false, the seam_lf special handling, and the minting of new tags on condition. This significantly enriches parameter understanding beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb phrase 'Add, remove, or patch supporting-materials rows' and grounds the tool in the 'per-condition Supporting Materials panel,' clearly distinguishing it from sibling tools like edit_condition and takeoff_summary. It also explains the row structure and calculation, leaving no ambiguity about what the tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit context on when to use the tool: it is for 'quantity config, not traced geometry' and notes that undo_last reverses a call. It also contrasts with takeoff_summary ('strips materials') and directs users to edit_condition for roll_setup, making the relationship to alternatives clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
edit_shapeA
REVISE a shape you already committed, instead of deleting it and starting over: pass new verts to move the geometry, condition to reassign it to a different finish tag, role to switch between floor_area / deduct / linear, label to name the room it belongs to, or any combination. Quantities are recomputed from the result — a role flip alone re-measures (closed area vs open length). The loop this is for: one_click or measure_polygon to commit, view_sheet with overlay:true to LOOK at what landed, then edit_shape to fix the two vertices that overshot into the corridor. label is the per-room reporting seam: detect_rooms already stamps the room number it traced from, so this is how a shape traced by hand — or one whose room number the sweep read wrong — joins the same per-room breakdown the Report and the workbook's floor × room tab group by. Shapes a human affirmed (origin.reviewed) are ink and are refused — an agent revises its own pencil and nothing else. Agent self-revision is tallied on origin.agent_edits, kept deliberately separate from the human-correction fields. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| role | No | Switch what the shape measures — flipping INTO surface_area needs a height on the shape or its condition | |
| label | No | The room (or phase/area) this shape belongs to, e.g. "134" or "OFFICE 101" — what per-room reporting groups by. Pass "" to clear it | |
| verts | No | Replacement geometry (image px): ≥3 vertices for an area shape, ≥2 points for a linear/surface run, ≥1 for a count marker | |
| shape_id | Yes | Id returned when the shape was committed | |
| condition | No | Reassign to this finish tag (minted on first use) |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | No | count shapes only — the marker's EA (preserved across the edit) |
| label | No | The shape's room/phase label after this call — absent when it carries none (a cleared label reports as absent, not as an empty string) |
| nverts | Yes | |
| area_sf | No | 0 for linear shapes; LF × height for surface_area; absent for count |
| changed | Yes | Which fields this call actually changed |
| shape_id | Yes | |
| agent_edits | Yes | How many times the agent has revised this shape — separate from the human-correction tally |
| measure_role | Yes | |
| perimeter_lf | No | Length for linear/surface runs, perimeter for closed ones; absent for count |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It discloses that quantities are recomputed from the result, that a role flip alone re-measures, that human-affirmed shapes are refused, and that agent self-revision is tallied separately. It also specifies the coordinate system (image px at render scale 2.0, origin top-left, y down), which is critical for correct usage.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense and front-loaded: purpose, parameter summary, workflow, constraints, and coordinate system are arranged logically. The label reporting seam paragraph is lengthy and somewhat complex, but every sentence adds meaningful context, so the length is justified.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 5 parameters, no annotations, and the existence of an output schema, the description explains the tool's role in a broader lifecycle, the refusal rule, recalculation behavior, and coordinate semantics. It could mention the no-op case for sending only shape_id, but the description is otherwise comprehensive and complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% so the baseline is 3; the description adds value by mapping parameters to actions, e.g., 'verts to move the geometry', 'condition to reassign it to a different finish tag', 'role to switch...', 'label to name the room'. However, it only lists floor_area/deduct/linear in the prose and omits the surface_area and count role options (which are only in the schema enum), and the coordinate detail is helpful but not essential beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'REVISE a shape you already committed' — a specific verb and resource — and enumerates the exact modifications supported: verts, condition, role, label. It explicitly contrasts with the alternative of deleting and starting over, and clearly distinguishes from sibling tools like measure_polygon and delete_shape.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides an explicit workflow loop: 'one_click or measure_polygon to commit, view_sheet with overlay:true to LOOK at what landed, then edit_shape to fix...'. It also states a strict exclusion rule: 'Shapes a human affirmed (origin.reviewed) are ink and are refused — an agent revises its own pencil and nothing else.' This gives clear when-to-use and when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
export_dxfA
The takeoff as a CAD drawing — a DXF (R2000) AutoCAD, BricsCAD, LibreCAD and Revit import as native geometry, not a picture. ONE sheet per file, like a DWG: every committed shape on that sheet becomes an LWPOLYLINE (floor rings CLOSED, walls and linear runs open, count marks a 1-ft circle), on a layer named for its finish — OT-, with -DEDUCT / -HOLE / -WALL / -LINEAR / -COUNT suffix layers so a CAD user isolates any bucket with one layer filter, and room labels as TEXT on OT-LABELS. Coordinates are real units in the sheet's own frame: origin at the sheet's BOTTOM-left, Y up (CAD convention), feet by default ($INSUNITS 2) or metres with units:"m"; a ring's area in CAD equals its area in export_report to rounding, so the drawing IS the audit. Requires the sheet's scale (refuses otherwise — pixels in a DXF are worse than nothing); with several sheets carrying shapes, pass sheet to choose the drawing (the refusal lists them). The reply names every shape left out and why — a reconciled deduct ships as its parent's -HOLE ring, never twice. Writes to path (required — a DXF lives on disk, next to the DWG it aligns to); pair with export_marked_pdf for the reviewed planset.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | File path to write the .dxf to | |
| sheet | No | Sheet key ("plan.pdf", "plan.pdf#2") or title-block number ("A-101"). Optional only when exactly one calibrated sheet carries shapes | |
| units | No | Output units — "ft" (default) or "m" | |
| overwrite | No | Replace the file at path even when it is not an OpenTakeoff export. Off by default: re-exporting over a previous export of your own already overwrites without this, so you only need it to deliberately destroy an unrelated file. |
Output Schema
| Name | Required | Description |
|---|---|---|
| path | Yes | The DXF written |
| bytes | Yes | |
| sheet | Yes | Sheet key the drawing was cut from |
| units | Yes | |
| layers | Yes | Layer names in table order — OT-<TAG>, plus -DEDUCT/-HOLE/-WALL/-LINEAR/-COUNT suffix layers and OT-LABELS |
| shapes | Yes | Committed shapes that produced geometry |
| extents | Yes | Model-space bounding box in output units; origin = sheet's bottom-left, Y up |
| skipped | Yes | Shapes on this sheet left out, each with why — never silent |
| entities | Yes | LWPOLYLINE + CIRCLE + TEXT entities in model space |
| sheet_number | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and succeeds thoroughly. It discloses output geometry, layer naming, coordinate conventions, units, scale requirements, refusal behavior, handling of missing shapes, and overwrite semantics — far beyond what an annotation could capture.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose, and every sentence adds technical or behavioral value. It is dense and somewhat run-on, but for a CAD export tool with many edge cases, the length is justified. It could be tightened with clearer sentence breaks.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity, the description covers all essential operational concerns: output format, layer organization, coordinate system, units, scale prerequisite, multi-sheet selection, exclusions, overwrite safety, and a companion PDF tool. The presence of an output schema means return-value details are not needed here.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description adds meaningful meaning: path is required because DXF is a disk file aligned to the DWG; sheet is optional only when one calibrated sheet carries shapes; units affect INSUNITS; and overwrite has a nuanced 'your own previous exports already overwrite' behavior. This is more than baseline schema documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the tool exports the takeoff as a CAD drawing in DXF R2000 format, with native geometry rather than an image. It clearly distinguishes this from export_marked_pdf by emphasizing CAD interoperability and from export_report by noting that CAD areas match the report's areas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides strong context: requires a calibrated sheet scale, requires a path, and explains when to pass the sheet parameter. It also names export_marked_pdf as a companion for a reviewed planset. However, it never explicitly states 'use this instead of X when Y' or lists exclusion conditions beyond the scale requirement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
export_marked_pdfA
The MARKED-UP PLANSET — the deliverable of every takeoff. Writes a distribution-ready PDF to disk: a legend cover (per-condition totals, swatches, a by-sheet breakdown) followed by every sheet that carries takeoff shapes or annotations, vector-copied from the source plan with the work burned in as drawn — condition colors and hatches, a quantity chip on every shape, annotation clouds/callouts/highlights, and approval marks (the estimator's APPROVED rings, the agent's AGENT diamonds — the cover tallies the split). Built by the same module as the canvas's MARKED SET button, so agent output and app output are one implementation. A construction takeoff is no good without markup: finish EVERY takeoff by writing this file and giving the user its path (export_report carries the numbers for pricing; this carries the evidence). When the shapes were machine-traced and unreviewed, the document says so on its last page — the review path is importing the export_takeoff payload into the app, where agent shapes arrive as pencil proposals. Default path: next to the loaded plan as " - marked set.pdf". Needs no native canvas — pure vector copy, so it works even where view_sheet cannot render.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | Where to write the PDF (default: "<plan dir>/<plan> - marked set.pdf") | |
| overwrite | No | Replace the file at path even when it is not an OpenTakeoff export. Off by default: re-exporting over a previous export of your own already overwrites without this, so you only need it to deliberately destroy an unrelated file. | |
| project_name | No | Cover-page project name (default: the plan file's name) |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | Yes | |
| path | Yes | Absolute path of the written marked-set PDF — hand this to the user |
| pages | Yes | Legend cover + one page per marked sheet |
| shapes_drawn | Yes | |
| sheets_marked | Yes | Sheets carrying shapes, annotations, or approval marks — unmarked sheets are omitted |
| approvals_drawn | Yes | Approval-family glyphs burned in (#176) — estimator APPROVED rings + agent AGENT diamonds; the cover tallies the split when any exist |
| annotations_drawn | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It discloses that the tool writes a PDF to disk, states the default path, notes the machine-traced warning page, clarifies it needs no native canvas, and confirms it is built from the same module as the app button. These are meaningful behavioral details beyond the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and fairly dense, but it is longer than necessary. Several phrases could be tightened (e.g., 'The MARKED-UP PLANSET — the deliverable of every takeoff' and later 'A construction takeoff is no good without markup'). Still, most sentences earn their place, covering content, workflow, caveats, and edge cases.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity and the presence of an output schema, the description is remarkably complete. It explains what the PDF contains, when to use it, how it relates to other workflows, the machine-traced caveat, the default path, and the no-canvas requirement. There are no obvious gaps for an agent to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description only reiterates the default path for the 'path' parameter and does not add deeper meaning for 'overwrite' or 'project_name.' It adds no practical usage nuances beyond what the schema already states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific deliverable: 'Writes a distribution-ready PDF to disk' and enumerates its contents (legend cover, vector-copied sheets, quantity chips, annotation clouds). It explicitly differentiates from siblings: 'export_report carries the numbers for pricing; this carries the evidence,' and notes it is the same module as the canvas's MARKED SET button.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: 'finish EVERY takeoff by writing this file and giving the user its path.' It contrasts with exports ('export_report carries the numbers... this carries the evidence') and notes a conditional usage context ('When the shapes were machine-traced and unreviewed... the review path is importing the export_takeoff payload'). Also mentions it works even where view_sheet cannot render, implying a fallback use case.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
export_reportA
The computed Report document — "opentakeoff.report.v1", the same schema the canvas Report's JSON export writes. Everything a pricing consumer needs without re-implementing the app's math: per-condition quantities with waste and multiplier applied (gross and *_net), the computed materials BUY LIST per condition (order quantity = basis ÷ coverage rate, rounded up to whole purchase units) plus the project-wide roll-up summed by (name, unit), per-sheet BASE subtotals, scale provenance per sheet, and annotations. Contrast: export_takeoff is the raw canvas payload (materials as CONFIG rows, no computed quantities) and takeoff_summary strips materials for a compact reply — when the numbers are leaving for pricing, consume this. A report alone is HALF the deliverable: pair it with export_marked_pdf, because a takeoff is reviewed on marked drawings, not on numbers. Returned inline; pass path to also write it to disk as JSON.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | File path to write the document to | |
| overwrite | No | Replace the file at path even when it is not an OpenTakeoff export. Off by default: re-exporting over a previous export of your own already overwrites without this, so you only need it to deliberately destroy an unrelated file. | |
| project_name | No | Label for the document's project_name field (a headless session has no project of its own; omitted → null) |
Output Schema
| Name | Required | Description |
|---|---|---|
| rfis | Yes | |
| units | Yes | |
| schema | Yes | |
| sheets | Yes | Scale provenance per sheet — how each scale was set |
| totals | Yes | |
| markups | Yes | |
| by_label | Yes | |
| by_sheet | Yes | BASE per-sheet subtotals — multiplier NOT applied, no waste, no materials |
| materials | Yes | Project-wide buy list — condition rows summed by (name, unit) |
| conditions | Yes | conditionTotals rows: gross + *_net quantities AND the computed materials buy list |
| roll_goods | Yes | Roll-goods order rows (#136) — order_lf / rolls / order_qty per roll-goods condition, ×N applied; empty when no condition carries a roll_setup (always the case for a headless session today) |
| project_name | Yes | |
| shape_labels | Yes | |
| display_units | Yes | |
| generated_with | Yes | |
| condition_columns | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description carries full burden. It discloses that the call returns an inline JSON and optionally writes to disk via path ('Returned inline; pass path to also write it to disk as JSON'). It clarifies the schema compatibility and that it is computed. However, it does not explicitly state side-effect safety (e.g., no project changes) or any failure conditions, which would push it to 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with a precise definition, then logically flows through contents, sibling contrast, usage guidance, and output behavior. Each sentence adds new information—no filler or repetition of schema. Despite length, it earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is complex, but the description thoroughly covers contents, use case, alternatives, and complementary tools. It also explains the 'half deliverable' pairing rationale. With an output schema present, the detailed return-value enumeration still adds value by explaining what the report computes and why it matters.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with each parameter ('path', 'overwrite', 'project_name') already documented. The description only indirectly references path ('pass path to also write it to disk') and does not add new meaning beyond the schema. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'The computed Report document — "opentakeoff.report.v1"' immediately identifying the tool as producing a specific, computed report. It enumerates detailed contents (per-condition quantities with waste/multiplier, BUY LIST, roll-ups, per-sheet subtotals, scale provenance, annotations) and explicitly contrasts with export_takeoff and takeoff_summary, making it unmistakable from siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit guidance is given: 'when the numbers are leaving for pricing, consume this.' It also says 'A report alone is HALF the deliverable: pair it with export_marked_pdf' and contrasts with raw payload export_takeoff and compact takeoff_summary. This clearly states when to use, when not to, and recommends a complementary sibling.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
export_takeoffA
The full "opentakeoff.takeoff_canvas.v1" annotations payload — exactly what the app autosaves, importable by it. Returned inline; pass path to also write it to disk as JSON. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | File path to write the payload to | |
| overwrite | No | Replace the file at path even when it is not an OpenTakeoff export. Off by default: re-exporting over a previous export of your own already overwrites without this, so you only need it to deliberately destroy an unrelated file. |
Output Schema
| Name | Required | Description |
|---|---|---|
| units | Yes | |
| schema | Yes | |
| shapes | Yes | |
| sheets | Yes | |
| markups | Yes | |
| approvals | No | Approval-family records (#176) — the estimator's APPROVED seals and the agent's verdict marks {id, actor, ts, sheet_id, at:[nx,ny], shape_id?, text?}. Present only when any exist (the canvas payload's own convention), so a verdict-free export stays byte-identical |
| conditions | Yes | |
| last_group | Yes | |
| sheet_tabs | Yes | |
| sheet_group | Yes | |
| project_name | Yes | |
| sheet_levels | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and discloses important behaviors: coordinate system (scale 2.0, origin top-left, y down), inline return vs disk write, and overwrite semantics (requires overwrite flag for non-OpenTakeoff files). It does not explicitly state that the takeoff data itself is unmodified, but export implies read-only.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, dense sentence packed with essential information. It is front-loaded with the core purpose and every clause contributes meaning, with no fluff or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description adequately covers the payload format, coordinate system, optional disk write, and overwrite behavior. Since an output schema exists, return values are documented separately. Minor gap: no reference to sibling tools for comparison, but overall it is complete for a technical export tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already provides high coverage (100%) for both parameters. The description adds value by clarifying that the payload is returned inline by default and that passing 'path' writes to disk, which explains the relationship between parameters and output beyond the schema's individual descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly indicates the tool returns the full 'opentakeoff.takeoff_canvas.v1' annotations payload, which is the app's autosaved format. It distinguishes from siblings like export_report and import_takeoff by focusing on the raw canvas payload, but uses technical jargon that may obscure the simple 'export' action.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for exporting/backing up the annotations payload and optionally writing to disk. It does not explicitly compare with alternatives like export_report or mention when not to use this tool, so guidance is present but not fully explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_scheduleA
Locate a schedule table in the set (#87): pass a kind ("room finish", "material"/"finish") and get every matching table's sheet, title, headers, TOTAL row count, and REGION — sized for a view_sheet look or a read_sheet_text pull of exactly the table. A schedule continued across sheets is ONE match whose "parts" list every fragment (base first) with its own viewable region; tables read through rotated headers say so; a table answering for one building carries "building"; a table with delta/REV-marked rows says how many in "revised_rows". Errors with what WAS found when the asked-for kind isn't in the set. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | Yes | "room finish" (rooms → surface finishes) or "finish"/"material" (codes → products) |
Output Schema
| Name | Required | Description |
|---|---|---|
| matches | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden and excels. It discloses how schedules are matched (including continued schedules as one match with parts), handling of rotated headers, building-specific tables, revised rows, and coordinate system details (px at render scale 2.0, origin top-left, y down). Error messages are explained, and sheet payload dimensions are mentioned. This is highly transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but densely packed with essential information. It front-loads the main purpose and then systematically covers edge cases and technical details. Every sentence adds value—no filler or repetition. The structure is logical and efficient.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity and the presence of an output schema, the description is complete. It explains the result structure (parts, building, revised_rows), error handling, coordinate system, and sheet payload dimensions. No critical aspect is missing; users know exactly what to expect and how to use the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% for the single 'kind' parameter, so baseline is 3. The description adds contextual meaning: it explains that 'room finish' corresponds to rooms→surface finishes and 'finish'/'material' to codes→products, and connects the parameter to the tool's overall purpose. This extra explanation justifies a 4, though the schema already covers the literal values.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: to locate schedule tables in a set by passing a kind, and it lists exactly what information is returned (sheet, title, headers, TOTAL row count, REGION). It distinguishes from siblings by referencing view_sheet and read_sheet_text usage and detailing edge cases (multi-sheet schedules, rotated headers, building-specific tables). This is specific and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly explains when to use the tool: to find schedules for viewing or reading. It explains the 'kind' parameter choices and their meanings ('room finish' vs 'finish'/'material'), and describes the output structure that guides downstream actions. It also covers error behavior when the kind isn't found, giving clear expectations.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_textA
LOCATE a known string on a sheet — the complement to read_sheet_text (which returns what a region SAYS; this finds WHERE a string you already know sits). Case-insensitive substring match against each pdf.js text run, so a room label split across runs ("OFFICE" then "134" as separate items) needs a find_text call per fragment, or read_sheet_text over a region to see the whole thing joined. Every hit's center feeds straight into one_click as the seed — the locate-then-trace workflow: find_text the room number, one_click at (or just past) its center. Optionally restrict to a region {x0, y0, x1, y1}; results cap at limit (default 200), with count/truncated telling you exactly how much a tighter region or higher limit would recover. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | Text to find — a room number ('134'), a label fragment ('RECEPTION'), a schedule tag ('CPT-1') | |
| limit | No | Max hits returned | |
| sheet | Yes | ||
| region | No | Rect in image px (origin top-left, y down); omit for the full sheet |
Output Schema
| Name | Required | Description |
|---|---|---|
| q | Yes | |
| hits | Yes | |
| count | Yes | Total matches before the limit cap |
| sheet | Yes | |
| truncated | Yes | true = count exceeds hits.length; narrow the region or raise limit |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description fully carries the behavioral disclosure burden. It details case-insensitive substring matching, pdf.js text run splitting, coordinate system (image px at render scale 2.0, origin top-left, y down), result capping, and count/truncated fields. This is comprehensive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, information-dense paragraph. It front-loads the purpose and workflow, but could be more structured with bullet points or sections. However, every sentence adds value, and there is no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the existence of an output schema (not shown), the description does not need to detail return values. It covers coordinate system, region restriction, limit behavior, and workflow integration. For a find_text tool, this is complete and actionable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 75% (3 of 4 parameters described). The description adds value beyond the schema by explaining coordinate semantics for the region parameter, case-insensitive behavior for q, and default/max behavior for limit. The undocumented sheet parameter is minor.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'LOCATE a known string on a sheet' and explicitly distinguishes it from the sibling tool read_sheet_text, which returns what a region says. The verb 'locate' and resource 'string on a sheet' are specific and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage context, including the locate-then-trace workflow with one_click, and explains the behavior for fragmented text runs. It does not explicitly state when not to use it, but the guidance is strong and the scenario is concrete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
import_takeoffA
The way BACK IN (#151): load an "opentakeoff.takeoff_canvas.v1" file — a prior export_takeoff, or the app's own save — into this session, through the SAME tested merge rules as the app's Sheet-menu import: finish-tag identity joins imported conditions onto this session's own (their knobs win), new ids append, duplicate ids skip (re-import is idempotent), and THIS session's calibration wins per sheet. An empty session adopts the file wholesale. Resume yesterday's work, extend a takeoff a human already reviewed (their ink stays ink — reviewed shapes arrive untouchable by agent verbs), or audit someone else's export with list_shapes/takeoff_summary. Requires a loaded plan; shapes referencing OTHER files ride along and count in totals but can't be viewed against this document — the reply's unknown_files names them. Approval marks ride the file too — transport, not minting: an estimator seal arriving by import stays estimator ink, listable but untouchable here. undo_last removes the imported SHAPES as one step; adopted conditions, scales, annotations, and approval marks stay.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Path to a takeoff_canvas.v1 JSON file on disk |
Output Schema
| Name | Required | Description |
|---|---|---|
| file | Yes | Basename of the imported file |
| note | Yes | |
| replaced | Yes | true = the session was empty and adopted the file wholesale |
| shapes_added | Yes | |
| shapes_total | Yes | |
| unknown_files | Yes | Files referenced by imported shapes that this document doesn't have — they count in totals but can't be viewed here |
| rules_imported | Yes | Correction rules (#88) that arrived with the file — apply_rules re-runs them |
| scales_adopted | Yes | Sheets whose calibration came from the file (this session's own always wins) |
| shapes_pending | Yes | Of the added shapes, how many are unreviewed machine pencil |
| conditions_added | Yes | |
| conditions_merged | Yes | Imported conditions that joined an existing finish tag (its knobs won) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and does so richly: it discloses merge rules (finish-tag identity, duplicate skip, idempotency), calibration precedence, reviewed-shape protection, unknown_files behavior, approval-mark transport, and undo semantics. No annotation contradiction exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but appropriately dense, with each sentence carrying meaningful behavioral or usage information. The opening 'The way BACK IN (#151)' is slightly cryptic but not harmful; overall it is well-structured and front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers all necessary context: preconditions, merge behavior, return field (unknown_files), limitations with other-file shapes, and undo impact. Given the complexity and the existence of an output schema, the description is fully self-sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% for the single 'path' parameter, and the description does not add significant new parameter-level detail beyond referring to the file type already in the schema. The baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies 'load an opentakeoff.takeoff_canvas.v1 file' into the session, specifying the exact file type and operation. It distinguishes itself from siblings by naming export_takeoff as the counterpart and load_plan as a different resource (plan vs canvas).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly lists use cases: 'Resume yesterday's work, extend a takeoff a human already reviewed... or audit someone else's export with list_shapes/takeoff_summary.' Also states the prerequisite 'Requires a loaded plan' and references the Sheet-menu import as an alternative with the same merge rules.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
link_annotationA
Attach an existing annotation to a condition, or detach it by passing an empty condition — the canvas's Attach/Detach control, reachable by an agent. Use it to tie up notes left unattached (list_annotations reports how many), or to move one to the finish it actually concerns. Attaching mints the tag on first use.
| Name | Required | Description | Default |
|---|---|---|---|
| condition | Yes | Finish tag to attach to; empty string detaches | |
| annotation_id | Yes | Id from annotate or list_annotations |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| note | Yes | |
| condition | Yes | |
| condition_id | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description carries burden. Discloses key behavior: detaching via empty string and tag minting on first use. Could mention more side effects, but adequate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with primary function, followed by use cases and a behavioral note. No redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given tool simplicity and existence of output schema, description covers purpose, usage, and key behavior. Does not address errors or edge cases, but sufficient for typical use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with clear parameter descriptions. Description adds extra context: 'list_annotations' for IDs and tag minting, enhancing meaning beyond schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the verb 'attach' or 'detach' and the resource 'annotation' and 'condition', clearly distinguishing from siblings like 'annotate' and 'list_annotations' by referencing them. It explains primary function and use cases.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit use cases: tie up unattached notes or move annotations to correct conditions. References sibling 'list_annotations' for context. Does not list when not to use, but guidance is sufficient.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_annotationsA
Every annotation on the takeoff, with condition_id RESOLVED to its finish tag so you can act on the reply without joining against conditions[]. Filter by sheet, by condition, or both. Coordinates come back in image px (the same frame you passed in), not the normalized form they're stored as. unattached counts the notes carrying no condition — the candidates for link_annotation. verdicts is the approval family's inventory (mark_verdict/delete_verdict): every mark with its actor stated — the estimator's APPROVED ring or the agent's AGENT diamond — under the same filters, a condition filter reaching a verdict through its target shape. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| sheet | No | Only annotations on this sheet | |
| condition | No | Only annotations attached to this finish tag |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| verdicts | Yes | Approval-family records (#176) under the same filters: sheet applies directly; a condition filter reaches a verdict THROUGH its target shape (a sheet-point mark carries no scope and drops out) |
| unattached | Yes | How many carry no condition — candidates for link_annotation |
| annotations | Yes | |
| verdict_count | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description fully discloses critical behavior: coordinate transformation (image px at render scale 2.0, PDF pt × 2, origin top-left, y down), resolution of condition_id to finish tags, and the semantics of 'unattached' and 'verdicts'. This goes far beyond basic expectations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence carries necessary behavioral detail. It efficiently covers output fields, filters, and coordinate system without redundancy. Slightly long, but the complexity of information justifies the length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with no annotations and complex output behavior, the description covers all essential aspects: filters, coordinate system, resolved condition semantics, and the purpose of special fields. An output schema exists, so not explaining every return field is acceptable; the description still adds enough context to use the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema descriptions already cover both parameters (100% coverage). The description adds meaning by clarifying that filters can be combined ('or both'), and explicitly maps the 'condition' parameter to the resolved finish tag concept, which is not fully apparent from the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists every annotation on the takeoff with condition_id resolved to its finish tag. It distinguishes itself from siblings by mentioning resolved conditions, unattached counts, and verdicts, which positions it as the comprehensive annotation inventory tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly explains filtering by sheet, condition, or both, and ties output fields to concrete actions: 'unattached' identifies candidates for link_annotation, and 'verdicts' serves the mark_verdict/delete_verdict family. This provides clear when-to-use guidance versus related tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_shapesA
The mid-session shape inventory (#149): every committed shape's id, sheet, condition tag, role, quantities, room label, vertex count, and review state in one compact read — the ids edit_shape and delete_shape assume you have, without pulling the whole export_takeoff payload to find one shape. Filter by sheet, by condition, or both; filters narrow, an empty list is a result, not an error.
| Name | Required | Description | Default |
|---|---|---|---|
| sheet | No | Only shapes on this sheet | |
| condition | No | Only shapes under this finish tag (must exist) |
Output Schema
| Name | Required | Description |
|---|---|---|
| count | Yes | |
| shapes | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of disclosing behavior. It states the operation is a 'compact read', implying non-destructive, and clarifies that it only includes 'committed' shapes, not drafts. It also notes that filters narrow results, which informs the caller about side effects. However, it does not explicitly mention authorization needs or potential error conditions beyond the schema's 'condition must exist'.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is informative and front-loaded with the core purpose. It uses three sentences: the first lists the returned fields and positions the tool, the second explains its use case relative to siblings, and the third clarifies filter semantics. While the first sentence is long, every part adds value, so it earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The output schema exists, so return values are covered. The description explains what fields are included, how filters behave, and when to use it, which is sufficient for a read-only list tool. It could mention potential errors (e.g., invalid condition), but the schema already notes the condition must exist, making this reasonably complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema covers both parameters with clear descriptions ('Only shapes on this sheet', 'Only shapes under this finish tag (must exist)'), so the description adds limited new meaning. It reinforces that the two filters can be combined and that they narrow results, but this does not go significantly beyond the schema. Baseline of 3 is appropriate given high schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists all committed shapes with specific fields (id, sheet, condition tag, etc.), using the verb 'list' plus the resource 'shapes'. It distinguishes itself from siblings by positioning it as a compact read that provides ids for edit_shape and delete_shape without pulling the full export_takeoff payload.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use this tool: to obtain ids that edit_shape and delete_shape assume, while avoiding the heavier export_takeoff. It also clarifies that filters (by sheet, condition) narrow results and that an empty list is a valid result, not an error, which sets user expectations.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
load_planA
Open a plan PDF from disk. Default: replace the whole session (previous documents, scales, conditions, and shapes are cleared). merge: true ADDS the document to the working set instead (#152) — a bid set is plans + schedule + addenda, not one PDF — keeping every scale, condition, and shape; sheet keys carry file names so documents never collide, the sheet graph spans the whole set (resolve_tag can chain a plan tag on one file to a schedule row in another), and the marked set covers every worked sheet. Re-loading an already-merged file is refused — reload = replace, deliberately. Returns file, files, page_count, and one entry per sheet. The loaded sheets also become browsable resources (takeoff://sheets). Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | Path to a plan PDF on disk | |
| merge | No | true = ADD this document to the working set, keeping all existing work (merge into an empty session is just a load) |
Output Schema
| Name | Required | Description |
|---|---|---|
| file | Yes | The document just loaded (basename) |
| note | Yes | |
| files | Yes | Every document in the working set, load order (#152 — one entry unless merge was used) |
| sheets | Yes | EVERY sheet in the working set, not just the file loaded by this call |
| page_count | Yes | Total sheets across the working set |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and excels: it discloses the destructive default (clears session), the merge behavior, the refusal condition, the return payload, coordinate system details, and the conversion to browsable resources. This is a comprehensive safety/behavioral profile.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single dense paragraph but logically organized: action, default behavior, merge variant, edge case, return values, resources, and coordinate system. Every sentence adds necessary information with no filler, achieving conciseness through purposeful density.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity and absence of annotations, the description covers everything an agent needs: the two modes, re-load restriction, return fields, coordinate transformation, and resource availability. It is fully sufficient for correct invocation and interpretation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already documents both parameters with 100% coverage, so the baseline is 3. The description adds meaningful context beyond the schema by elaborating on the default behavior, the meaning of merge:true, and the edge case of merging into an empty session, increasing clarity for parameter usage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear verb+resource ('Open a plan PDF from disk') and immediately clarifies the default replacement behavior versus merge mode. This distinguishes it from sibling tools that operate on already-loaded sheets or produce analyses.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly contrasts the default replace-all behavior with merge:true for adding to a working set, explains the bid-set use case, and states that re-loading an already-merged file is refused. This gives clear when-to-use and when-not-to-use guidance, including a concrete scenario.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mark_verdictA
Mark the agent's VERDICT on work — the pencil half of the approval family, and the only half an agent can mint. Two actors exist on the record: the estimator's APPROVED ring is ink, minted solely by a human's click at the canvas's Approve tool; this tool mints the AGENT diamond and structurally nothing else — it takes no actor input to misuse. Target the work either way: shape_id anchors the mark ON a committed shape (a room at its area centroid, a run at its on-path midpoint, a count marker at its point) and records WHAT was marked — the shape_id stays on the record as provenance, and the glyph keeps its own anchor even if the shape is later deleted; or sheet + at drops the mark at a sheet point (image px). Exactly one target. Optional text rides the record through every export; the glyph itself always reads AGENT. A verdict touches no quantity and gates nothing: it is the agent's signed claim that it checked this work — pencil beside the estimator's ink, never in its place. The mark renders as the graphite AGENT diamond on the canvas and in the marked set, the marked-set cover tallies the split ("Approval stamps: N estimator-approved · M agent-marked"), and the record rides the annotations payload through export_takeoff / import_takeoff and the app's own saves. One mark per shape (re-mark = delete_verdict, then mark again); list_annotations returns the inventory in verdicts[]; undo_last steps over a mark exactly like any other mutation. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| at | No | Sheet-point mode: where the AGENT diamond renders (image px) | |
| text | No | Optional short note riding the record and every export — the glyph always reads AGENT | |
| sheet | No | Sheet-point mode: the sheet, together with at | |
| shape_id | No | Mark a committed shape (list_shapes has the ids) — anchored on the shape, recorded as provenance. Exactly one target: this OR sheet + at |
Output Schema
| Name | Required | Description |
|---|---|---|
| at | No | Where the AGENT diamond renders (image px) — absent only when the marked shape rides a sheet from a file this session hasn't loaded (#152) |
| id | Yes | The minted record id ("apr-…") |
| ts | Yes | ISO-8601 mint time |
| note | Yes | |
| text | No | |
| actor | Yes | Always agent — this tool is structurally incapable of minting the estimator's seal |
| sheet | Yes | |
| shape_id | No | Shape mode: the committed shape this verdict is about |
| condition | No | Shape mode: the marked shape's finish tag, resolved |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full responsibility for behavioral disclosure. It details side effects (provenance recording, glyph anchor persists even if shape deleted), non-effects (touches no quantity, gates nothing), rendering behavior, tally in marked-set, payload propagation, and coordinate conventions. It also explains the one-mark-per-shape rule and interaction with undo_last. This is exemplary transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but every sentence contributes unique, essential information. It is front-loaded with purpose, then systematically covers targeting, behavior, rendering, and coordinates. No filler or repetition; efficient for the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity, the description is remarkably complete. It covers all parameters, behavioral nuances, sibling relationships, coordinate systems, and lifecycle interactions (re-mark, undo, list). The output schema exists, so not describing return values is acceptable. There are no significant gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description adds substantial meaning beyond the schema. It clarifies the mutually exclusive targeting modes (shape_id vs sheet+at), the coordinate system (image px at render scale 2.0, top-left origin, y down), and the exact role of text. This significantly enriches parameter understanding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear, specific verb+resource ('Mark the agent's VERDICT on work') and immediately distinguishes this tool from the estimator's approval ring, identifying it as the only half an agent can mint. It explicitly states what the tool does and how it differs from siblings like delete_verdict and annotate.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: it distinguishes the two targeting modes (shape_id vs sheet+at) and states 'Exactly one target.' It also mentions alternatives such as delete_verdict for re-marking and list_annotations for inventory, and clarifies that this is the agent's tool distinct from the estimator's approval. This exceeds basic usage guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
measure_lineA
Measure an open polyline (min 2 points, image px): length_lf at the sheet's scale. Requires the scale to be set. Pass condition to commit it as a linear shape (base, transitions, feature strips). Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| pts | Yes | ||
| sheet | Yes | ||
| condition | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| npts | Yes | |
| shape_id | No | Present when condition was passed and the shape committed |
| length_lf | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It discloses coordinate system (image px at render scale 2.0, origin top-left, y-down), scale requirement, and that sheet payloads carry dims in px and pt. It doesn't mention destructiveness or auth, but for a read-like measurement tool, these are less critical.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured, starting with verb and purpose, then key constraints. Every sentence adds value (coordinate details, scale requirement, condition usage). It could be slightly tightened (e.g., merging coordinate and sheet payload sentences) but is efficient overall.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 3 parameters and an output schema, the description covers purpose, prerequisites, coordinate system, and one param's role. It lacks details on return values (output schema presumably covers that) and error cases. The sheet param missing is a gap, but overall it provides sufficient context for typical use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so description must compensate. It explains 'pts' as polyline points and 'condition' as committing as a linear shape. However, 'sheet' is not described at all (likely an ID or key). This leaves one of three parameters unexplained, which is a gap given zero schema help.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool measures an open polyline's length in linear feet at the sheet's scale, contrasting with siblings like measure_polygon (likely area). The verb 'Measure' and specific resource 'open polyline' make the action unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It specifies a prerequisite ('Requires the scale to be set') and mentions passing a condition to commit as a linear shape. Though it doesn't explicitly list alternatives, the sibling context (e.g., measure_polygon for closed shapes, set_scale for scale) provides implicit guidance. Could be more directive about when not to use.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
measure_polygonA
Measure a closed polygon you supply (min 3 vertices, image px): area_sf and perimeter_lf at the sheet's scale. Requires the scale to be set. Pass condition to commit it; role "deduct" subtracts. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| role | No | floor_area | |
| sheet | Yes | ||
| verts | Yes | ||
| condition | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| nverts | Yes | |
| area_sf | Yes | |
| warning | No | Mixed-scale warning (#153): a scale note disagreeing with the sheet's sits in the measured region — verify before trusting these numbers |
| shape_id | No | Present when condition was passed and the shape committed |
| perimeter_lf | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations available, the description takes on the full burden. It discloses the non-obvious coordinate system (render scale 2.0, top-left origin, y-down), the need for a scale, and the role-based subtraction behavior. The 'commit' phrasing is ambiguous, but overall it provides substantial behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is four sentences and front-loads the core purpose. It packs important details about coordinate systems and prerequisites without excessive verbosity. The phrase 'Pass condition to commit it' is cryptic but does not waste words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the output schema covers return values, the description doesn't need to explain them. It covers prerequisites, coordinate system, role behavior, and sheet context, which is strong for a tool with no annotations. It could be clearer on the commit semantics but is overall complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has 0% description coverage, so the description must compensate. It adds key meaning to 'verts' (image px, closed polygon, min 3 vertices, coordinate space), clarifies 'role' (deduct subtracts), and explains 'condition' (commit). Only 'sheet' remains vague, but it is likely a standard reference.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool measures a supplied closed polygon (min 3 vertices) and reports area_sf and perimeter_lf at the sheet's scale. This immediately differentiates it from sibling tools like measure_line and measure_surface.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides a prerequisite (scale must be set) and explains conditional behavior (passing a condition commits it, role 'deduct' subtracts). However, it does not explicitly discuss when to use this tool vs. alternatives such as measure_line or measure_surface, so the guidance remains implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
measure_surfaceA
Surface Area — wall SF (#146): trace an OPEN run along the wall in plan view (min 2 points, image px) and the quantity is traced LF × height. This is how wall tile, wainscot, and wall systems are taken off — the quantity family one_click and measure_polygon cannot produce. Height lives on the CONDITION (the canvas's H knob): pass height_ft to set it on this call (journals as its own undo step, like typing H before tracing), or set it once with edit_condition; with neither, this refuses and mints nothing. The shape snapshots the height it was quantified at. Requires the sheet's scale. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| pts | Yes | The wall run, an open polyline (image px) | |
| sheet | Yes | ||
| condition | Yes | Finish tag to commit under (minted on first use), e.g. 'CT-W1' | |
| height_ft | No | Wall height in feet — written to the condition's H knob first, then used |
Output Schema
| Name | Required | Description |
|---|---|---|
| npts | Yes | |
| area_sf | Yes | length_lf × height_ft — the wall SF committed |
| shape_id | Yes | |
| condition | Yes | |
| height_ft | Yes | The height this shape was quantified at (snapshotted on the shape) |
| length_lf | Yes | The traced run's open length |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and handles it exceptionally well. It discloses the refusal behavior when height is not set (mints nothing), the undo-step journaling of height_ft, the coordinate system (image px, render scale 2.0, origin top-left, y down), and that the shape snapshots the height used. This level of detail goes beyond typical descriptions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but not bloated; every sentence earns its place. It front-loads the purpose and packs constraints, prerequisites, and coordinate details efficiently. It could be slightly more parseable with clearer separation of topics, but it is far from verbose and maintains a logical flow.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite the tool's complexity (4 parameters, no annotations, output schema exists), the description covers purpose, usage, prerequisites, failure behavior, and coordinate details. Since an output schema exists, not explaining return values is acceptable. The description also differentiates from siblings, making it fully contextual for an agent to invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 75%, so the baseline is 3. The description adds meaningful context: it clarifies that pts are image px in an open polyline (min 2 points), that height_ft is written to the condition's H knob before being used, and that sheet payloads carry dims in both px and pt. However, the 'sheet' parameter itself is still not explicitly described, which is a minor gap given it's a required string.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: measuring surface area of walls by tracing an open run in plan view, with the quantity given as LF × height. It explicitly distinguishes itself from siblings by noting that one_click and measure_polygon cannot produce this takeoff, making the purpose and scope unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides solid usage context: it is for wall tile, wainscot, and wall systems, and explicitly states that one_click and measure_polygon cannot produce this. It also requires the sheet's scale and height to be set, but doesn't explicitly enumerate when not to use it or mention alternatives like measure_line. Still, the guidance is clear enough for an agent to choose appropriately.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
one_clickA
One-Click Area: click inside a room (image px) and the plan's vector linework bounds it — the sealed flood engine (RFC #60), contour trace, vertices snapped to true PDF endpoints. The engine's arguments are FEET-TRUE through the sheet's scale, exactly the canvas's: gap sealing bridges up to a door-width opening (disclosed as gap_sealed_px — that much boundary is synthetic), door-swing wedges annex the swing a doorway sweeps (door_wedges), and the minimum-passage rule keeps sub-half-foot slits from conjoining two rooms (min_pass_px/min_pass_delta). Every trace carries the engine's own account of itself: confidence (0..1, with confidence_factors naming what deducted) — a review PRIORITIZER, never a verification. 1.0 means every signal ran clean, not that the trace is right; a LOW confidence is a view_sheet {overlay: true} audit prompt, not a fact to bid from — put eyes on the flagged edge before the total means anything. SCANNED sheets work too (#154): where vectors can't bound the room (an image-only scan, or a scan wrapper whose only linework is the title block), the flood falls back automatically to the sheet's rendered pixels — same engine the canvas uses — and the reply plus the committed shape's origin carry raster_traced: true so a pixel-bounded ring is never mistaken for a vector-snapped one. Vector always wins where it works; a raster ring's corners are unsnapped, so audit it with view_sheet {overlay: true} before trusting the total. With the sheet's scale set, returns area_sf / perimeter_lf; pass condition (a finish tag, e.g. "CPT-1") to commit the traced shape to the takeoff — the full engine account rides the committed shape's origin, so the export tells the truth about how each shape was made. Without a scale it returns px-only quantities with a warning and commits nothing (the engine also degrades to its scale-blind fallbacks — a weaker measurement, one more reason set_scale comes first). role "deduct" makes the committed shape subtract. After committing, LOOK at what landed — view_sheet {overlay: true} — and fix an overshot ring with edit_shape before trusting any total. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| x | Yes | ||
| y | Yes | ||
| role | No | floor_area | |
| sheet | Yes | ||
| layers | No | Override the sheet's classified layer roles for THIS call (see sheet_info.layers) | |
| condition | No | Finish tag to commit under (minted on first use) | |
| sensitivity | No | Fill sensitivity, the same knob the canvas has: 0 strict (hatch/light linework always blocks), 0.5 balanced (default), 1 aggressive (crosses more hatch, tolerates more growth). Raise it when a flood stops short at hatching INSIDE the room; verify the grown ring with view_sheet overlay before committing | |
| return_verts | No | Include the traced polygon's vertices (image px) |
Output Schema
| Name | Required | Description |
|---|---|---|
| verts | No | Traced polygon vertices (image px), when return_verts was set |
| nverts | Yes | Vertex count of the traced polygon |
| status | Yes | |
| area_sf | No | Scaled mode: traced area in SF |
| warning | No | Preview mode (no scale): why quantities are unavailable — OR, in scaled mode, a mixed-scale warning (#153): a scale note disagreeing with the sheet's sits in the measured region (enlarged plan/detail viewport likely) |
| area_px2 | No | Preview mode (no scale): raw area in px² |
| shape_id | No | Scaled mode: id of the committed shape, when condition was passed |
| confidence | No | 0..1 — the trace scored from the engine's own signals (sealed openings, door wedges, min-passage rule, hatch tier, raster boundary, mask coarseness, implausible size). A review PRIORITIZER, not a verification: 1.0 means every signal came back clean, never that the trace is right. A low score is a view_sheet {overlay:true} audit prompt, not a fact to bid from |
| door_wedges | No | Door-swing wedges annexed into the region under grow-but-verify — how many doorways' swings were included, the canvas's own door handling; rides origin.door_wedges |
| min_pass_px | No | The feet-true minimum-passage rule (openings under ~0.5 ft never connect two spaces) ran at this dilation radius AND changed the answer — present only with min_pass_delta |
| perimeter_lf | No | Scaled mode: traced perimeter in LF |
| perimeter_px | No | Preview mode (no scale): raw perimeter in px |
| gap_sealed_px | No | Present when the seal ladder closed a genuine OPENING this many mask px wide (doorway-scale — scaled by the sheet's feet, distinct from gap_bridged_px's drafting-pinhole rescue). Part of the boundary is synthetic, and confidence deducts by that share; rides origin.gap_sealed_px on the committed shape |
| raster_traced | No | Present when the region was bounded by the sheet's RENDERED PIXELS (the scanned-sheet raster fallback, #154) rather than vector linework — absent means the vector path ran. Rides origin.raster_traced on the committed shape; a raster ring's corners are unsnapped (a scan has no true endpoints), so audit it with view_sheet overlay before trusting the total |
| gap_bridged_px | No | Present when the seal ladder bridged a drafting pinhole this many px wide to close the region — the rescue rides provenance (origin.gap_bridged_px) rather than passing as a clean fill |
| hatch_filtered | No | Present when hatch/pattern linework was classified out of the boundary |
| min_pass_delta | No | Fraction of the verbatim flood the minimum-passage rule removed; 1 means the drawn linework bounds nothing here and the rule is the only reason there is a measurement — audit before trusting |
| ring_interiors | No | Of those wedges, how many were a CLOSED ring's interior (round column, callout bubble) rather than a door swing — annexed floor you may want as a deduct instead |
| confidence_factors | No | The named factors behind a sub-1.0 confidence (e.g. "sealed-opening(10% synthetic boundary)") — each names the edge worth putting eyes on; absent when every signal ran clean |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full transparency burden and does so extensively. It discloses synthetic boundary from gap sealing (gap_sealed_px), door-swing wedges (door_wedges), the meaning of confidence, automatic raster fallback for scanned sheets, and the exact coordinate system (render scale 2.0, origin top-left, y down). It even warns that '1.0 means every signal ran clean, not that the trace is right'. This is exceptionally rich behavioral disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose, but it is very long and includes repeated audit warnings ('put eyes on the flagged edge', 'LOOK at what landed', 'audit it'). While almost every sentence adds technical detail, the redundancy and dense jargon make it less concise than it could be. It earns a mid score for structure over length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter tool with no annotations, the description covers most critical contextual areas: scale dependency, raster fallback, commit semantics, confidence as a review tool, coordinate system, and post-commit verification. It lacks explicit detail on layers and sensitivity, but those are documented in the input schema. Given the tool's complexity, the description is near-complete, though not perfectly exhaustive.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 50%, so the description must compensate for x, y, role, and sheet. It does explain x/y as image px click coordinates, role's deduct behavior, and condition as a finish tag. However, it omits the layers override, sensitivity knob, and return_verts flag entirely, leaving those to schema descriptions. It adds meaningful semantics for some parameters but not all.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description leads with a specific verb and resource: 'click inside a room (image px) and the plan's vector linework bounds it — the sealed flood engine'. This clearly distinguishes one_click as an automatic area-tracing tool, unlike sibling tools such as measure_polygon or detect_rooms. It also specifies the commit behavior and role semantics, making the purpose unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives concrete usage context: 'With the sheet's scale set, returns area_sf / perimeter_lf', 'Without a scale it returns px-only quantities with a warning and commits nothing', and explicitly says 'role "deduct" makes the committed shape subtract'. It recommends setting scale first and auditing afterward with view_sheet, and mentions using edit_shape for fixes. However, it never explicitly compares against sibling tools like measure_polygon or detect_rooms, so it stops short of full when-to-use versus alternatives guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
place_countA
Count markers — EA (#146): one point, one each. Thresholds, stair nosings, floor boxes, entrance mats — the scale-free quantity family. Commits one count shape per point (computed {count: 1}, exactly the canvas's Count tool), NO scale required, and the whole call is ONE undo step like a detect_rooms sweep. takeoff_summary reports them as ea; the marked set draws each marker. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| sheet | Yes | ||
| points | Yes | Marker positions (image px), one committed count shape each | |
| condition | Yes | Finish tag to commit under (minted on first use), e.g. 'TR-1' |
Output Schema
| Name | Required | Description |
|---|---|---|
| ea_total | Yes | The condition's total EA after this call |
| committed | Yes | Count shapes committed by this call — one per point |
| condition | Yes | |
| shape_ids | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations are absent, so the description fully carries the burden and does so thoroughly. It discloses the exact computed shape ({count: 1}), the single-undo-step behavior, coordinate system conventions (image px, render scale 2.0, origin top-left, y down), and how results appear in takeoff_summary and the marked set.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense and every sentence adds unique information, from core purpose to coordinate details. It is front-loaded with the primary function. While somewhat long, the length is justified by the complexity of coordinate systems and undo behavior.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return values need not be described. The description covers operational behavior, coordinate transformations, undo grouping, scale requirement (none), and how the results are reported. For a 3-parameter tool with an array parameter, this is highly complete without obvious gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers 67% of parameters (points and condition have descriptions). The description adds critical meaning for 'points' by specifying the coordinate system (render scale, origin, y-down) and for 'condition' by explaining finish-tag commitment. The 'sheet' parameter lacks schema description but the description mentions dimensional payloads, partially compensating.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Count markers' with one count shape per point. It distinguishes itself from siblings by identifying the 'scale-free quantity family' and explicitly stating 'NO scale required', contrasting with measure tools like measure_line or measure_surface.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear context for when to use this tool: counting EA items (thresholds, stair nosings, etc.) that are counted as each, not measured. It explicitly states no scale is required, implying it is for use without scale, but does not name specific alternative tools or explicitly exclude edge cases.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_sheet_textA
The sheet's text with positions — items [{str, x, y}] in image px plus the joined text. Optionally restrict to a region {x0, y0, x1, y1}. Use it to read title blocks, room labels, finish schedules, and scale notes. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| sheet | Yes | ||
| region | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| text | Yes | The items joined with spaces |
| items | Yes | Positioned text items (image px) |
| sheet | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Since no annotations are provided, the description fully carries the transparency burden. It discloses the coordinate system (image px, origin top-left, y down, render scale 2.0), explains the return format, and notes how sheet payloads carry dimensions in both px and pt. No behavioral surprises are left unmentioned.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is five sentences long, front-loaded with the core functionality ('The sheet's text with positions'), followed by optional behavior, use cases, coordinate details, and dims note. Every sentence adds value without redundancy or verbosity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity (2 params, one nested) and the presence of an output schema (so return values need not be reiterated), the description covers all essential aspects: purpose, parameter formats, coordinate system, and typical use cases. It is sufficiently complete for an agent to invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 2 parameters (sheet, region) with 0% description coverage, so the description must compensate. It explains the optional region parameter's format and coordinate significance, and it clarifies coordinate space for region values. The 'sheet' parameter is not elaborated, but its purpose is inferable from context. The description adds meaningful value beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool reads sheet text with positions, including the structure '[{str, x, y}]' and joined text. It provides concrete use cases (title blocks, room labels, etc.) and distinguishes itself from sibling tools like 'sheet_info' (metadata) or 'measure_*' (measurements), making the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says 'Use it to read title blocks, room labels, finish schedules, and scale notes,' giving clear guidance on when to apply the tool. However, it does not explicitly state when not to use it or contrast with siblings, though the context of siblings (delete, measure, etc.) makes the use case distinct.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resolve_tagA
Resolve ONE room tag across the set (#87): the plan tag → its room-finish schedule row → each finish code's definition in the finish/material schedule, EVERY edge carrying an evidence pointer (sheet + literal text + bbox — pass a bbox to view_sheet to look at the source). Rows carried by a continuation sheet ("… SCHEDULE — CONT'D") resolve exactly like base-sheet rows, citing the sheet the ink is on. The doctrine is refusal over guessing: a room that appears on the plan with no schedule row returns status "unresolved" with the reason (and still cites the plan tag); reused room numbers return "ambiguous" rather than picking one — on a multi-building set the refusal LISTS the candidate rows per building, and a building-qualified tag ("A-134") picks the building the set names. A delta triangle or REV tag on the answering row (or the plan bubble) rides the result as "revisions": the codes returned are the POST-revision answer, but the ink changed under that delta — view_sheet the marker's bbox and check the addendum before pricing. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | Yes | The room tag as drawn, e.g. "134" or "139A" — or building-qualified on a multi-building set, e.g. "A-134" (building A, room 134) |
Output Schema
| Name | Required | Description |
|---|---|---|
| tag | Yes | |
| room | Yes | The plan tag, when the room appears on a plan sheet — cited even when resolution fails. null on a multi-building ambiguity: citing one building's tag would be quietly wrong |
| reason | No | unresolved only — WHY (no schedule row / ambiguous / no schedule found). A room that appears on the plan with no row comes back here, never as a silent omission |
| status | Yes | |
| sources | No | The chain: plan tag → schedule row (the row cites the sheet that CARRIES it — under a continuation that is the CONT'D sheet) |
| building | No | resolved only — the building whose schedule row answered, when the set names buildings |
| finishes | No | |
| revisions | No | resolved only — delta/REV markers on the answering schedule row or the plan bubble. The finishes above are the POST-revision answer, but the ink changed: check the marker (view_sheet its bbox) and the addendum before pricing |
| candidates | No | unresolved only — every schedule row that COULD have answered (an ambiguous multi-building tag lists one per building; qualify the tag, e.g. "A-134", to pick) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations to fall back on, the description thoroughly discloses behavioral traits: refusal over guessing, 'unresolved' status on ambiguity, handling of reused numbers, building-qualified tags, delta/REV tags and their effect (post-revision answer), and the image coordinate system (px at render scale 2.0, top-left origin, y down). This is far beyond what annotations would provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single dense paragraph, but logically ordered: from plan tag to schedule rows to finish codes, then evidence and edge cases. Each sentence adds necessary information. It is long but appropriately packed with essential behavior for a complex tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (multi-step resolution, continuation sheets, ambiguity handling, building-qualified tags, revision markers, coordinate system), the description covers all crucial context. It explains edge cases, coordinate details, and the link to view_sheet. The existing output schema handles return values, so the description is complete for selection and invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single parameter 'tag' is fully described in the schema (including examples and building-qualified syntax). The description adds little beyond restating the schema's parameter description, though it does reinforce the resolution context. Since schema coverage is 100%, baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the tool's action: resolving a single room tag across the entire set, tracing through schedule rows and finish definitions, and outputting evidence pointers. It distinguishes itself from sibling tools like sweep or find_text by specifying the multi-step resolution chain, making its purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides strong contextual guidance: it explains when to use the tool (to resolve a room tag end-to-end), how to handle multi-building sets, and that view_sheet should be used to inspect evidence. While it doesn't explicitly name alternative tools for non-resolution tasks, it implicitly distinguishes itself by describing a unique workflow, and the mention of view_sheet for evidence checking gives practical usage direction.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_scaleA
Set a sheet's scale — exactly ONE of: label (a standard scale, e.g. '1/4" = 1'-0"'), upp (real feet per image px), calibrate (two points along a known dimension plus its real feet), or use_detected (adopt the drawn scale note read off the sheet). The detected scale is never applied automatically — setting it is always this explicit call. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| upp | No | Real feet per image px at render scale 2.0 | |
| label | No | A standard scale label, exactly as listed in the error on a miss | |
| sheet | Yes | ||
| calibrate | No | Two points (image px) a known real distance apart, and that distance in feet | |
| use_detected | No | true = adopt the sheet's detected scale |
Output Schema
| Name | Required | Description |
|---|---|---|
| upp | Yes | Real feet per image px at render scale 2.0 |
| label | No | The standard scale label, when set by label or detected note |
| sheet | Yes | |
| source | Yes | |
| warning | No | Present when the sheet carries MULTIPLE distinct scale notes (#153) — enlarged plans/details likely; region measurements under a disagreeing note will warn |
| confirmed | Yes | Always false here: set_scale is the agent surface, and an agent-set scale stays UNCONFIRMED until a human confirms it in the canvas — quantities still flow, wearing the caveat |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the transparency burden. It discloses important behavioral details: coordinates are image px at render scale 2.0 with origin top-left and y-down, and the detected scale is never applied automatically. This goes beyond the schema by explaining the rendering coordinate space. It does not mention side effects like overwriting an existing scale, but the provided context is substantial.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences, front-loaded with the core action, and every sentence adds distinct value: mode enumeration, automatic-detection caveat, and coordinate system. No redundant or vague phrasing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 5 parameters and a nested object, the description covers the essential context: modes, coordinate semantics, and the explicit-call requirement. A return value is not described, but an output schema exists. The description is sufficiently complete for an agent to invoke the tool correctly, though it does not explain how sheet is identified or what happens to prior scale settings.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 80%, so the baseline is 3. The description adds meaning by explaining the coordinate system for calibrate points, giving an example label format, and clarifying that use_detected adopts the drawn scale note. It also clarifies the 'exactly as listed in the error' for label, which helps with validation. This exceeds baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'Set a sheet's scale'. It then enumerates the four mutually exclusive modes (label, upp, calibrate, use_detected), clearly distinguishing this tool from measurement or detection siblings. The added note that the detected scale is never applied automatically further clarifies its unique role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states 'exactly ONE of' and explains each mode's purpose, giving clear context on when to use which option. It also warns that detected scale is never automatic, so the agent knows to call this tool explicitly to adopt it. However, it does not name alternatives or state when not to use this tool, so it stops short of full exclusionary guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sheet_contextA
The sheet's STRUCTURE in one call and one frame: the classified vector segments, the positioned text spans, and the hatch-family instances of a region — everything the engine itself floods against, exposed as data instead of pixels. Use it when you need to REASON about a region rather than look at it: which lines bound this space and at what pen weight, what the region says, and which periodic fill pattern covers it. The join is the point — all three arrive in image px with no reconciliation left to do, and the reply echoes the post-clamp region so passing that same rect to view_sheet gives you the matching render by construction. Hatch families carry a content-derived id (same pattern spec ⇒ same id, anywhere on the sheet), so matching a plan region to a legend swatch is comparing two ids, not guessing from a render — read the legend region, read the room region, match ids, and cite both bboxes as evidence. Decimation is declared, ordered, and counted on every reply: segments shorter than min_len_px drop first (invisible ink), then a max_segments cap applies LONGEST-FIRST so walls survive and hatch strokes go; kept + dropped always reconciles to total_in_region, and whole segments drop with their meta intact — nothing is ever simplified or merged, because these are classified segments and a merge would rewrite the classification. A scan returns has_vector_linework: false with empty vectors — absence of linework, never a claim the region is blank. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| sheet | Yes | ||
| region | No | Rect in image px (origin top-left, y down); omit for the full sheet | |
| min_len_px | No | Drop segments shorter than this (default 2 — one PDF point at render scale 2.0, below any pen width). 0 keeps everything | |
| max_segments | No | Segment cap, applied longest-first (default 4000). The reply's dropped.cap says exactly what a smaller region would recover |
Output Schema
| Name | Required | Description |
|---|---|---|
| page | Yes | |
| text | Yes | |
| hatch | Yes | |
| sheet | Yes | |
| region | Yes | The region actually resolved, post-clamp — pass this same rect to view_sheet and the render is in the same frame by construction |
| vectors | Yes | |
| sheet_px | Yes | |
| has_vector_linework | Yes | false = a scan: vectors and hatch are empty because there are none, not because the region is blank |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and excels: it details decimation ordering (longest-first, whole segments drop), the meaning of has_vector_linework:false, coordinate space, and the non-mutating nature ('exposed as data instead of pixels'). It also accounts for reconciliation of kept/dropped counts, leaving no ambiguity about side effects or edge behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but dense, with each sentence adding unique information about decimation, coordinate systems, or usage. It is slightly repetitive about image px and top-left origin, but this emphasis is justified given how often coordinate mistakes cause errors. No filler or tautology.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description fully covers what the tool returns, how decimation works, how the output relates to view_sheet, and the semantics of hatch IDs. Given the complexity (three data types, coordinate conversion, decimation policy), it leaves no significant gaps for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema covers 75% of parameters, and the description adds semantic depth beyond the schema: it explains that min_len_px default corresponds to one point at render scale 2.0, and that max_segments cap is applied 'longest-first' so walls survive. This complements the schema's terse descriptions, though the schema already provides solid baseline coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns structural data (classified vector segments, positioned text spans, hatch-family instances) for a region, distinguishing it from rendering tools like view_sheet. The resource and scope are specific, and the emphasis on the integrated response makes the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says 'Use it when you need to REASON about a region rather than look at it' and mentions view_sheet as the counterpart for visual rendering. It also provides a concrete usage pattern (matching plan regions to legend swatches via hatch IDs), leaving no doubt about when to apply this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sheet_graphA
The plan-set INDEX (#87): every sheet's role (plan / schedule / legend / …, with confidence and the title evidence), the schedule tables found (kind, row count, region — a schedule CONTINUED across sheets ("… SCHEDULE — CONT'D") reads as ONE table, the continuation fragment naming its base in "continues"; rotated column headers are read at their quarter-turn and flagged), every number CORROBORATED as a room (with the stacked room NAME when one exists, the room's BUILDING on multi-building sets, and "corroboration" saying why it counts as a room) plus "unmatched_tags" — the numbers that are NOT rooms (keynote hexagons, detail markers, dimension fragments, legend rows), each with a reason, listed and never dropped; READ those reasons, one of them may be a room the schedule left out, the detail callouts (3/A-601 → sheet edges), the set's building designators, every REVISION marker the set carries (text markers "Δ2"/"REV 2" AND drawn deltas — a bare digit inside a triangle of linework, proven from vector geometry and flagged drawn — in "revisions", and attached to the schedule row / room tag they sit on), and named indexing gaps in "notes". Built once per document from the text layer and cached. This is how an agent decides WHAT to measure without a human enumerating the rooms: list the rooms here, resolve each with resolve_tag, then measure with one_click/detect_rooms. A scanned set (no text layer) returns available: false — unavailable, never half-populated. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | No | Named gaps found while indexing (e.g. a continuation whose rows could not be aligned) — the graph refuses silently dropping anything |
| rooms | Yes | Numbers CORROBORATED as rooms — a room-finish row answers for them, or (where the set carries no room-finish schedule) a room name is drawn with them. Each says which in `corroboration`. Schedule sheets contribute rows, never phantom rooms |
| counts | Yes | |
| sheets | Yes | |
| callouts | Yes | Detail callouts (3/A-601) — edges to their target sheets |
| available | Yes | false = the set has no text layer (a scan) — the graph degrades to unavailable, never half-populates |
| buildings | No | Every building designator the set names (sorted) — present only on multi-building-aware sets. Room numbers reused across these need qualified tags ('A-134') |
| revisions | No | Every delta-triangle / REV-tag marker the set carries — text markers ('Δ2', 'REV 2') and DRAWN deltas (a bare digit inside a triangle of linework, drawn: true) — where one sits, the ink changed under that revision. Markers on a schedule row or room bubble also attach there (and ride resolve_tag). A revision CLOUD is arc-chain linework these detectors do not read — absence here is not absence of revisions |
| unmatched_tags | No | Numbered tags on plan sheets that are NOT counted as rooms — keynote hexagons, detail markers, dimension fragments, legend rows. Listed with a reason, never dropped. A real finish plan is covered in 2–3 digit numbers that are not rooms; counting them as rooms makes every one come back "no schedule row", which reads exactly like the lost-bid case and buries it |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure — and it fully delivers. It reveals observable behavior well beyond the schema: caching and build-once semantics ('Built once per document from the text layer and cached'), atomic availability ('unavailable, never half-populated'), the exact pixel-vs-point coordinate system ('PDF pt × 2, origin top-left, y down'), and the way it treats table continuation and rotated headers. This is exactly the kind of context annotations would otherwise need to carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
This is a dense run-on wall of text — about 250 words with heavy parenthetical nesting, ALL-CAPS emphasis, and em-dash on em-dash — with no bullet points or line breaks. Every sentence does add unique information, but the format makes it difficult for an agent to scan or parse. This is the description's one real weakness: thorough to the point of being overwhelming with zero visual hierarchy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the high complexity of this tool (a full plan-set index with sheets, schedules, rooms, unmatched tags, detail callouts, revision markers, and building designators) plus an available output schema that it complements rather than duplicates, the description is remarkably complete. It covers content, failure modes, coordinate spaces, caching semantics, and pipeline position — leaving virtually no question about what the agent receives or when the tool is appropriate.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With zero parameters, the baseline is 4 per calibration; the description goes well beyond the baseline by thoroughly documenting the rich return structure an agent must understand — including the shape of 'corroboration', the 'unmatched_tags' array with reasons, and the revision markers. Since there are no parameters to document, the tool's entire semantic weight is in its output, and the description maps that out completely.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description is unambiguous about what this tool is: 'The plan-set INDEX (#87): every sheet's role (plan / schedule / legend...)' — a specific noun (index) with an enumerated scope (sheet roles, schedule tables, rooms, unmatched tags, revisions). It clearly distinguishes itself from siblings by positioning itself as the read/preparation step: 'This is how an agent decides WHAT to measure without a human enumerating the rooms.' The scope and resource are specific and concrete, not a restatement of the name.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use and workflow guidance is provided: 'list the rooms here, resolve each with resolve_tag, then measure with one_click/detect_rooms.' The failure case is explicitly governed ('A scanned set (no text layer) returns available: false — unavailable, never half-populated'), and the warning to 'READ those reasons, one of them may be a room the schedule left out' gives the agent a concrete decision aid. Alternative sibling tools (resolve_tag, one_click/detect_rooms) are named directly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sheet_infoA
Sheet detail: dims (px and pt), vector segment count, whether the sheet has vector linework (one_click floods it when present; a scanned sheet falls back to rendered pixels, disclosed as raster_traced), scale status, the detected scale suggestion, and this sheet's committed shape count. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| sheet | Yes | Sheet key ("plan.pdf", "plan.pdf#2") or title-block number ("A-101") |
Output Schema
| Name | Required | Description |
|---|---|---|
| upp | No | Real feet per image px at render scale 2.0 — present once the scale is set |
| page | Yes | 1-based page number |
| sheet | Yes | Sheet key: page 1 is the bare file name ("plan.pdf"), pages 2+ are "plan.pdf#2" |
| layers | Yes | The sheet's PDF layer table (#85) — [] when no Optional Content survived export (every engine path then runs the heuristics unchanged) |
| width_pt | Yes | |
| width_px | Yes | Image px at render scale 2.0 — the coordinate space every tool speaks |
| height_pt | Yes | |
| height_px | Yes | |
| scale_set | Yes | |
| seg_count | Yes | Vector segment count |
| shape_count | Yes | Committed shapes on this sheet |
| sheet_number | No | Title-block sheet number ("A-101") where detected |
| detected_scale | No | Drawn scale note read off the sheet — a suggestion, never auto-applied |
| multiple_scales | No | Several DISTINCT scale notes on this sheet (#153) — enlarged plans/details likely |
| has_vector_linework | Yes | one_click needs vector linework |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden, and it does so well by disclosing the coordinate system (image px at render scale 2.0, origin top-left, y down) and that the payload includes both px and pt dimensions. It also transparently notes the raster_traced fallback for scanned sheets, adding behavioral context beyond what a simple 'get info' would imply.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded with 'Sheet detail:' followed by a dense list of attributes. The two additional sentences on coordinate space and payload dimensions are valuable and non-redundant; every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has a simple single parameter and an output schema (not shown), the description covers the necessary context: what values are returned, what the coordinate system is, and how the vector/raster distinction affects downstream tools. It is complete for an information-retrieval tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already documents the single 'sheet' parameter with 100% coverage, giving examples of sheet keys and title-block numbers. The description does not add further meaning about the parameter, so the baseline score of 3 is appropriate; the schema fully handles parameter semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Sheet detail:' and enumerates specific attributes (dims, vector segment count, linework type, scale status, shape count), making the tool's purpose unmistakable. It also references the sibling tool one_click, which distinguishes its role in diagnosing whether a sheet will flood or fall back to raster tracing.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains that sheet_info reports whether vector linework is present, which is directly relevant to whether one_click will flood or use rendered pixels. This provides clear context for when to consult sheet_info before invoking one_click, though it stops short of an explicit 'use this when...' or naming other alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
split_conditionA
Cut a twin loose from its family: every following material row freezes at its current values and edits to the original stop reaching it. It keeps its finish tag and still groups with its siblings — only the inheritance ends. Use when two variants have diverged far enough that following one another is wrong. A condition that already owns its materials returns split:false rather than erroring. Reversible with undo_last.
| Name | Required | Description | Default |
|---|---|---|---|
| condition | Yes | Finish tag of the twin to split, e.g. 'CPT-1 – Level 2' |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | Yes | |
| split | Yes | false = it already owned its materials; nothing was following |
| condition | Yes | |
| family_id | No | Kept — it still groups with its siblings |
| frozen_rows | Yes | Following rows frozen at their current values |
| condition_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does excellently. It discloses key behavioral consequences: following rows freeze, edits stop propagating, finish tag is kept, grouping remains, and the operation is reversible with undo_last. It also explains the non-erroring split:false case, which is valuable safety information.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact yet information-dense, using four sentences to cover purpose, effects, usage guidance, edge-case behavior, and reversibility. Every sentence earns its place with no filler or repetition of schema details.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with an output schema, the description provides comprehensive context: what happens to the data, when to invoke it, what happens in a specific edge case, and how to undo it. The existence of an output schema means return-value details are not required in the description.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already provides 100% coverage for the single 'condition' parameter with a clear description and example. The tool description reinforces the concept by repeatedly referencing 'finish tag' and 'twin', adding some semantic context, but does not significantly exceed what the schema documents.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the specific action ('Cut a twin loose from its family') and the resource (a condition/twin), while detailing the effect: inheritance ends, materials freeze. It distinguishes itself from sibling tools like duplicate_condition by explaining that this tool severs inheritance rather than duplicating.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides explicit use case: 'Use when two variants have diverged far enough that following one another is wrong.' It also describes behavior for an edge case (already-owned condition returns split:false) and reversibility, but does not explicitly mention when not to use it or name alternative tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
sweep_schedule_rowA
Take off a schedule row's mark from the row itself — the estimator's own gesture: a transition type sometimes exists only as a schedule row plus tag markers scattered across the plan sheets, and this tool mints the condition FROM the row and finds every occurrence. Pass the row's key (e.g. 'T1') and the tool (1) reads the row from the set's schedule tables (the sheet_graph/find_schedule machinery — the row is the condition's cited source), (2) anchors a geometric fingerprint on the marker the tag is DRAWN as on a plan sheet (a deterministic pad ladder around the tag text; where the tag occurs more than once the fingerprint must recur at a second occurrence before it is trusted — anchor.corroborated), and (3) sweeps every PLAN-role sheet for it. The count is geometry AND text agreeing: drafting reuses one bubble shape across many marks, so a match counts ONLY when the row's own tag sits within the marker footprint (its bbox rides the match as tag_at evidence); a match labeled with a SIBLING row's tag is excluded and says whose it is, an unlabeled match is withheld as a question, and a tag drawn with no matching marker is disclosed as text_only. REFUSAL over guessing, with the reason and the fix: no such row; the same key in two tables (ambiguous); a tag drawn on no plan sheet; no repeatable marker linework around the tag — a fingerprint is never guessed from text alone (the fallback is always: marquee one instance with symbol_sweep). commit: true commits the counted matches as EA markers under the row's own key — one undo step for the whole set-wide sweep, every marker carrying origin.assignment {source: "schedule"} plus the anchor and row citation on origin.symbol.seed. The COUNT is scale-free (EA), but matching is not: where the anchor sheet and a target sheet both carry a scale, the marker is resized by their exact ratio before matching (scaled per sheet), and where one does not, the sweep runs at 1:1 and discloses it (scale_assumed) rather than reporting a confident zero. After committing, LOOK: view_sheet {overlay: true} over each swept sheet. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | Yes | The schedule row's key exactly as drawn, e.g. 'T1', 'TR-2' — it becomes the condition tag on commit | |
| commit | No | Commit every counted match as one EA count marker (excluded/withheld/text_only never commit) | |
| mirror | No | Also match mirrored markers | |
| rotations | No | Also match 90/180/270-rotated markers | |
| tolerance_px | No | Endpoint match tolerance in image px (default 2 — CAD jitter, not drift) |
Output Schema
| Name | Required | Description |
|---|---|---|
| row | Yes | The schedule row the sweep was seeded from — the condition's source |
| tag | Yes | The row key as normalized (the tag as drawn) |
| note | No | |
| found | Yes | Matches carrying the row's own tag — the honest count, across every plan sheet |
| anchor | Yes | |
| sheets | Yes | One entry per swept PLAN-role sheet, load order |
| skipped | Yes | Sheets excluded from counting (schedule/detail/legend/unknown), each with its reason |
| warning | No | Present when the per-sheet work cap dropped candidates |
| complete | Yes | True when every proposed placement was scored on every swept sheet — false means at least one sheet's count is a FLOOR, not a total (#261) |
| ea_total | No | |
| committed | No | commit mode: count shapes committed — one per counted match, the whole sweep ONE undo step |
| condition | No | commit mode: the condition minted FROM the row — its key is the tag |
| shape_ids | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full transparency burden and surpasses expectations. It discloses commit semantics ('commits the counted matches as EA markers under the row's own key — one undo step'), refusal conditions, the `scale_assumed` disclosure instead of a confident zero, and coordinate-space details. The section on what counts vs. is excluded vs. withheld is particularly detailed and honest about the system's behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
At ~400 words, this is a long description, but nearly every sentence earns its place — covering match-counting rules, scale behavior, refusal conditions, and coordinate systems without repetition. The density of unique information justifies the length, though a short example or heading structure could aid scannability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with this level of algorithmic complexity — fingerprint anchoring, corroboration, scale handling — the description explains return evidence (`tag_at`), edge cases (`text_only`, `scale_assumed`), the fallback path, and even post-commit verification steps. The presence of an output schema offsets the need to document return values in detail, and the description fills in the behavioral gaps thoroughly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% across 5 parameters, satisfying the baseline-3 standard. The description enriches parameter understanding by explaining how `tag` becomes the condition tag, how `commit: true` interacts with match types (excluded/withheld/text_only never commit), and how `mirror`/`rotations`/scale affect matching. The parameter semantics benefit from the description, elevating it above the minimum baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Take off a schedule row's mark from the row itself' and details a specific verb+resource with an exact 3-step process (reads/binds the row, anchors a fingerprint, sweeps all PLAN-role sheets). It explicitly distinguishes itself from the sibling `symbol_sweep` by naming it as the fallback, which differentiates it from a tool in the same domain.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit context for when to use this tool — the estimator's gesture of a transition type living only as a schedule row plus tag markers — and lists concrete refusal/fix scenarios (no such row, ambiguous duplicate keys, no plan sheet, no repeatable linework). It also names the alternative `symbol_sweep` as the fallback path. It could go further in contrasting with additional siblings like `resolve_tag` or `find_schedule`, but the usage context is otherwise strong.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
symbol_sweepA
Find EVERY instance of a repeated plan symbol from ONE example — drains, thresholds, fixtures, transition markers: marquee a tight seed_rect around a single instance and the vector linework is searched for every other placement of that same segment cluster. Deterministic geometry, not vision: each placement scores as the length-weighted fraction of the seed's segments reproduced within tolerance_px, under translation plus 0/90/180/270 rotation and mirroring (symbols rotate on plans — both ON by default; turn them off to pin orientation). Score ≥ 0.92 is a match; the 0.75–0.92 band comes back in withheld with a reason — a near-match is a question you answer by LOOKING (view_sheet at its at), never a silent commit and never a silent drop. The seed's own location is reported in seed and never double-committed. Every proposed placement is scored up to a hard work ceiling sized for pathological sheets, and the reply says which it was: complete true means the count is a total; complete false (with candidates.dropped > 0) means the count is a FLOOR — some placements were never scored — so tighten the seed rect around more distinctive geometry rather than trusting it as a total. Marquee discipline: the rect must hug ONE instance — only segments FULLY inside it define the symbol, so a loose rect that swallows wall linework fingerprints the wall, not the symbol. scope "set" sweeps the WHOLE working set, counting on PLAN-role sheets only (the sheet graph decides): a symbol drawn in a detail, legend, or schedule is a reference drawing and never counts itself — which is also how you seed from one: marquee the assembly on the detail sheet and its plan-sheet occurrences are counted while the detail stays excluded (the exclusion disclosed in skipped, per-sheet results with per-sheet caps and wall-clock in sheets). Scale across sheets: the fingerprint is size-true and is never scale-SEARCHED, so a detail drawn at 1-1/2" = 1'-0" is 12× the size of the same mark on a 1/8" plan — when BOTH sheets have a scale set, the exact ratio is computed from them and the seed is resized before matching (reported per sheet as scaled); when a scale is missing, the sweep runs at 1:1 and SAYS so (scale_assumed), because an unknown ratio plus a zero count is not evidence of absence. Seeding from a detail/legend/schedule sheet REFUSES outright until both scales are set — that is the case where an unstated ratio silently finds nothing. commit: true (requires condition) commits every match center as an EA count marker through the same path as place_count — the whole sweep (set-wide included) is ONE undo step, each marker carries origin.method "symbol_sweep" with its score, transform, and seed source, and withheld placements are NEVER committed. The SEED instance is not in that count (#296) — in sheet scope it is almost always installed work, so pass commit_seed: true to mint it into the same batch (the reply reminds you whenever a sheet-scope commit leaves it out; ea_total one short of the hand tally is exactly this). The COUNT is scale-free (EA), but matching across sheets of different scales is not — set_scale on the sheets involved is what turns the ratio from an assumption into arithmetic. Counter-examples (#259): drafting reuses one generic shape for different devices — a wall-mounted data outlet drawn as a plain triangle, the flush-floor variant the SAME triangle inside a square, keynote callouts a triangle with a letter in it — so the seed legitimately matches things you do not mean, and seeding more geometry only works where the drawing offers more to capture. exclude takes rects around instances you do NOT mean, marqueed exactly like the seed. You never choose a mechanism; the rect's contents decide, because both are the same gesture: a rect holding EXTRA linework beyond the seed rejects placements where that extra linework is present too (the box, the letter), and a rect holding no extra linework of its own is read as the line running THROUGH it — a bare ceiling-grid tile whose grid line a real fixture, drawn over it, would BREAK. That second mechanic is not expressible as a seed: only segments fully INSIDE a rect define a symbol, and background structure is long by nature. Every rejection is disclosed in rejected[] — which negative, what fraction of its evidence was found, and the placement — and NEVER counted in found: an exclusion is a judgement, so look at it and reinstate any you disagree with using place_count at its at. A counter-example that holds no instance of the seed, or holds the seed with nothing extra, is REFUSED rather than silently doing nothing. Stroke luminance (#260): a flattened export strips the layer tree and flattens every pen, but the file still STATES stroke color — a black fixture outline over a grey ceiling grid is unambiguous there even when the geometry is identical (two empty 2 ft grid tiles reproduce a 2×4 fixture's outline exactly). luminance_tolerance (0–254) gates on it: a sheet segment only answers for a seed segment when their stroke luminances are within the stated tolerance (Rec. 709, 0 = black, 255 = white; 32–64 separates black from grey without touching anti-aliasing wobble). OPT-IN and disclosed, in the spirit of tolerance_px — omitted, sweeps score exactly as before; stated, the reply's lum_gate says the seed's own luminance band and names every placement the geometry would have committed and the pen did not, so you can LOOK at what a stated gate cost. Prefer geometry (a counter-example, a tighter seed) where the drawing offers it — color is the fallback for exports where nothing else survived. Labels (#308): for a LABELED family — fixtures, tagged equipment, keyed devices — the drawing already names every instance, and the sweep reads those names: a fixture token written beside a placement, or connected to it by a drawn leader line (leader-following arms only on multi-pen sheets, where the annotation pen separates from the work), comes back as label + label_via on the row, and the seed's own tag rides seed.label. Disclosure in both directions, never a recount: a committed match with NO label while the family is labeled was counted on shape alone (measured case: two 0.97 matches that were valve internals, not drains — LOOK at those first), a withheld row carrying the seed's own tag is the drawing vouching for a near-miss (look, then place_count), and a withheld row named a DIFFERENT tag is a sibling fixture answered, not a missed count. After any batch commit, LOOK at what landed — view_sheet {overlay: true} over the swept area — and audit the markers against the drawing before trusting the EA total. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| scope | No | "sheet" = this sheet only; "set" = every PLAN-role sheet in the working set (needs a text layer for the sheet graph; non-plan sheets are excluded and disclosed) | sheet |
| sheet | Yes | The sheet the seed rect sits on — in scope 'set' it may be ANY sheet (a detail/legend seed sheet is fingerprint source only, never counted) | |
| commit | No | Commit every MATCH center as one EA count marker (withheld placements never commit) | |
| mirror | No | Also match mirrored placements | |
| exclude | No | Counter-examples: rects around instances you do NOT mean, same gesture as seed_rect — 'count the triangles, not the keynote ones'. Marquee the LOOKALIKE ITSELF (the flush-floor variant with its box, the keynote triangle with its letter) or an EMPTY position whose background line a real instance would break (a bare ceiling grid tile). You never say which kind it is: the rect's own contents decide. Every rejection comes back in rejected[] with which negative did it and what it saw | |
| condition | No | Finish tag to commit match markers under (minted on first use), e.g. 'FD-1'. Required when commit is true | |
| rotations | No | Also match 90/180/270-rotated placements | |
| seed_rect | Yes | Marquee around ONE example instance, [[x0,y0],[x1,y1]] in image px — tight: segments fully inside define the symbol | |
| commit_seed | No | Sheet scope + commit only (#296): also commit the SEED instance — in sheet scope the seed is almost always installed work, and a count that excludes it bids one short. Joins the same one-undo-step batch, origin score 1. Refused in set scope, where a detail/legend seed is a reference drawing | |
| tolerance_px | No | Endpoint match tolerance in image px (default 2 — CAD jitter, not drift) | |
| luminance_tolerance | No | Stroke-luminance gate, 0–254 (#260): a sheet segment only answers for a seed segment when their stroke luminances (Rec. 709, 0 black – 255 white) are within this. For flattened exports where a black device and its grey background twin are geometrically identical — 32–64 separates black from grey. Omit to score on geometry alone; stated, the reply's lum_gate discloses the seed's luminance band and every placement the gate pulled under the commit bar |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | |
| seed | Yes | |
| found | Yes | Placements that cleared the commit bar — across every swept sheet in set scope |
| scope | Yes | "sheet" = the swept sheet alone (matches/withheld/candidates at top level); "set" = every PLAN-role sheet in the working set (per-sheet results in sheets[], exclusions in skipped[]) |
| sheets | No | Set scope only: one entry per swept PLAN-role sheet, load order |
| matches | No | Sheet scope only. Deterministic reading order (y, then x). The seed's own location is never listed here |
| skipped | No | Set scope only: every sheet excluded from counting, with role and reason — including the seed's own sheet when it is not a plan |
| warning | No | Present when the work cap dropped candidates — what a tighter seed rect would recover |
| complete | Yes | True when every proposed placement was scored (every swept sheet, in set scope) and the count is a total. FALSE MEANS THE COUNT IS A FLOOR — acknowledge it before trusting found (#261) |
| ea_total | No | commit mode: the condition's total EA after this call |
| lum_gate | No | Sheet scope only. The stated stroke-luminance gate's accounting (#260): the tolerance, the seed's own luminance band, and every placement the geometry would have committed that the pen pulled under the bar — NEVER counted in found, never silent. Set scope accounts per sheet in sheets[] |
| rejected | No | Sheet scope only. Placements the geometry accepted and a counter-example refused (#259) — NEVER counted in found, and never silent: each says which negative did it and what it saw. Reinstate one by hand with place_count at its `at` if you disagree |
| withheld | No | Sheet scope only. Near-matches in the [0.75, 0.92) band — reported with a reason, NEVER committed. A withheld placement is a question you can answer with view_sheet; a hidden one is a miscount |
| committed | No | commit mode: count shapes committed — one per match |
| condition | No | commit mode: the finish tag the markers counted under |
| negatives | No | What each `exclude` rect was read as, in the order you passed them (#259) |
| shape_ids | No | |
| candidates | No | Sheet scope only — set scope accounts per sheet in sheets[] |
| rejected_total | No | Set scope: placements counter-examples rejected across every swept sheet |
| seed_committed | No | Present when commit_seed: true minted the seed instance into the batch (#296) — ea_total then includes it |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With zero annotations, the description carries the full disclosure burden and fully satisfies it: internal watch-scores (0.92 vs 0.75–0.92 band), the floor-when-complete-false semantic, the hard work ceiling, the one-undo-step batch commit behavior, refusal cases, the coordinate system, and the scale-resize/scale-assumed report. There is no hidden behavior an agent would learn only after a risky experiment.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The first sentence front-loads the primary purpose and the rest organizes by theme (scope, scale, commit, exclude, luminance, labels, coordinates) — every sentence carries a distinct fact. The description is very long, organized in long unbroken paragraphs rather than compact sections, so a scanning agent must read a large block to extract the key thresholds. Length is proportionate to the tool's real complexity, so this is fair rather than a defect.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 11 parameters, special scoping and disclaimers, the description plus an output schema covers everything: the threshold semantics, the floor-vs-total disambiguation, refusals, scale handling, exclusion disclosure, the commit/undo guarantee, and coordinates in both px and pt. An agent has everything needed to call this tool correctly even in binary sheet-scale edge cases.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, which sets the description-bar baseline at 3, but the shared vocabulary goes beyond it: it prescribes 'tight seed_rect around a single instance' and explains the fully-inside-are-seed behavioral, the empty-rect exclusion mechanic, the commit_seed sheet-only-#296 rule, and the concrete 32–64 luminance band as a semantic key for black-vs-grey stroke separation. Not a 5 because the schema already documents each parameter accurately and the extra value is refinement, not basic understanding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource statement: 'Find EVERY instance of a repeated plan symbol from ONE example', then names the matching mechanism (deterministic geometry, not vision), the scope modes (sheet/set), and the score threshold. It also distinguishes itself from the sibling manual counter place_count by stating it uses 'the same path' only for the commit step.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use and when-not-to-use guidance: a detail/legend/schedule seed is refused until both scales are set, a missing scale degrades to a disclosed 1:1 run, and the 'complete false' branch tells the agent to tighten the seed rather than trust the count as a total. It also routes to more specific alternatives like place_count for reinstatement and view_sheet for LOOKING at WITHHELD and exclusion candidates.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
takeoff_summaryA
Per-condition totals (floor/wall/border SF, LF, EA, SY, with and without waste) plus grand totals — the Report's numbers, computed by the same rules. Numbers only: the deliverable that SHOWS the work on the drawings is export_marked_pdf. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| totals | Yes | |
| conditions | Yes | |
| scale_unconfirmed | No | Sheets whose scale is agent-set and no human has confirmed — these totals stand on an unverified scale; verify against a stated dimension or confirm in the canvas |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full transparency burden and delivers meaningful behavioral context: numbers-only output, coordinate space in image px at render scale 2.0 (PDF pt × 2, origin top-left, y down), and sheet payloads carrying dims in both px and pt. It lacks only an explicit statement about side effects or read-only behavior, but for a zero-parameter summary compute this is reasonably covered.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core computation and unit types, followed by the key sibling differentiator, then the coordinate-system context. Each sentence contributes distinct information — purpose, numbers-only boundary with alternative, coordinate convention, and payload dims — with zero filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter tool with an output schema present, this description is complete: it covers what is computed, the units and waste variants, the numbers-only deliverable boundary, the coordinate system (px, scale 2.0, origin top-left), and the px/pt relationship in sheet payloads. An agent has everything needed to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has zero parameters, so the description can't add parameter-level meaning; per the rubric, 0 params gets a baseline of 4. The description adds useful return-value context (px/pt dimensions, scale, origin) instead, which is outside the empty schema's scope.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the tool's output: per-condition totals (floor/wall/border SF, LF, EA, SY, with/without waste) plus grand totals, tied to 'the Report's numbers.' It explicitly differentiates from the sibling export_marked_pdf by declaring 'Numbers only,' but it doesn't distinguish from other numeric siblings like export_report or export_takeoff, and the verb is implicit rather than explicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives an explicit when-not-to-use signal: 'Numbers only' and 'the deliverable that SHOWS the work on the drawings is export_marked_pdf,' directing agents to the visual alternative. However, it doesn't address other relevant siblings (export_report, export_takeoff, sweep_schedule_row), so usage guidance is partial rather than comprehensive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
undo_lastA
Step back over your OWN last n mutations, newest first — a committed one_click, a whole detect_rooms sweep, an edit_shape, a delete_shape, an edit_materials call, or an edit_condition call. Each step is reversed exactly (a commit is removed, an edit is restored verbatim, a delete is re-inserted where it was, a materials edit's whole array is restored, a condition edit's waste/multiplier pair is restored), so this restores state rather than approximating it. Reads are never journaled, so n counts gestures that changed something, not tool calls you made. Use it when a sweep committed against the wrong condition or a batch went in on the wrong sheet — one call instead of N deletes. Scope: this session's own history only. It is not the browser canvas's undo stack, and load_plan clears it along with the shapes it refers to.
| Name | Required | Description | Default |
|---|---|---|---|
| n | No | How many steps to reverse (1–100) |
Output Schema
| Name | Required | Description |
|---|---|---|
| note | No | |
| steps | Yes | Newest first |
| undone | Yes | Steps actually reversed |
| remaining | Yes | Steps still available to undo |
| shape_count | Yes | Committed shapes after the undo |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present, the description carries full burden and excels: it discloses the journaling model ('reads are never journaled, so n counts gestures that changed something'), reveals the exactness guarantee per mutation type (restored verbatim, re-inserted, array restored, pair restored), and disclaims approximation ('restores state rather than approximating it'). For a session-internal undo, omitted details like auth or rate limits are not applicable, and there is no contradiction with structured data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
There is no dead weight — every sentence carries semantic load, and scoping comes first. However, the opening sentence is a run-on that buries the type enumeration in a long parenthetical, and the emphatic 'your OWN' adds tone without information. A judicious trim (~30%) would make it tighter, but this is well above average.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given zero annotations and the inherent complexity of an undo-with-journaling semantics, the description covers semantics, scope, state-reset triggers (load_plan), and usage signals. The presence of an output schema removes the need to document return values, and the only genuine gap — behavior on an empty history — is a minor edge case.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% (n: integer, 1–100, default 1), so the baseline is 3 even with no extra description. The description earns the bump by clarifying what n actually counts ('gestures that changed something, not tool calls you made') and hinting at batching via 'one call instead of N deletes.' It doesn't reach 5 because the schema already handled the mechanics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a clear verb+resource+scope ('Step back over your OWN last n mutations, newest first') in the very first phrase, so an agent immediately knows what it does. It explicitly differentiates itself from a likely source of confusion ('It is not the browser canvas's undo stack') and grounds every example type (one_click, detect_rooms, edit_shape...) in actual sibling tools, making the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides concrete trigger scenarios ('a sweep committed against the wrong condition or a batch went in on the wrong sheet') with an economics argument for choosing it ('one call instead of N deletes'). Explicitly scopes itself away from alternatives: not the browser canvas undo, session-only history, and invalidated by load_plan. This is exactly the when/when-not guidance the rubric asks for.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
view_sheetA
SEE the sheet — render the page (or a crop of it) to a PNG image. This is your eyes on the plan, so CROP, DON'T SQUINT: the render downsamples to the px budget (≤2000 long side), which on an E-size sheet is ~4 sheet pixels per returned pixel — a full-sheet render finds WHERE things are, and only a tight region crop can tell you what the linework and labels actually say. Never audit a trace or read a dimension off a full-sheet render. region is in image px — the same space as every other tool — so a feature at pixel (ix, iy) of the returned image sits at x = region_x0 + ix × (region_x1 − region_x0) / img_w (same for y), and those coordinates go straight into one_click, measure_polygon, or read_sheet_text. overlay:true burns the session's committed shapes into the render (human-affirmed ink solid red, unreviewed machine shapes dashed blue) — render again after committing to verify your geometry landed where you intended, and sanity-check what you see: a fixture-sized ring where a room should be means the seed landed inside a stall or casework; an outsized ring means the flood escaped through an opening. To MEASURE rather than guess, pass grid: a calibrated measuring grid is burned in — thin lines every 1 ft, heavy blue every 5 ft, foot labels along the crop edges, feet counted from the crop's top-left corner. Count grid cells between walls exactly like an estimator scaling a plan; never derive a dimension by eye when the grid can give it to you. grid "auto" uses the sheet's set scale; before set_scale, pass the drawing scale read off the title block as inches-per-foot — "1/4" for a 1/4" = 1'-0" plan, "3/16", "0.25". marks (#297) burns DISCLOSURE layers into the render, so what a reply names, the picture shows: pass the coordinate lists a tool disclosed — question: withheld placements (orange ?-circles), struck: rejections a counter-example or luminance gate refused (magenta struck ×), ring: reference points like the sweep's own seed (violet double ring). The colors sit deliberately off the common CAD pens so they cannot vanish into color-plotted work. An overlay audit without marks shows only committed ink — the validation trap where 37 disclosed near-misses read as "it missed them". Rendering needs the optional native canvas (@napi-rs/canvas); where it isn't installed this tool errors cleanly and every other tool still works. Coordinates are image px at render scale 2.0: PDF pt × 2, origin top-left, y down (the browser canvas's native space). Sheet payloads carry dims in both px and pt.
| Name | Required | Description | Default |
|---|---|---|---|
| px | No | Long-side pixel budget of the returned image (default 1400) — small region + high px = readable dimension strings | |
| grid | No | Burn in a calibrated 1-ft/5-ft measuring grid: "auto" = the sheet's set scale; otherwise the drawing scale as inches-per-foot, e.g. "1/4", "3/16", "0.25" | |
| marks | No | Disclosure marks to burn into the render (#297): what the reply names, the picture shows. Coordinates in image px | |
| sheet | Yes | ||
| region | No | Crop rect in image px (origin top-left, y down); omit for the full sheet | |
| overlay | No | Burn committed shapes into the render (solid = human-affirmed, dashed = unreviewed) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral load. It discloses the 2000-px long-side limit, the coordinate mapping formula, the visual semantics of overlay (solid vs dashed, colors), the grid's meaning (1-ft/5-ft lines, labels), the marks meaning (colors and shapes), and the dependency on a native canvas module with graceful degradation. It also warns about render precision and how to verify geometry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Though long, the description is dense and every sentence carries essential guidance. It is front-loaded with the core purpose and crop guidance, then proceeds logically through coordinate mapping, overlay, grid, marks, and canvas dependency. No fluff; each clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fully explains return behavior (image, pixel coordinate mapping), all parameter effects (grid, overlay, marks), the required canvas dependency with failure mode, and a coordinate conversion formula. The only minor omission is an explicit statement of return type, but it is clearly implied by 'returns a PNG image' and the coordinate discussion. Complete for an agent to call correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Every parameter is explained well beyond the schema's one-liners: region is defined as image px and the coordinate mapping formula is given; grid specifies feet counts and colors; overlay explains solid vs dashed and their meaning; marks details each visual symbol and its purpose. The description makes each parameter operationally clear.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear verb and resource: 'SEE the page (or a crop of it) as a PNG image.' It immediately distinguishes itself from sibling tools by explaining that it is the visual counterpart to measure_polygon and read_sheet_text, and it gives concrete use cases (locating vs reading details). Purpose is unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage guidance is explicit and actionable: full-sheet renders for orientation, tight crops for reading linework/dimensions, and a precise warning never to audit a trace or read a dimension off a full-sheet render. It also tells the agent when to re-render with overlay:true to verify geometry and how to use grid for measurement. Alternatives are named directly (measure_polygon, read_sheet_text, one_click).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
1 tool update
v0.1.20- Added
export_dxf
5 tool updates
v0.1.19- Added
count_marks - Changed
cut_out11 fields changed- changed
Input schema / properties / parent_shape_id / descriptionPrevious value: -"A committed floor_area shape id (list_shapes)"New value: +"A committed floor_area shape id, or an open run (surface_area / linear) to clip (list_shapes)" - changed
Input schema / properties / verts / descriptionPrevious value: -"The hole's ring, image px, fully inside the parent"New value: +"The ring, image px — fully inside the parent for an area; over the stretch to remove for a run" - changed
Output schema / properties / deduct_shape_id / descriptionPrevious value: -"The reconciled deduct — carries cuts_shape_id; totals skip it (the parent nets the hole)"New value: +"Area parent: the reconciled deduct — carries cuts_shape_id; totals skip it (the parent nets the hole)" - changed
Output schema / properties / hole_sf / descriptionPrevious value: -"What this cut actually removed from the parent's net — 0 when the ring fell entirely inside an existing hole"New value: +"Area parent: what this cut actually removed from the parent's net — 0 when the ring fell entirely inside an existing hole" - changed
Output schema / properties / holes / descriptionPrevious value: -"Holes the parent now carries"New value: +"Area parent: holes the parent now carries" - added
Output schema / properties / measure_roleAdded value: +{ + "description": "Run parent: surface_area or linear", + "type": "string" +} - added
Output schema / properties / piecesAdded value: +{ + "description": "Run parent: every stretch that survived the cut — more than one when the ring fell in the middle", + "items": { + "additionalProperties": false, + "properties": { + "lf": { + "description": "This piece's own length", + "type": "number" + }, + "sf": { + "description": "LF × the height (wall) or the thickness (border) it was measured at", + "type": "number" + }, + "shape_id": { + "type": "string" + } + }, + "required": [ + "shape_id", + "lf", + "sf" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / removed_lfAdded value: +{ + "description": "Run parent: length the cut took out", + "type": "number" +} - added
Output schema / properties / removed_sfAdded value: +{ + "description": "Run parent: the SF that rode on that length", + "type": "number" +} - added
Output schema / properties / shape_idAdded value: +{ + "description": "Run parent: the run that was clipped — it keeps its id and takes the first surviving stretch", + "type": "string" +} - changed
Output schema / requiredPrevious value: -[ - "deduct_shape_id", - "parent_shape_id", - "hole_sf", - "parent_net", - "holes", - "note" -]New value: +[ + "note" +]
- Changed
symbol_sweep20 fields changed- added
Input schema / properties / commit_seedAdded value: +{ + "default": false, + "description": "Sheet scope + commit only (#296): also commit the SEED instance — in sheet scope the seed is almost always installed work, and a count that excludes it bids one short. Joins the same one-undo-step batch, origin score 1. Refused in set scope, where a detail/legend seed is a reference drawing", + "type": "boolean" +} - added
Input schema / properties / excludeAdded value: +{ + "description": "Counter-examples: rects around instances you do NOT mean, same gesture as seed_rect — 'count the triangles, not the keynote ones'. Marquee the LOOKALIKE ITSELF (the flush-floor variant with its box, the keynote triangle with its letter) or an EMPTY position whose background line a real instance would break (a bare ceiling grid tile). You never say which kind it is: the rect's own contents decide. Every rejection comes back in rejected[] with which negative did it and what it saw", + "items": { + "items": [ + { + "$ref": "#/properties/seed_rect/items/0" + }, + { + "$ref": "#/properties/seed_rect/items/0" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "type": "array" +} - added
Input schema / properties / luminance_toleranceAdded value: +{ + "description": "Stroke-luminance gate, 0–254 (#260): a sheet segment only answers for a seed segment when their stroke luminances (Rec. 709, 0 black – 255 white) are within this. For flattened exports where a black device and its grey background twin are geometrically identical — 32–64 separates black from grey. Omit to score on geometry alone; stated, the reply's lum_gate discloses the seed's luminance band and every placement the gate pulled under the commit bar", + "maximum": 254, + "minimum": 0, + "type": "integer" +} - added
Output schema / properties / lum_gateAdded value: +{ + "additionalProperties": false, + "description": "Sheet scope only. The stated stroke-luminance gate's accounting (#260): the tolerance, the seed's own luminance band, and every placement the geometry would have committed that the pen pulled under the bar — NEVER counted in found, never silent. Set scope accounts per sheet in sheets[]", + "properties": { + "at": { + "description": "Where each of them is, image px — view_sheet and look before trusting the gate; place_count reinstates one you disagree with", + "items": { + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "type": "array" + }, + "rejected": { + "description": "Placements the geometry alone would have COMMITTED and the gate did not — one entry per physical spot", + "type": "integer" + }, + "seed_lum": { + "description": "The seed's own stroke luminances, deduplicated — the band candidates were held to", + "items": { + "type": "number" + }, + "type": "array" + }, + "tol": { + "description": "The luminance tolerance that was applied, 0–254", + "type": "number" + } + }, + "required": [ + "tol", + "seed_lum", + "rejected", + "at" + ], + "type": "object" +} - added
Output schema / properties / matches / items / properties / labelAdded value: +{ + "description": "The drawing's own tag for this placement (#308) — a fixture token written beside it or connected by a drawn leader (e.g. \"P-7\", \"FD1\"). Disclosure, never a recount: a match with NO label in a labeled family was counted on shape alone (look before trusting), and a withheld row carrying the seed's own tag is the drawing vouching for it", + "type": "string" +} - added
Output schema / properties / matches / items / properties / label_viaAdded value: +{ + "description": "How the tag reached this placement: written beside it, or followed along a drawn leader line (leader-following arms only on multi-pen sheets, where the annotation pen separates from the work)", + "enum": [ + "adjacent", + "leader" + ], + "type": "string" +} - added
Output schema / properties / negativesAdded value: +{ + "description": "What each `exclude` rect was read as, in the order you passed them (#259)", + "items": { + "additionalProperties": false, + "properties": { + "center": { + "description": "Where the seed's own geometry was located inside that rect, image px — what the negative aligned to", + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "mode": { + "enum": [ + "shape", + "crossing" + ], + "type": "string" + }, + "segments": { + "description": "Discriminating segments this counter-example contributes — the linework that is NOT the seed", + "type": "integer" + } + }, + "required": [ + "mode", + "segments", + "center" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / rejectedAdded value: +{ + "description": "Sheet scope only. Placements the geometry accepted and a counter-example refused (#259) — NEVER counted in found, and never silent: each says which negative did it and what it saw. Reinstate one by hand with place_count at its `at` if you disagree", + "items": { + "additionalProperties": false, + "properties": { + "at": { + "$ref": "#/properties/matches/items/properties/at" + }, + "by": { + "description": "Which counter-example rejected it — 1-based index into the `exclude` rects you passed", + "type": "integer" + }, + "evidence": { + "description": "Fraction of that counter-example's discriminating linework found at this placement, 0..1 (rejection bar 0.5)", + "type": "number" + }, + "label": { + "$ref": "#/properties/matches/items/properties/label" + }, + "label_via": { + "$ref": "#/properties/matches/items/properties/label_via" + }, + "mirrored": { + "$ref": "#/properties/matches/items/properties/mirrored" + }, + "mode": { + "description": "What that counter-example was read as. \"shape\": it carries extra linework the seed does not, and that linework is present here too. \"crossing\": it carries no extra linework of its own — what marks it is a line running THROUGH it, and that line runs unbroken through this placement", + "enum": [ + "shape", + "crossing" + ], + "type": "string" + }, + "reason": { + "type": "string" + }, + "rotation": { + "$ref": "#/properties/matches/items/properties/rotation" + }, + "score": { + "$ref": "#/properties/matches/items/properties/score" + } + }, + "required": [ + "at", + "score", + "rotation", + "mirrored", + "by", + "mode", + "evidence", + "reason" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / rejected_totalAdded value: +{ + "description": "Set scope: placements counter-examples rejected across every swept sheet", + "type": "integer" +} - added
Output schema / properties / seed / properties / labelAdded value: +{ + "description": "The drawing's own tag for the seed instance (#308) — the family's identity, e.g. seeding a drain the sheet labels \"P-7\"", + "type": "string" +} - added
Output schema / properties / seed / properties / label_viaAdded value: +{ + "enum": [ + "adjacent", + "leader" + ], + "type": "string" +} - added
Output schema / properties / seed_committedAdded value: +{ + "description": "Present when commit_seed: true minted the seed instance into the batch (#296) — ea_total then includes it", + "type": "boolean" +} - added
Output schema / properties / sheets / items / properties / lum_gateAdded value: +{ + "$ref": "#/properties/lum_gate", + "description": "This sheet's stated-luminance-gate accounting (#260) — present only when luminance_tolerance was stated" +} - added
Output schema / properties / sheets / items / properties / matches / items / properties / labelAdded value: +{ + "$ref": "#/properties/matches/items/properties/label" +} - added
Output schema / properties / sheets / items / properties / matches / items / properties / label_viaAdded value: +{ + "$ref": "#/properties/matches/items/properties/label_via" +} - added
Output schema / properties / sheets / items / properties / rejectedAdded value: +{ + "description": "Placements a counter-example rejected on this sheet (#259) — never counted, always named", + "items": { + "$ref": "#/properties/rejected/items" + }, + "type": "array" +} - added
Output schema / properties / sheets / items / properties / withheld / items / properties / labelAdded value: +{ + "$ref": "#/properties/matches/items/properties/label" +} - added
Output schema / properties / sheets / items / properties / withheld / items / properties / label_viaAdded value: +{ + "$ref": "#/properties/matches/items/properties/label_via" +} - added
Output schema / properties / withheld / items / properties / labelAdded value: +{ + "$ref": "#/properties/matches/items/properties/label" +} - added
Output schema / properties / withheld / items / properties / label_viaAdded value: +{ + "$ref": "#/properties/matches/items/properties/label_via" +}
- Changed
undo_last1 field changed- changed
Output schema / properties / steps / items / properties / op / enumPrevious value: -[ - "commit", - "edit", - "delete", - "materials", - "condition", - "approval", - "duplicate_condition", - "split_condition", - "cutout", - "cutout_restore" -]New value: +[ + "commit", + "edit", + "delete", + "materials", + "condition", + "approval", + "duplicate_condition", + "split_condition", + "cutout", + "cutout_restore", + "runcut" +]
- Changed
view_sheet1 field changed- added
Input schema / properties / marksAdded value: +{ + "additionalProperties": false, + "description": "Disclosure marks to burn into the render (#297): what the reply names, the picture shows. Coordinates in image px", + "properties": { + "question": { + "description": "Open questions — withheld placements, spots to look at. Orange ?-in-circle", + "items": { + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "type": "array" + }, + "ring": { + "description": "Reference points — the sweep's seed.center, an anchor. Violet double ring", + "items": { + "$ref": "#/properties/marks/properties/question/items" + }, + "type": "array" + }, + "struck": { + "description": "Refusals — rejected[] placements, lum_gate.at. Magenta struck ×", + "items": { + "$ref": "#/properties/marks/properties/question/items" + }, + "type": "array" + } + }, + "type": "object" +}
5 tool updates
v0.1.18- Changed
find_schedule1 field changed- added
Output schema / properties / matches / items / properties / revised_rowsAdded value: +{ + "description": "Rows carrying a delta/REV marker — the ink changed there; resolve those tags to see which", + "type": "integer" +}
- Changed
resolve_tag10 fields changed- changed
Output schema / properties / finishes / items / properties / definition / properties / source / $refPrevious value: -"#/properties/finishes/items/properties/source"New value: +"#/properties/room/anyOf/0/properties/revision/properties/source" - added
Output schema / properties / finishes / items / properties / source / $refAdded value: +"#/properties/room/anyOf/0/properties/revision/properties/source" - removed
Output schema / properties / finishes / items / properties / source / additionalPropertiesRemoved value: -false - removed
Output schema / properties / finishes / items / properties / source / descriptionRemoved value: -"An evidence pointer — the sheet, the literal text, and where it sits (image px). Every edge in the graph carries one; pass the bbox to view_sheet to LOOK at the source." - removed
Output schema / properties / finishes / items / properties / source / propertiesRemoved value: -{ - "bbox": { - "$ref": "#/properties/room/anyOf/0/properties/bbox" - }, - "sheet": { - "type": "string" - }, - "text": { - "type": "string" - } -} - removed
Output schema / properties / finishes / items / properties / source / requiredRemoved value: -[ - "sheet", - "text", - "bbox" -] - removed
Output schema / properties / finishes / items / properties / source / typeRemoved value: -"object" - added
Output schema / properties / revisionsAdded value: +{ + "description": "resolved only — delta/REV markers on the answering schedule row or the plan bubble. The finishes above are the POST-revision answer, but the ink changed: check the marker (view_sheet its bbox) and the addendum before pricing", + "items": { + "$ref": "#/properties/room/anyOf/0/properties/revision" + }, + "type": "array" +} - changed
Output schema / properties / room / anyOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "bbox": { - "additionalProperties": false, - "properties": { - "x0": { - "type": "number" - }, - "x1": { - "type": "number" - }, - "y0": { - "type": "number" - }, - "y1": { - "type": "number" - } - }, - "required": [ - "x0", - "y0", - "x1", - "y1" - ], - "type": "object" - }, - "building": { - "description": "The building the room belongs to, when the set names one — its plan sheet's BUILDING/BLDG context, or the tag's own qualifier ('A-134')", - "type": "string" - }, - "name": { - "description": "The name span stacked over the tag ('' when none)", - "type": "string" - }, - "sheet": { - "type": "string" - }, - "tag": { - "type": "string" - } - }, - "required": [ - "tag", - "name", - "sheet", - "bbox" - ], - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "bbox": { + "additionalProperties": false, + "properties": { + "x0": { + "type": "number" + }, + "x1": { + "type": "number" + }, + "y0": { + "type": "number" + }, + "y1": { + "type": "number" + } + }, + "required": [ + "x0", + "y0", + "x1", + "y1" + ], + "type": "object" + }, + "building": { + "description": "The building the room belongs to, when the set names one — its plan sheet's BUILDING/BLDG context, or the tag's own qualifier ('A-134')", + "type": "string" + }, + "corroboration": { + "description": "Why this number is believed to be a room: \"schedule\" (a room-finish row answers for it), \"name\" (a name is drawn with it and the set has no room-finish schedule), or \"name+schedule\"", + "type": "string" + }, + "name": { + "description": "The name span stacked over the tag ('' when none)", + "type": "string" + }, + "revision": { + "additionalProperties": false, + "description": "A revision marker (delta triangle / 'REV 2' tag) attached to this item: the ink CHANGED under that revision. The value read is the post-revision answer — view_sheet the marker's bbox and check the addendum before pricing", + "properties": { + "drawn": { + "description": "true = a DRAWN delta: a bare digit inside a triangle of linework (the common CAD convention — the text layer carries only the digit; the geometry proved the triangle). The evidence bbox spans digit and triangle", + "type": "boolean" + }, + "rev": { + "type": "string" + }, + "source": { + "additionalProperties": false, + "description": "An evidence pointer — the sheet, the literal text, and where it sits (image px). Every edge in the graph carries one; pass the bbox to view_sheet to LOOK at the source.", + "properties": { + "bbox": { + "$ref": "#/properties/room/anyOf/0/properties/bbox" + }, + "sheet": { + "type": "string" + }, + "text": { + "type": "string" + } + }, + "required": [ + "sheet", + "text", + "bbox" + ], + "type": "object" + } + }, + "required": [ + "rev", + "source" + ], + "type": "object" + }, + "sheet": { + "type": "string" + }, + "tag": { + "type": "string" + } + }, + "required": [ + "tag", + "name", + "sheet", + "bbox" + ], + "type": "object" + }, + { + "type": "null" + } +] - changed
Output schema / properties / sources / items / $refPrevious value: -"#/properties/finishes/items/properties/source"New value: +"#/properties/room/anyOf/0/properties/revision/properties/source"
- Changed
sheet_graph6 fields changed- added
Output schema / properties / counts / properties / unmatched_tagsAdded value: +{ + "type": "integer" +} - added
Output schema / properties / revisionsAdded value: +{ + "description": "Every delta-triangle / REV-tag marker the set carries — text markers ('Δ2', 'REV 2') and DRAWN deltas (a bare digit inside a triangle of linework, drawn: true) — where one sits, the ink changed under that revision. Markers on a schedule row or room bubble also attach there (and ride resolve_tag). A revision CLOUD is arc-chain linework these detectors do not read — absence here is not absence of revisions", + "items": { + "additionalProperties": false, + "properties": { + "bbox": { + "$ref": "#/properties/sheets/items/properties/evidence/properties/bbox" + }, + "drawn": { + "type": "boolean" + }, + "rev": { + "type": "string" + }, + "sheet": { + "type": "string" + } + }, + "required": [ + "rev", + "sheet", + "bbox" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / properties / rooms / descriptionPrevious value: -"Room tags read off plan-role sheets — schedule sheets contribute rows, never phantom rooms"New value: +"Numbers CORROBORATED as rooms — a room-finish row answers for them, or (where the set carries no room-finish schedule) a room name is drawn with them. Each says which in `corroboration`. Schedule sheets contribute rows, never phantom rooms" - added
Output schema / properties / rooms / items / properties / corroborationAdded value: +{ + "description": "Why this number is believed to be a room: \"schedule\" (a room-finish row answers for it), \"name\" (a name is drawn with it and the set has no room-finish schedule), or \"name+schedule\"", + "type": "string" +} - added
Output schema / properties / rooms / items / properties / revisionAdded value: +{ + "additionalProperties": false, + "description": "A revision marker (delta triangle / 'REV 2' tag) attached to this item: the ink CHANGED under that revision. The value read is the post-revision answer — view_sheet the marker's bbox and check the addendum before pricing", + "properties": { + "drawn": { + "description": "true = a DRAWN delta: a bare digit inside a triangle of linework (the common CAD convention — the text layer carries only the digit; the geometry proved the triangle). The evidence bbox spans digit and triangle", + "type": "boolean" + }, + "rev": { + "type": "string" + }, + "source": { + "$ref": "#/properties/sheets/items/properties/evidence" + } + }, + "required": [ + "rev", + "source" + ], + "type": "object" +} - added
Output schema / properties / unmatched_tagsAdded value: +{ + "description": "Numbered tags on plan sheets that are NOT counted as rooms — keynote hexagons, detail markers, dimension fragments, legend rows. Listed with a reason, never dropped. A real finish plan is covered in 2–3 digit numbers that are not rooms; counting them as rooms makes every one come back \"no schedule row\", which reads exactly like the lost-bid case and buries it", + "items": { + "additionalProperties": false, + "properties": { + "bbox": { + "$ref": "#/properties/sheets/items/properties/evidence/properties/bbox" + }, + "building": { + "type": "string" + }, + "name": { + "description": "Text drawn with the number, when there is any — on a keynote legend this is the accessory description, not a room name", + "type": "string" + }, + "reason": { + "description": "WHY this number is not counted as a room. Read these: one of them may be a room the schedule left out, which is a hole in the bid", + "type": "string" + }, + "sheet": { + "type": "string" + }, + "tag": { + "type": "string" + } + }, + "required": [ + "tag", + "sheet", + "bbox", + "reason" + ], + "type": "object" + }, + "type": "array" +}
- Changed
sweep_schedule_row4 fields changed- added
Output schema / properties / completeAdded value: +{ + "description": "True when every proposed placement was scored on every swept sheet — false means at least one sheet's count is a FLOOR, not a total (#261)", + "type": "boolean" +} - added
Output schema / properties / sheets / items / properties / completeAdded value: +{ + "description": "True when every proposed placement on this sheet was scored — false means this sheet's count is a FLOOR, not a total (#261)", + "type": "boolean" +} - changed
Output schema / properties / sheets / items / requiredPrevious value: -[ - "sheet", - "found", - "matches", - "withheld", - "excluded", - "text_only", - "candidates", - "elapsed_ms" -]New value: +[ + "sheet", + "found", + "matches", + "withheld", + "excluded", + "text_only", + "candidates", + "complete", + "elapsed_ms" +] - changed
Output schema / requiredPrevious value: -[ - "tag", - "row", - "anchor", - "found", - "sheets", - "skipped" -]New value: +[ + "tag", + "row", + "anchor", + "found", + "sheets", + "complete", + "skipped" +]
- Changed
symbol_sweep5 fields changed- added
Output schema / properties / completeAdded value: +{ + "description": "True when every proposed placement was scored (every swept sheet, in set scope) and the count is a total. FALSE MEANS THE COUNT IS A FLOOR — acknowledge it before trusting found (#261)", + "type": "boolean" +} - changed
Output schema / properties / sheets / items / properties / candidates / descriptionPrevious value: -"The work cap applies PER SHEET; dropped > 0 here names exactly where the count is incomplete"New value: +"The work ceiling applies PER SHEET; dropped > 0 here names exactly where the count is incomplete" - added
Output schema / properties / sheets / items / properties / completeAdded value: +{ + "description": "True when every proposed placement on this sheet was scored — false means this sheet's count is a FLOOR, not a total (#261)", + "type": "boolean" +} - changed
Output schema / properties / sheets / items / requiredPrevious value: -[ - "sheet", - "found", - "matches", - "withheld", - "candidates", - "elapsed_ms" -]New value: +[ + "sheet", + "found", + "matches", + "withheld", + "candidates", + "complete", + "elapsed_ms" +] - changed
Output schema / requiredPrevious value: -[ - "scope", - "found", - "seed" -]New value: +[ + "scope", + "found", + "seed", + "complete" +]
2 tool updates
v0.1.16- Changed
set_scale2 fields changed- added
Output schema / properties / confirmedAdded value: +{ + "description": "Always false here: set_scale is the agent surface, and an agent-set scale stays UNCONFIRMED until a human confirms it in the canvas — quantities still flow, wearing the caveat", + "type": "boolean" +} - changed
Output schema / requiredPrevious value: -[ - "sheet", - "upp", - "source" -]New value: +[ + "sheet", + "upp", + "source", + "confirmed" +]
- Changed
takeoff_summary1 field changed- added
Output schema / properties / scale_unconfirmedAdded value: +{ + "description": "Sheets whose scale is agent-set and no human has confirmed — these totals stand on an unverified scale; verify against a stated dimension or confirm in the canvas", + "items": { + "type": "string" + }, + "type": "array" +}
8 tool updates
v0.1.15- Added
apply_rules - Added
cut_out - Changed
delete_shape1 field changed- added
Output schema / properties / noteAdded value: +{ + "description": "Cutout interplay (#206), when it applies: the parent's cut was reverted, could not be rebuilt, or reconciled deducts were orphaned by a parent delete", + "type": "string" +}
- Changed
export_marked_pdf1 field changed- added
Input schema / properties / overwriteAdded value: +{ + "description": "Replace the file at path even when it is not an OpenTakeoff export. Off by default: re-exporting over a previous export of your own already overwrites without this, so you only need it to deliberately destroy an unrelated file.", + "type": "boolean" +}
- Changed
export_report1 field changed- added
Input schema / properties / overwriteAdded value: +{ + "description": "Replace the file at path even when it is not an OpenTakeoff export. Off by default: re-exporting over a previous export of your own already overwrites without this, so you only need it to deliberately destroy an unrelated file.", + "type": "boolean" +}
- Changed
export_takeoff1 field changed- added
Input schema / properties / overwriteAdded value: +{ + "description": "Replace the file at path even when it is not an OpenTakeoff export. Off by default: re-exporting over a previous export of your own already overwrites without this, so you only need it to deliberately destroy an unrelated file.", + "type": "boolean" +}
- Changed
import_takeoff2 fields changed- added
Output schema / properties / rules_importedAdded value: +{ + "description": "Correction rules (#88) that arrived with the file — apply_rules re-runs them", + "type": "integer" +} - changed
Output schema / requiredPrevious value: -[ - "file", - "replaced", - "shapes_added", - "shapes_pending", - "conditions_merged", - "conditions_added", - "scales_adopted", - "unknown_files", - "shapes_total", - "note" -]New value: +[ + "file", + "replaced", + "shapes_added", + "shapes_pending", + "conditions_merged", + "conditions_added", + "scales_adopted", + "unknown_files", + "rules_imported", + "shapes_total", + "note" +]
- Changed
undo_last1 field changed- changed
Output schema / properties / steps / items / properties / op / enumPrevious value: -[ - "commit", - "edit", - "delete", - "materials", - "condition", - "approval", - "duplicate_condition", - "split_condition" -]New value: +[ + "commit", + "edit", + "delete", + "materials", + "condition", + "approval", + "duplicate_condition", + "split_condition", + "cutout", + "cutout_restore" +]
7 tool updates
v0.1.14- Added
duplicate_condition - Changed
edit_materials6 fields changed- changed
Input schema / properties / add / items / properties / basis / descriptionPrevious value: -"Which of the condition's totals this row divides against — default 'area' (total SF)"New value: +"Which of the condition's totals this row divides against — default 'area' (total SF). 'seam_lf' is the figured roll-layout seam length (weld rod, seam tape), 0 until the condition carries a roll_setup" - changed
Input schema / properties / add / items / properties / basis / enumPrevious value: -[ - "area", - "linear", - "count" -]New value: +[ + "area", + "linear", + "count", + "seam_lf" +] - changed
Output schema / properties / materials / items / properties / basis / descriptionPrevious value: -"Which of the condition's totals this row's quantity is computed against"New value: +"Which of the condition's totals this row's quantity is computed against — 'seam_lf' is the FIGURED roll-layout seam length (weld rod, seam tape), 0 until the condition carries a roll_setup" - changed
Output schema / properties / materials / items / properties / basis / enumPrevious value: -[ - "area", - "linear", - "count" -]New value: +[ + "area", + "linear", + "count", + "seam_lf" +] - added
Output schema / properties / materials / items / properties / inheritedAdded value: +{ + "description": "On a twin: true while the row still follows the family — a patch on it takes it local, split_condition freezes them all", + "type": "boolean" +} - added
Output schema / properties / materials / items / properties / origin_idAdded value: +{ + "description": "On a twin: the parent row this one follows (the variants.ts family link)", + "type": "string" +}
- Changed
edit_shape3 fields changed- added
Input schema / properties / labelAdded value: +{ + "description": "The room (or phase/area) this shape belongs to, e.g. \"134\" or \"OFFICE 101\" — what per-room reporting groups by. Pass \"\" to clear it", + "type": "string" +} - changed
Output schema / properties / changed / items / enumPrevious value: -[ - "verts", - "condition", - "role" -]New value: +[ + "verts", + "condition", + "role", + "label" +] - added
Output schema / properties / labelAdded value: +{ + "description": "The shape's room/phase label after this call — absent when it carries none (a cleared label reports as absent, not as an empty string)", + "type": "string" +}
- Changed
export_report2 fields changed- changed
Output schema / properties / conditions / items / properties / materials / items / properties / basis / enumPrevious value: -[ - "area", - "linear", - "count" -]New value: +[ + "area", + "linear", + "count", + "seam_lf" +] - changed
Output schema / properties / conditions / items / properties / materials / items / properties / basis_qty / descriptionPrevious value: -"The condition total this row divides (SF, LF, or EA — multiplier applied, waste not)"New value: +"The condition total this row divides (SF, LF, EA, or figured seam LF — multiplier applied, waste not)"
- Changed
list_shapes1 field changed- added
Output schema / properties / shapes / items / properties / labelAdded value: +{ + "description": "The room (or phase/area) this shape belongs to — detect_rooms stamps the room number it traced from; edit_shape sets or clears it. Absent when unlabeled", + "type": "string" +}
- Added
split_condition - Changed
undo_last1 field changed- changed
Output schema / properties / steps / items / properties / op / enumPrevious value: -[ - "commit", - "edit", - "delete", - "materials", - "condition", - "approval" -]New value: +[ + "commit", + "edit", + "delete", + "materials", + "condition", + "approval", + "duplicate_condition", + "split_condition" +]
15 tool updates
v0.1.12- Added
delete_verdict - Added
derive_transitions - Changed
detect_rooms7 fields changed- added
Output schema / properties / rooms / items / properties / confidenceAdded value: +{ + "description": "0..1 — the trace scored from the engine's own signals (sealed openings, door wedges, min-passage rule, hatch tier, raster boundary, mask coarseness, implausible size). A review PRIORITIZER, not a verification: 1.0 means every signal came back clean, never that the trace is right. A low score is a view_sheet {overlay:true} audit prompt, not a fact to bid from", + "type": "number" +} - added
Output schema / properties / rooms / items / properties / confidence_factorsAdded value: +{ + "description": "The named factors behind a sub-1.0 confidence (e.g. \"sealed-opening(10% synthetic boundary)\") — each names the edge worth putting eyes on; absent when every signal ran clean", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / rooms / items / properties / door_wedgesAdded value: +{ + "description": "Door-swing wedges annexed into the region under grow-but-verify — how many doorways' swings were included, the canvas's own door handling; rides origin.door_wedges", + "type": "integer" +} - added
Output schema / properties / rooms / items / properties / gap_sealed_pxAdded value: +{ + "description": "Present when the seal ladder closed a genuine OPENING this many mask px wide (doorway-scale — scaled by the sheet's feet, distinct from gap_bridged_px's drafting-pinhole rescue). Part of the boundary is synthetic, and confidence deducts by that share; rides origin.gap_sealed_px on the committed shape", + "type": "number" +} - added
Output schema / properties / rooms / items / properties / min_pass_deltaAdded value: +{ + "description": "Fraction of the verbatim flood the minimum-passage rule removed; 1 means the drawn linework bounds nothing here and the rule is the only reason there is a measurement — audit before trusting", + "type": "number" +} - added
Output schema / properties / rooms / items / properties / min_pass_pxAdded value: +{ + "description": "The feet-true minimum-passage rule (openings under ~0.5 ft never connect two spaces) ran at this dilation radius AND changed the answer — present only with min_pass_delta", + "type": "number" +} - added
Output schema / properties / rooms / items / properties / ring_interiorsAdded value: +{ + "description": "Of those wedges, how many were a CLOSED ring's interior (round column, callout bubble) rather than a door swing — annexed floor you may want as a deduct instead", + "type": "integer" +}
- Changed
export_marked_pdf3 fields changed- added
Output schema / properties / approvals_drawnAdded value: +{ + "description": "Approval-family glyphs burned in (#176) — estimator APPROVED rings + agent AGENT diamonds; the cover tallies the split when any exist", + "type": "integer" +} - changed
Output schema / properties / sheets_marked / descriptionPrevious value: -"Sheets carrying shapes or annotations — unmarked sheets are omitted"New value: +"Sheets carrying shapes, annotations, or approval marks — unmarked sheets are omitted" - changed
Output schema / requiredPrevious value: -[ - "path", - "pages", - "sheets_marked", - "shapes_drawn", - "annotations_drawn", - "note" -]New value: +[ + "path", + "pages", + "sheets_marked", + "shapes_drawn", + "annotations_drawn", + "approvals_drawn", + "note" +]
- Changed
export_takeoff1 field changed- added
Output schema / properties / approvalsAdded value: +{ + "description": "Approval-family records (#176) — the estimator's APPROVED seals and the agent's verdict marks {id, actor, ts, sheet_id, at:[nx,ny], shape_id?, text?}. Present only when any exist (the canvas payload's own convention), so a verdict-free export stays byte-identical", + "items": {}, + "type": "array" +}
- Changed
find_schedule5 fields changed- added
Output schema / properties / matches / items / properties / buildingAdded value: +{ + "description": "The building this table answers for, when its title or sheet names one", + "type": "string" +} - added
Output schema / properties / matches / items / properties / partsAdded value: +{ + "description": "Present when the table CONTINUES across sheets ('… SCHEDULE — CONT'D'): every fragment, base first, each with its own viewable region", + "items": { + "additionalProperties": false, + "properties": { + "region": { + "additionalProperties": false, + "properties": { + "x0": { + "$ref": "#/properties/matches/items/properties/region/properties/x0" + }, + "x1": { + "$ref": "#/properties/matches/items/properties/region/properties/x1" + }, + "y0": { + "$ref": "#/properties/matches/items/properties/region/properties/y0" + }, + "y1": { + "$ref": "#/properties/matches/items/properties/region/properties/y1" + } + }, + "required": [ + "x0", + "y0", + "x1", + "y1" + ], + "type": "object" + }, + "rows": { + "type": "integer" + }, + "sheet": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "sheet", + "title", + "rows", + "region" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / properties / matches / items / properties / region / descriptionPrevious value: -"Pass to view_sheet to look at the table"New value: +"Pass to view_sheet to look at the table (the BASE fragment's region when the table continues)" - added
Output schema / properties / matches / items / properties / rotated_headersAdded value: +{ + "description": "true when the column headers were read at a quarter-turn", + "type": "boolean" +} - added
Output schema / properties / matches / items / properties / rows / descriptionAdded value: +"Total data rows — a continued schedule counts every fragment's rows"
- Changed
list_annotations3 fields changed- added
Output schema / properties / verdict_countAdded value: +{ + "type": "integer" +} - added
Output schema / properties / verdictsAdded value: +{ + "description": "Approval-family records (#176) under the same filters: sheet applies directly; a condition filter reaches a verdict THROUGH its target shape (a sheet-point mark carries no scope and drops out)", + "items": { + "additionalProperties": false, + "properties": { + "actor": { + "description": "\"estimator\" = the human APPROVED ring (ink — import-borne here, never minted over MCP), \"agent\" = the AGENT diamond", + "enum": [ + "estimator", + "agent" + ], + "type": "string" + }, + "at": { + "description": "Render anchor (image px) — absent only when the record rides a sheet from a file this session hasn't loaded (#152)", + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "condition": { + "description": "The targeted shape's finish tag, resolved — '' for sheet-point marks", + "type": "string" + }, + "id": { + "type": "string" + }, + "shape_id": { + "description": "Present when the verdict targets a committed shape — WHAT was marked, not where it draws", + "type": "string" + }, + "sheet": { + "type": "string" + }, + "text": { + "description": "The optional short note riding the record", + "type": "string" + }, + "ts": { + "description": "ISO-8601 mint time", + "type": "string" + } + }, + "required": [ + "id", + "actor", + "sheet", + "condition" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / requiredPrevious value: -[ - "annotations", - "count", - "unattached" -]New value: +[ + "annotations", + "count", + "unattached", + "verdicts", + "verdict_count" +]
- Added
mark_verdict - Changed
one_click7 fields changed- added
Output schema / properties / confidenceAdded value: +{ + "description": "0..1 — the trace scored from the engine's own signals (sealed openings, door wedges, min-passage rule, hatch tier, raster boundary, mask coarseness, implausible size). A review PRIORITIZER, not a verification: 1.0 means every signal came back clean, never that the trace is right. A low score is a view_sheet {overlay:true} audit prompt, not a fact to bid from", + "type": "number" +} - added
Output schema / properties / confidence_factorsAdded value: +{ + "description": "The named factors behind a sub-1.0 confidence (e.g. \"sealed-opening(10% synthetic boundary)\") — each names the edge worth putting eyes on; absent when every signal ran clean", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / door_wedgesAdded value: +{ + "description": "Door-swing wedges annexed into the region under grow-but-verify — how many doorways' swings were included, the canvas's own door handling; rides origin.door_wedges", + "type": "integer" +} - added
Output schema / properties / gap_sealed_pxAdded value: +{ + "description": "Present when the seal ladder closed a genuine OPENING this many mask px wide (doorway-scale — scaled by the sheet's feet, distinct from gap_bridged_px's drafting-pinhole rescue). Part of the boundary is synthetic, and confidence deducts by that share; rides origin.gap_sealed_px on the committed shape", + "type": "number" +} - added
Output schema / properties / min_pass_deltaAdded value: +{ + "description": "Fraction of the verbatim flood the minimum-passage rule removed; 1 means the drawn linework bounds nothing here and the rule is the only reason there is a measurement — audit before trusting", + "type": "number" +} - added
Output schema / properties / min_pass_pxAdded value: +{ + "description": "The feet-true minimum-passage rule (openings under ~0.5 ft never connect two spaces) ran at this dilation radius AND changed the answer — present only with min_pass_delta", + "type": "number" +} - added
Output schema / properties / ring_interiorsAdded value: +{ + "description": "Of those wedges, how many were a CLOSED ring's interior (round column, callout bubble) rather than a door swing — annexed floor you may want as a deduct instead", + "type": "integer" +}
- Changed
resolve_tag6 fields changed- changed
Input schema / properties / tag / descriptionPrevious value: -"The room tag as drawn, e.g. \"134\" or \"139A\""New value: +"The room tag as drawn, e.g. \"134\" or \"139A\" — or building-qualified on a multi-building set, e.g. \"A-134\" (building A, room 134)" - added
Output schema / properties / buildingAdded value: +{ + "description": "resolved only — the building whose schedule row answered, when the set names buildings", + "type": "string" +} - added
Output schema / properties / candidatesAdded value: +{ + "description": "unresolved only — every schedule row that COULD have answered (an ambiguous multi-building tag lists one per building; qualify the tag, e.g. \"A-134\", to pick)", + "items": { + "additionalProperties": false, + "properties": { + "building": { + "type": "string" + }, + "key": { + "type": "string" + }, + "sheet": { + "type": "string" + }, + "table": { + "type": "string" + } + }, + "required": [ + "key", + "sheet", + "table" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / properties / room / anyOfPrevious value: -[ - { - "additionalProperties": false, - "properties": { - "bbox": { - "additionalProperties": false, - "properties": { - "x0": { - "type": "number" - }, - "x1": { - "type": "number" - }, - "y0": { - "type": "number" - }, - "y1": { - "type": "number" - } - }, - "required": [ - "x0", - "y0", - "x1", - "y1" - ], - "type": "object" - }, - "name": { - "description": "The name span stacked over the tag ('' when none)", - "type": "string" - }, - "sheet": { - "type": "string" - }, - "tag": { - "type": "string" - } - }, - "required": [ - "tag", - "name", - "sheet", - "bbox" - ], - "type": "object" - }, - { - "type": "null" - } -]New value: +[ + { + "additionalProperties": false, + "properties": { + "bbox": { + "additionalProperties": false, + "properties": { + "x0": { + "type": "number" + }, + "x1": { + "type": "number" + }, + "y0": { + "type": "number" + }, + "y1": { + "type": "number" + } + }, + "required": [ + "x0", + "y0", + "x1", + "y1" + ], + "type": "object" + }, + "building": { + "description": "The building the room belongs to, when the set names one — its plan sheet's BUILDING/BLDG context, or the tag's own qualifier ('A-134')", + "type": "string" + }, + "name": { + "description": "The name span stacked over the tag ('' when none)", + "type": "string" + }, + "sheet": { + "type": "string" + }, + "tag": { + "type": "string" + } + }, + "required": [ + "tag", + "name", + "sheet", + "bbox" + ], + "type": "object" + }, + { + "type": "null" + } +] - changed
Output schema / properties / room / descriptionPrevious value: -"The plan tag, when the room appears on a plan sheet — cited even when resolution fails"New value: +"The plan tag, when the room appears on a plan sheet — cited even when resolution fails. null on a multi-building ambiguity: citing one building's tag would be quietly wrong" - changed
Output schema / properties / sources / descriptionPrevious value: -"The chain: plan tag → schedule row"New value: +"The chain: plan tag → schedule row (the row cites the sheet that CARRIES it — under a continuation that is the CONT'D sheet)"
- Changed
sheet_context1 field changed- added
Output schema / properties / text / properties / spans / items / properties / rotAdded value: +{ + "description": "Run direction in degrees, clockwise, y down — present only when rotated (90/270 = a quarter-turn, e.g. rotated schedule headers)", + "type": "number" +}
- Changed
sheet_graph7 fields changed- added
Output schema / properties / buildingsAdded value: +{ + "description": "Every building designator the set names (sorted) — present only on multi-building-aware sets. Room numbers reused across these need qualified tags ('A-134')", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / counts / properties / schedules / descriptionAdded value: +"LOGICAL tables — a schedule continued across sheets counts once" - added
Output schema / properties / notesAdded value: +{ + "description": "Named gaps found while indexing (e.g. a continuation whose rows could not be aligned) — the graph refuses silently dropping anything", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / rooms / items / properties / buildingAdded value: +{ + "description": "The building the room belongs to, when the set names one — its plan sheet's BUILDING/BLDG context, or the tag's own qualifier ('A-134')", + "type": "string" +} - added
Output schema / properties / sheets / items / properties / buildingAdded value: +{ + "description": "The sheet's building context, when it names exactly one (BUILDING A / BLDG 2)", + "type": "string" +} - added
Output schema / properties / sheets / items / properties / schedules / items / properties / continuesAdded value: +{ + "description": "Present on a continuation fragment ('… SCHEDULE — CONT'D'): the sheet carrying the table's base fragment. The fragments read as ONE table — resolve_tag and find_schedule already see the union", + "type": "string" +} - added
Output schema / properties / sheets / items / properties / schedules / items / properties / rotated_headersAdded value: +{ + "description": "true when the column headers were read at a quarter-turn", + "type": "boolean" +}
- Added
sweep_schedule_row - Changed
symbol_sweep13 fields changed- added
Input schema / properties / scopeAdded value: +{ + "default": "sheet", + "description": "\"sheet\" = this sheet only; \"set\" = every PLAN-role sheet in the working set (needs a text layer for the sheet graph; non-plan sheets are excluded and disclosed)", + "enum": [ + "sheet", + "set" + ], + "type": "string" +} - added
Input schema / properties / sheet / descriptionAdded value: +"The sheet the seed rect sits on — in scope 'set' it may be ANY sheet (a detail/legend seed sheet is fingerprint source only, never counted)" - added
Output schema / properties / candidates / descriptionAdded value: +"Sheet scope only — set scope accounts per sheet in sheets[]" - changed
Output schema / properties / found / descriptionPrevious value: -"Placements that cleared the commit bar — matches.length"New value: +"Placements that cleared the commit bar — across every swept sheet in set scope" - changed
Output schema / properties / matches / descriptionPrevious value: -"Deterministic reading order (y, then x). The seed's own location is never listed here"New value: +"Sheet scope only. Deterministic reading order (y, then x). The seed's own location is never listed here" - added
Output schema / properties / scopeAdded value: +{ + "description": "\"sheet\" = the swept sheet alone (matches/withheld/candidates at top level); \"set\" = every PLAN-role sheet in the working set (per-sheet results in sheets[], exclusions in skipped[])", + "enum": [ + "sheet", + "set" + ], + "type": "string" +} - added
Output schema / properties / seed / properties / roleAdded value: +{ + "description": "Set scope: the seed sheet's graph role — a non-plan seed sheet is the fingerprint SOURCE and is excluded from counting", + "type": "string" +} - added
Output schema / properties / seed / properties / sheetAdded value: +{ + "description": "The sheet the seed rect was marqueed on", + "type": "string" +} - changed
Output schema / properties / seed / requiredPrevious value: -[ - "segments", - "center", - "rect", - "length_px" -]New value: +[ + "sheet", + "segments", + "center", + "rect", + "length_px" +] - added
Output schema / properties / sheetsAdded value: +{ + "description": "Set scope only: one entry per swept PLAN-role sheet, load order", + "items": { + "additionalProperties": false, + "properties": { + "candidates": { + "additionalProperties": false, + "description": "The work cap applies PER SHEET; dropped > 0 here names exactly where the count is incomplete", + "properties": { + "considered": { + "$ref": "#/properties/candidates/properties/considered" + }, + "dropped": { + "$ref": "#/properties/candidates/properties/dropped" + } + }, + "required": [ + "considered", + "dropped" + ], + "type": "object" + }, + "elapsed_ms": { + "description": "Wall-clock for this sheet's sweep", + "type": "number" + }, + "found": { + "type": "integer" + }, + "matches": { + "items": { + "additionalProperties": false, + "properties": { + "at": { + "$ref": "#/properties/matches/items/properties/at" + }, + "mirrored": { + "$ref": "#/properties/matches/items/properties/mirrored" + }, + "rotation": { + "$ref": "#/properties/matches/items/properties/rotation" + }, + "score": { + "$ref": "#/properties/matches/items/properties/score" + } + }, + "required": [ + "at", + "score", + "rotation", + "mirrored" + ], + "type": "object" + }, + "type": "array" + }, + "scale_assumed": { + "description": "#186: present when the true ratio is UNKNOWN (a scale is missing on the seed sheet or this one) and the sweep ran at 1:1 — an unstated ratio plus a zero count is not evidence of absence", + "type": "string" + }, + "scaled": { + "additionalProperties": false, + "description": "#186: present only when the seed was resized for this sheet", + "properties": { + "footprint_px": { + "description": "The symbol's size on THIS sheet after the resize", + "type": "number" + }, + "ratio": { + "description": "Seed-sheet px per target-sheet px, computed from the two sheets' own committed scales (upp_seed / upp_target) — stated, never scale-searched", + "type": "number" + }, + "segments": { + "description": "Fingerprint segments that survived the resize and were actually searched for", + "type": "integer" + }, + "sub_pixel_dropped": { + "description": "Seed segments that fell below matchable length when scaled down — excluded from the score rather than depressing it, so a score here is a fraction of what survived, not of the whole seed", + "type": "integer" + }, + "tol_px": { + "description": "The endpoint tolerance actually applied — it rides the ratio up when the seed is magnified (its drawn jitter magnifies too) and never down", + "type": "number" + } + }, + "required": [ + "ratio", + "segments", + "sub_pixel_dropped", + "footprint_px", + "tol_px" + ], + "type": "object" + }, + "sheet": { + "type": "string" + }, + "withheld": { + "items": { + "additionalProperties": false, + "properties": { + "at": { + "$ref": "#/properties/matches/items/properties/at" + }, + "mirrored": { + "$ref": "#/properties/matches/items/properties/mirrored" + }, + "reason": { + "type": "string" + }, + "rotation": { + "$ref": "#/properties/matches/items/properties/rotation" + }, + "score": { + "$ref": "#/properties/matches/items/properties/score" + } + }, + "required": [ + "at", + "score", + "rotation", + "mirrored", + "reason" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "sheet", + "found", + "matches", + "withheld", + "candidates", + "elapsed_ms" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / skippedAdded value: +{ + "description": "Set scope only: every sheet excluded from counting, with role and reason — including the seed's own sheet when it is not a plan", + "items": { + "additionalProperties": false, + "properties": { + "reason": { + "type": "string" + }, + "role": { + "description": "The sheet's graph role (plan / schedule / legend / detail / …)", + "type": "string" + }, + "sheet": { + "type": "string" + } + }, + "required": [ + "sheet", + "role", + "reason" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / properties / withheld / descriptionPrevious value: -"Near-matches in the [0.75, 0.92) band — reported with a reason, NEVER committed. A withheld placement is a question you can answer with view_sheet; a hidden one is a miscount"New value: +"Sheet scope only. Near-matches in the [0.75, 0.92) band — reported with a reason, NEVER committed. A withheld placement is a question you can answer with view_sheet; a hidden one is a miscount" - changed
Output schema / requiredPrevious value: -[ - "found", - "matches", - "withheld", - "seed", - "candidates" -]New value: +[ + "scope", + "found", + "seed" +]
- Changed
undo_last2 fields changed- changed
Output schema / properties / steps / items / properties / op / enumPrevious value: -[ - "commit", - "edit", - "delete", - "materials", - "condition" -]New value: +[ + "commit", + "edit", + "delete", + "materials", + "condition", + "approval" +] - changed
Output schema / properties / steps / items / properties / shapes / descriptionPrevious value: -"Shapes affected by reversing this step — 0 for a materials step (it restores a condition's supporting-materials rows, not shapes) and for a condition step (it restores the waste/multiplier pair)"New value: +"Shapes affected by reversing this step — 0 for a materials step (it restores a condition's supporting-materials rows, not shapes), for a condition step (it restores the waste/multiplier pair), and for an approval step (it re-seats or removes a verdict mark)"
5 tool updates
v0.1.11- Changed
annotate6 fields changed- changed
Input schema / properties / from / descriptionPrevious value: -"Arrow tail (image px)"New value: +"Arrow tail / dimension start (image px)" - changed
Input schema / properties / text / descriptionPrevious value: -"The note. A cloud with no text still reads as 'look here'; a bubble's text draws centered in the circle"New value: +"The note. A cloud with no text still reads as 'look here'; a bubble's text draws centered in the circle; a dimension appends it after the measured length" - changed
Input schema / properties / to / descriptionPrevious value: -"Arrow head — what it points at (image px)"New value: +"Arrow head / dimension end (image px)" - changed
Input schema / properties / type / descriptionPrevious value: -"cloud/highlight need rect; text/callout/bubble need at; callout also needs target; arrow needs from + to"New value: +"cloud/highlight need rect; text/callout/bubble need at; callout also needs target; arrow and dimension need from + to" - changed
Input schema / properties / type / enumPrevious value: -[ - "cloud", - "text", - "callout", - "highlight", - "arrow", - "bubble" -]New value: +[ + "cloud", + "text", + "callout", + "highlight", + "arrow", + "bubble", + "dimension" +] - added
Output schema / properties / length_lfAdded value: +{ + "description": "Dimension only: the measured length (real feet) the annotation will label itself with", + "type": "number" +}
- Changed
detect_rooms1 field changed- added
Output schema / properties / rooms / items / properties / raster_tracedAdded value: +{ + "const": true, + "description": "Present when the room was bounded by rendered pixels (scanned-sheet raster fallback, #154) rather than vector linework — sheet-wide per sweep, and it rides origin.raster_traced on the committed shape", + "type": "boolean" +}
- Changed
list_annotations3 fields changed- changed
Output schema / properties / annotations / items / properties / from / descriptionPrevious value: -"Arrow tail (image px)"New value: +"Arrow tail / dimension start (image px)" - added
Output schema / properties / annotations / items / properties / length_lfAdded value: +{ + "description": "Dimension only: the measured length in real feet, snapshotted at annotate time from the sheet scale", + "type": "number" +} - changed
Output schema / properties / annotations / items / properties / to / descriptionPrevious value: -"Arrow head (image px)"New value: +"Arrow head / dimension end (image px)"
- Changed
one_click1 field changed- added
Output schema / properties / raster_tracedAdded value: +{ + "const": true, + "description": "Present when the region was bounded by the sheet's RENDERED PIXELS (the scanned-sheet raster fallback, #154) rather than vector linework — absent means the vector path ran. Rides origin.raster_traced on the committed shape; a raster ring's corners are unsnapped (a scan has no true endpoints), so audit it with view_sheet overlay before trusting the total", + "type": "boolean" +}
- Added
symbol_sweep
3 tool updates
v0.1.10- Changed
detect_rooms6 fields changed- added
Input schema / properties / assign_from_scheduleAdded value: +{ + "default": false, + "description": "Commit each room under the FLOOR finish its OWN room-finish schedule row states (resolve_tag's chain, per room): the citation rides origin.assignment, and rooms the schedule cannot answer for — no row, no FLOOR cell, a compound cell like \"CPT-1/VCT-1\" — are returned in unresolved[] with reasons and seeds instead of committed under a guess. Needs the sheet's scale and a room-finish schedule in the working set (merge the schedule sheet in with load_plan first). Mutually exclusive with condition", + "type": "boolean" +} - changed
Input schema / properties / condition / descriptionPrevious value: -"Finish tag to commit every detected room under (minted on first use)"New value: +"Finish tag to commit every detected room under (minted on first use). Mutually exclusive with assign_from_schedule" - added
Output schema / properties / rooms / items / properties / conditionAdded value: +{ + "description": "The finish tag this room committed under — the passed condition, or in assign mode the FLOOR finish its own schedule row states. Present exactly when shape_id is", + "type": "string" +} - added
Output schema / properties / unresolvedAdded value: +{ + "description": "Assign mode only, empty array included: [] is the positive claim that every detected room resolved against its own schedule row", + "items": { + "additionalProperties": false, + "properties": { + "area_sf": { + "description": "The room's real traced area — withheld from committing, not from reporting", + "type": "number" + }, + "label": { + "description": "The room tag as drawn", + "type": "string" + }, + "perimeter_lf": { + "type": "number" + }, + "reason": { + "description": "WHY the schedule could not answer — resolveTag's own reason, \"states no FLOOR finish\", or \"ambiguous: …\" for a compound cell", + "type": "string" + }, + "seed": { + "description": "The flood seed (image px) — once the estimator answers, one_click here with the stated condition commits it", + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + } + }, + "required": [ + "label", + "reason", + "area_sf", + "perimeter_lf", + "seed" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / withheld / properties / unresolvedAdded value: +{ + "description": "Assign mode: rooms the schedule could not answer for (no row, no FLOOR cell, or a compound cell) — withheld into unresolved[], never committed under a guess. Always present; 0 outside assign mode", + "type": "integer" +} - changed
Output schema / properties / withheld / requiredPrevious value: -[ - "total", - "degenerate", - "duplicate", - "bubble", - "implausible" -]New value: +[ + "total", + "degenerate", + "duplicate", + "bubble", + "implausible", + "unresolved" +]
- Changed
export_takeoff1 field changed- changed
Output schema / properties / shapes / items / properties / origin / descriptionPrevious value: -"Provenance: method (manual|one_click_v1), actor (omitted=human, 'agent'=MCP/automation), reviewed (human affirmed at an explicit gate), and correction fields (edited, edited_before_create, copied, proposed_verts_norm, edits)"New value: +"Provenance: method (manual|one_click_v1), actor (omitted=human, 'agent'=MCP/automation), reviewed (human affirmed at an explicit gate), assignment (where the finish tag came from — {source: 'schedule', room_tag, surface, schedule_sheet} when the room's own schedule row decided it, {source: 'asserted'} when the agent chose; stamped on every agent commit), and correction fields (edited, edited_before_create, copied, proposed_verts_norm, edits)"
- Changed
list_shapes1 field changed- added
Output schema / properties / shapes / items / properties / assignmentAdded value: +{ + "description": "Where the finish tag came from: \"schedule\" = resolved from the room's own schedule row, \"asserted\" = the agent chose it. origin.assignment in export_takeoff carries the citation. Absent on human canvas shapes", + "enum": [ + "schedule", + "asserted" + ], + "type": "string" +}
21 tool updates
v0.1.9- Changed
annotate7 fields changed- changed
Input schema / properties / at / descriptionPrevious value: -"Anchor point (image px) — text and callout"New value: +"Anchor point (image px) — text, callout, and bubble (the circle's center)" - added
Input schema / properties / fromAdded value: +{ + "$ref": "#/properties/at", + "description": "Arrow tail (image px)" +} - added
Input schema / properties / rAdded value: +{ + "description": "Bubble radius (image px); omitted → the canvas default (2% of sheet width)", + "exclusiveMinimum": 0, + "type": "number" +} - changed
Input schema / properties / text / descriptionPrevious value: -"The note. A cloud with no text still reads as 'look here'"New value: +"The note. A cloud with no text still reads as 'look here'; a bubble's text draws centered in the circle" - added
Input schema / properties / toAdded value: +{ + "$ref": "#/properties/at", + "description": "Arrow head — what it points at (image px)" +} - changed
Input schema / properties / type / descriptionPrevious value: -"cloud/highlight need rect; text/callout need at; callout also needs target"New value: +"cloud/highlight need rect; text/callout/bubble need at; callout also needs target; arrow needs from + to" - changed
Input schema / properties / type / enumPrevious value: -[ - "cloud", - "text", - "callout", - "highlight" -]New value: +[ + "cloud", + "text", + "callout", + "highlight", + "arrow", + "bubble" +]
- Added
derive_base - Changed
detect_rooms2 fields changed- added
Input schema / properties / layersAdded value: +{ + "additionalProperties": false, + "description": "Override the sheet's classified layer roles for THIS call (see sheet_info.layers)", + "properties": { + "exclude": { + "description": "Layer names or ids whose ink must not block the flood at all", + "items": { + "type": "string" + }, + "type": "array" + }, + "include": { + "description": "Layer names or ids whose ink must plot as HARD boundary", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" +} - added
Output schema / properties / multiple_scalesAdded value: +{ + "const": true, + "description": "Several DISTINCT scale notes on this sheet (#153) — rooms inside an enlarged viewport may be figured at the wrong scale", + "type": "boolean" +}
- Changed
edit_condition5 fields changed- added
Input schema / properties / height_ftAdded value: +{ + "description": "Wall height in feet — the canvas's H knob; measure_surface quantifies traced LF × this", + "exclusiveMinimum": 0, + "type": "number" +} - added
Input schema / properties / roll_setupAdded value: +{ + "anyOf": [ + { + "description": "Opt the condition OUT of roll goods", + "type": "null" + }, + { + "additionalProperties": false, + "properties": { + "direction": { + "description": "Run direction; auto lets the engine pick per room", + "enum": [ + "auto", + "ns", + "ew" + ], + "type": "string" + }, + "doorway_overage_in": { + "minimum": 0, + "type": "number" + }, + "material": { + "description": "Material class — fresh opt-ins and material changes start from this class's engine defaults (carpet sells sy, others sf)", + "enum": [ + "carpet", + "sheet_vinyl", + "rubber" + ], + "type": "string" + }, + "price_unit": { + "description": "Sell unit the order quantity is figured in", + "enum": [ + "sy", + "sf", + "lf" + ], + "type": "string" + }, + "roll_length_ft": { + "description": "Physical roll length; 0 = unlimited", + "minimum": 0, + "type": "number" + }, + "roll_width_ft": { + "exclusiveMinimum": 0, + "type": "number" + }, + "seam_allowance_in": { + "minimum": 0, + "type": "number" + }, + "wall_overage_in": { + "minimum": 0, + "type": "number" + } + }, + "type": "object" + } + ], + "description": "Roll-goods opt-in (#147): presence of a setup is what makes the condition roll goods — seams figured, cuts packed, order footage beside the measured quantities. Same-material partial edits patch the existing setup; null opts out. The reply echoes the figured order (cuts, order_lf, rolls, order_qty) whenever floor shapes exist on scaled sheets, and export_report's roll_goods block carries the same rows" +} - added
Output schema / properties / height_ftAdded value: +{ + "description": "The condition's wall height after this write — present once set (measure_surface multiplies traced LF by it)", + "type": "number" +} - added
Output schema / properties / rollAdded value: +{ + "additionalProperties": true, + "description": "The figured order (same row export_report's roll_goods carries) — present when the roll-goods condition has floor shapes on scaled sheets", + "properties": { + "condition_id": { + "type": "string" + }, + "cuts": { + "type": "integer" + }, + "direction": { + "type": "string" + }, + "finish_tag": { + "type": "string" + }, + "material": { + "type": "string" + }, + "order_lf": { + "description": "Full-width roll footage to order, ×N applied, rounded up to the inch", + "type": "number" + }, + "order_qty": { + "type": "number" + }, + "order_unit": { + "type": "string" + }, + "oversize": { + "description": "true when a cut exceeds the physical roll length (roll_length_ft binds)", + "type": "boolean" + }, + "roll_length_ft": { + "type": "number" + }, + "roll_width_ft": { + "type": "number" + }, + "rolls": { + "type": "number" + } + }, + "required": [ + "condition_id", + "finish_tag", + "material", + "roll_width_ft", + "roll_length_ft", + "direction", + "cuts", + "order_lf", + "rolls", + "order_qty", + "order_unit", + "oversize" + ], + "type": "object" +} - added
Output schema / properties / roll_setupAdded value: +{ + "additionalProperties": true, + "description": "The condition's roll-goods setup after this write — present while opted in", + "properties": {}, + "type": "object" +}
- Changed
edit_shape8 fields changed- changed
Input schema / properties / role / descriptionPrevious value: -"Switch what the shape measures"New value: +"Switch what the shape measures — flipping INTO surface_area needs a height on the shape or its condition" - changed
Input schema / properties / role / enumPrevious value: -[ - "floor_area", - "deduct", - "linear" -]New value: +[ + "floor_area", + "deduct", + "linear", + "surface_area", + "count" +] - changed
Input schema / properties / verts / descriptionPrevious value: -"Replacement geometry (image px): ≥3 vertices for an area shape, ≥2 points for a linear one"New value: +"Replacement geometry (image px): ≥3 vertices for an area shape, ≥2 points for a linear/surface run, ≥1 for a count marker" - changed
Output schema / properties / area_sf / descriptionPrevious value: -"0 for linear shapes"New value: +"0 for linear shapes; LF × height for surface_area; absent for count" - added
Output schema / properties / countAdded value: +{ + "description": "count shapes only — the marker's EA (preserved across the edit)", + "type": "number" +} - changed
Output schema / properties / measure_role / enumPrevious value: -[ - "floor_area", - "deduct", - "linear" -]New value: +[ + "floor_area", + "deduct", + "linear", + "surface_area", + "count" +] - changed
Output schema / properties / perimeter_lf / descriptionPrevious value: -"Length for linear shapes, perimeter for closed ones"New value: +"Length for linear/surface runs, perimeter for closed ones; absent for count" - changed
Output schema / requiredPrevious value: -[ - "shape_id", - "changed", - "measure_role", - "nverts", - "area_sf", - "perimeter_lf", - "agent_edits" -]New value: +[ + "shape_id", + "changed", + "measure_role", + "nverts", + "agent_edits" +]
- Added
export_marked_pdf - Changed
export_report3 fields changed- added
Input schema / properties / project_nameAdded value: +{ + "description": "Label for the document's project_name field (a headless session has no project of its own; omitted → null)", + "type": "string" +} - added
Output schema / properties / roll_goodsAdded value: +{ + "description": "Roll-goods order rows (#136) — order_lf / rolls / order_qty per roll-goods condition, ×N applied; empty when no condition carries a roll_setup (always the case for a headless session today)", + "items": { + "additionalProperties": {}, + "type": "object" + }, + "type": "array" +} - changed
Output schema / requiredPrevious value: -[ - "schema", - "project_name", - "generated_with", - "sheets", - "conditions", - "by_sheet", - "totals", - "materials", - "markups", - "rfis", - "condition_columns", - "shape_labels", - "by_label", - "units", - "display_units" -]New value: +[ + "schema", + "project_name", + "generated_with", + "sheets", + "conditions", + "by_sheet", + "totals", + "materials", + "markups", + "rfis", + "condition_columns", + "shape_labels", + "by_label", + "units", + "display_units", + "roll_goods" +]
- Changed
export_takeoff4 fields changed- added
Output schema / properties / shapes / items / properties / computed / descriptionAdded value: +"count shapes carry {count} alone; every other role carries area_sf + perimeter_lf" - added
Output schema / properties / shapes / items / properties / computed / properties / countAdded value: +{ + "type": "number" +} - removed
Output schema / properties / shapes / items / properties / computed / requiredRemoved value: -[ - "area_sf", - "perimeter_lf" -] - changed
Output schema / properties / shapes / items / properties / measure_role / enumPrevious value: -[ - "floor_area", - "deduct", - "linear" -]New value: +[ + "floor_area", + "deduct", + "linear", + "surface_area", + "count" +]
- Added
find_schedule - Added
import_takeoff - Changed
list_annotations3 fields changed- added
Output schema / properties / annotations / items / properties / fromAdded value: +{ + "description": "Arrow tail (image px)", + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" +} - added
Output schema / properties / annotations / items / properties / rAdded value: +{ + "description": "Bubble radius (image px)", + "type": "number" +} - added
Output schema / properties / annotations / items / properties / toAdded value: +{ + "description": "Arrow head (image px)", + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" +}
- Added
list_shapes - Changed
load_plan6 fields changed- added
Input schema / properties / mergeAdded value: +{ + "description": "true = ADD this document to the working set, keeping all existing work (merge into an empty session is just a load)", + "type": "boolean" +} - added
Output schema / properties / file / descriptionAdded value: +"The document just loaded (basename)" - added
Output schema / properties / filesAdded value: +{ + "description": "Every document in the working set, load order (#152 — one entry unless merge was used)", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / page_count / descriptionAdded value: +"Total sheets across the working set" - added
Output schema / properties / sheets / descriptionAdded value: +"EVERY sheet in the working set, not just the file loaded by this call" - changed
Output schema / requiredPrevious value: -[ - "file", - "page_count", - "sheets", - "note" -]New value: +[ + "file", + "files", + "page_count", + "sheets", + "note" +]
- Changed
measure_polygon1 field changed- added
Output schema / properties / warningAdded value: +{ + "description": "Mixed-scale warning (#153): a scale note disagreeing with the sheet's sits in the measured region — verify before trusting these numbers", + "type": "string" +}
- Added
measure_surface - Changed
one_click2 fields changed- added
Input schema / properties / layersAdded value: +{ + "additionalProperties": false, + "description": "Override the sheet's classified layer roles for THIS call (see sheet_info.layers)", + "properties": { + "exclude": { + "description": "Layer names or ids whose ink must not block the flood at all", + "items": { + "type": "string" + }, + "type": "array" + }, + "include": { + "description": "Layer names or ids whose ink must plot as HARD boundary", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" +} - changed
Output schema / properties / warning / descriptionPrevious value: -"Preview mode (no scale): why quantities are unavailable and what to do"New value: +"Preview mode (no scale): why quantities are unavailable — OR, in scaled mode, a mixed-scale warning (#153): a scale note disagreeing with the sheet's sits in the measured region (enlarged plan/detail viewport likely)"
- Added
place_count - Added
resolve_tag - Changed
set_scale1 field changed- added
Output schema / properties / warningAdded value: +{ + "description": "Present when the sheet carries MULTIPLE distinct scale notes (#153) — enlarged plans/details likely; region measurements under a disagreeing note will warn", + "type": "string" +}
- Added
sheet_graph - Changed
sheet_info3 fields changed- added
Output schema / properties / layersAdded value: +{ + "description": "The sheet's PDF layer table (#85) — [] when no Optional Content survived export (every engine path then runs the heuristics unchanged)", + "items": { + "additionalProperties": false, + "properties": { + "confidence": { + "description": "0..1 — how sure the name classifier is", + "type": "number" + }, + "id": { + "description": "Optional Content Group id — pass to one_click/detect_rooms layers.include/exclude", + "type": "string" + }, + "name": { + "description": "The CAD layer name as exported (e.g. A-WALL-FULL)", + "type": "string" + }, + "role": { + "description": "What this layer's linework IS to a takeoff (lib/layers.ts) — boundary/structure plot hard, pattern/annotation/demolition are excluded, unknown falls back to the hatch heuristics", + "enum": [ + "boundary", + "finish-pattern", + "annotation", + "structure", + "demolition", + "unknown" + ], + "type": "string" + }, + "seg_count": { + "description": "Segments this layer owns on this sheet", + "type": "integer" + }, + "visible": { + "description": "Default-config visibility — a hidden layer's ink is excluded outright (or you trace demolition)", + "type": "boolean" + } + }, + "required": [ + "id", + "name", + "role", + "confidence", + "visible", + "seg_count" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / multiple_scalesAdded value: +{ + "const": true, + "description": "Several DISTINCT scale notes on this sheet (#153) — enlarged plans/details likely", + "type": "boolean" +} - changed
Output schema / requiredPrevious value: -[ - "sheet", - "page", - "width_pt", - "height_pt", - "width_px", - "height_px", - "seg_count", - "has_vector_linework", - "scale_set", - "shape_count" -]New value: +[ + "sheet", + "page", + "width_pt", + "height_pt", + "width_px", + "height_px", + "seg_count", + "has_vector_linework", + "scale_set", + "shape_count", + "layers" +]
5 tool updates
v0.1.8- Changed
detect_rooms1 field changed- added
Output schema / properties / rooms / items / properties / gap_bridged_pxAdded value: +{ + "description": "Present when the seal ladder bridged a drafting pinhole this many px wide to close the region", + "type": "number" +}
- Added
edit_condition - Added
export_report - Changed
one_click1 field changed- added
Output schema / properties / gap_bridged_pxAdded value: +{ + "description": "Present when the seal ladder bridged a drafting pinhole this many px wide to close the region — the rescue rides provenance (origin.gap_bridged_px) rather than passing as a clean fill", + "type": "number" +}
- Changed
undo_last2 fields changed- changed
Output schema / properties / steps / items / properties / op / enumPrevious value: -[ - "commit", - "edit", - "delete", - "materials" -]New value: +[ + "commit", + "edit", + "delete", + "materials", + "condition" +] - changed
Output schema / properties / steps / items / properties / shapes / descriptionPrevious value: -"Shapes affected by reversing this step — 0 for a materials step (it restores a condition's supporting-materials rows, not shapes)"New value: +"Shapes affected by reversing this step — 0 for a materials step (it restores a condition's supporting-materials rows, not shapes) and for a condition step (it restores the waste/multiplier pair)"
10 tool updates
v0.1.6- Added
annotate - Changed
detect_rooms5 fields changed- changed
Input schema / properties / min_area_sf / descriptionPrevious value: -"Plausibility floor: enclosed regions smaller than this are withheld as label bubbles/cavities, not rooms. Default 5 SF — below any real finished space (a broom closet is ~10 SF). Lower it to inspect what was skipped."New value: +"Plausibility floor: enclosed non-bubble regions smaller than this are withheld as cavities, not rooms. Default 5 SF — below any real finished space (a broom closet is ~10 SF). Lower it to inspect what was skipped." - added
Input schema / properties / sensitivityAdded value: +{ + "description": "Fill sensitivity, the same knob the canvas has: 0 strict (hatch/light linework always blocks), 0.5 balanced (default), 1 aggressive (crosses more hatch, tolerates more growth). Raise it when a flood stops short at hatching INSIDE the room; verify the grown ring with view_sheet overlay before committing", + "maximum": 1, + "minimum": 0, + "type": "number" +} - added
Output schema / properties / withheld / properties / bubbleAdded value: +{ + "description": "Labels whose every clean flood was their own label BUBBLE (ring bbox ≈ label bbox — plans box their room numbers). Scale-free, so it guards unscaled previews too", + "type": "integer" +} - changed
Output schema / properties / withheld / properties / implausible / descriptionPrevious value: -"Enclosed and clean, but smaller than min_area_sf — a label bubble, door swing, or wall cavity rather than a room"New value: +"Enclosed, clean, non-bubble, but smaller than min_area_sf — a door swing or wall cavity rather than a room" - changed
Output schema / properties / withheld / requiredPrevious value: -[ - "total", - "degenerate", - "duplicate", - "implausible" -]New value: +[ + "total", + "degenerate", + "duplicate", + "bubble", + "implausible" +]
- Added
edit_materials - Added
edit_shape - Added
find_text - Added
link_annotation - Added
list_annotations - Changed
one_click1 field changed- added
Input schema / properties / sensitivityAdded value: +{ + "description": "Fill sensitivity, the same knob the canvas has: 0 strict (hatch/light linework always blocks), 0.5 balanced (default), 1 aggressive (crosses more hatch, tolerates more growth). Raise it when a flood stops short at hatching INSIDE the room; verify the grown ring with view_sheet overlay before committing", + "maximum": 1, + "minimum": 0, + "type": "number" +}
- Added
sheet_context - Added
undo_last
5 tool updates
v0.1.5- Added
detect_rooms - Added
load_plan - Added
sheet_info - Added
takeoff_summary - Added
view_sheet
6 tool updates
v0.1.4- Removed
detect_rooms - Removed
load_plan - Added
one_click - Added
set_scale - Removed
sheet_info - Removed
takeoff_summary
5 tool updates
v0.1.3- Added
detect_rooms - Changed
export_takeoff1 field changed- changed
Output schema / properties / shapes / items / properties / origin / descriptionPrevious value: -"Provenance for one-click traces"New value: +"Provenance: method (manual|one_click_v1), actor (omitted=human, 'agent'=MCP/automation), reviewed (human affirmed at an explicit gate), and correction fields (edited, edited_before_create, copied, proposed_verts_norm, edits)"
- Added
load_plan - Removed
one_click - Removed
set_scale
10 tool updates
v0.1.2- Changed
delete_shape1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "deleted": { + "description": "The removed shape's id", + "type": "string" + }, + "shape_count": { + "description": "Committed shapes remaining", + "type": "integer" + } + }, + "required": [ + "deleted", + "shape_count" + ], + "type": "object" +}
- Added
export_takeoff - Removed
load_plan - Added
measure_line - Added
measure_polygon - Changed
one_click1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "area_px2": { + "description": "Preview mode (no scale): raw area in px²", + "type": "number" + }, + "area_sf": { + "description": "Scaled mode: traced area in SF", + "type": "number" + }, + "hatch_filtered": { + "const": true, + "description": "Present when hatch/pattern linework was classified out of the boundary", + "type": "boolean" + }, + "nverts": { + "description": "Vertex count of the traced polygon", + "type": "integer" + }, + "perimeter_lf": { + "description": "Scaled mode: traced perimeter in LF", + "type": "number" + }, + "perimeter_px": { + "description": "Preview mode (no scale): raw perimeter in px", + "type": "number" + }, + "shape_id": { + "description": "Scaled mode: id of the committed shape, when condition was passed", + "type": "string" + }, + "status": { + "const": "ok", + "type": "string" + }, + "verts": { + "description": "Traced polygon vertices (image px), when return_verts was set", + "items": { + "items": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "type": "array" + }, + "warning": { + "description": "Preview mode (no scale): why quantities are unavailable and what to do", + "type": "string" + } + }, + "required": [ + "status", + "nverts" + ], + "type": "object" +}
- Changed
read_sheet_text1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "items": { + "description": "Positioned text items (image px)", + "items": { + "additionalProperties": false, + "properties": { + "str": { + "type": "string" + }, + "x": { + "type": "number" + }, + "y": { + "type": "number" + } + }, + "required": [ + "str", + "x", + "y" + ], + "type": "object" + }, + "type": "array" + }, + "sheet": { + "type": "string" + }, + "text": { + "description": "The items joined with spaces", + "type": "string" + } + }, + "required": [ + "sheet", + "items", + "text" + ], + "type": "object" +}
- Added
set_scale - Changed
sheet_info1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "detected_scale": { + "description": "Drawn scale note read off the sheet — a suggestion, never auto-applied", + "type": "string" + }, + "has_vector_linework": { + "description": "one_click needs vector linework", + "type": "boolean" + }, + "height_pt": { + "type": "number" + }, + "height_px": { + "type": "number" + }, + "page": { + "description": "1-based page number", + "type": "integer" + }, + "scale_set": { + "type": "boolean" + }, + "seg_count": { + "description": "Vector segment count", + "type": "integer" + }, + "shape_count": { + "description": "Committed shapes on this sheet", + "type": "integer" + }, + "sheet": { + "description": "Sheet key: page 1 is the bare file name (\"plan.pdf\"), pages 2+ are \"plan.pdf#2\"", + "type": "string" + }, + "sheet_number": { + "description": "Title-block sheet number (\"A-101\") where detected", + "type": "string" + }, + "upp": { + "description": "Real feet per image px at render scale 2.0 — present once the scale is set", + "type": "number" + }, + "width_pt": { + "type": "number" + }, + "width_px": { + "description": "Image px at render scale 2.0 — the coordinate space every tool speaks", + "type": "number" + } + }, + "required": [ + "sheet", + "page", + "width_pt", + "height_pt", + "width_px", + "height_px", + "seg_count", + "has_vector_linework", + "scale_set", + "shape_count" + ], + "type": "object" +}
- Added
takeoff_summary
5 tool updates
v0.1.1- Removed
export_takeoff - Removed
measure_line - Removed
measure_polygon - Removed
set_scale - Removed
takeoff_summary
TDQS
Every tool has a clearly distinct purpose, from measurement (one_click, measure_polygon, measure_line, measure_surface) to exports (export_takeoff, export_report, export_marked_pdf) to schedule and text utilities. Even closely related tools like symbol_sweep and sweep_schedule_row are differentiated by input and workflow. No two tools appear to do the same thing.
All tool names use snake_case and most follow a verb_noun pattern (e.g., view_sheet, detect_rooms, edit_condition). There are a few deviations like sheet_context, sheet_info, one_click, cut_out, and undo_last, but overall the naming is predictable and readable.
With 40 tools, the server is well above the typical 3-15 well-scoped range and even above the 16-25 'heavy' range. While each tool is specialized, the sheer number feels overwhelming and could be consolidated (e.g., multiple export tools, several measurement variants, multiple list tools).
The toolset covers the full takeoff lifecycle: loading plans, setting scales, measuring areas/lengths/counts, detecting rooms, sweeping symbols, deriving base and transitions, managing conditions and materials, exporting to multiple formats, editing shapes, annotating, marking verdicts, and resolving schedules. No obvious dead ends or missing core operations.
Maintenance
Related MCP Connectors
Construction takeoff and estimating for AI agents. Measure a drawing PDF, export a priced estimate.
MCP server for the PDFGate API. Generate PDFs, manage documents and handle e-signatures.
Turn construction drawings into takeoffs, measured routes, schedules and estimates.
1Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA local MCP server that drives PDFium and pypdf to perform comprehensive PDF operations including inspection, assembly, page editing, watermarking, rendering, extraction, form filling, encryption, compression, attachments, bookmarks, and metadata management.MIT
- FlicenseNot gradedqualityCmaintenanceMCP server for hard decoration quoting: parses DXF floor plans, reconstructs room topology, computes material quantities, and generates deterministic itemized quotes with tiered pricing. Exposes tools parse_dxf, compute_quote, and generate_report via stdio.-
- AlicenseBqualityBmaintenanceA local, evidence-driven MCP runtime and control plane for open-source maintainers that provides workspace-bounded tools including controlled file operations, command execution, validation primitives, durable execution records, and human review workflows via stdio and Streamable HTTP transports.33MIT
- AlicenseAqualityAmaintenanceModern CAD CLI + MCP Server: 2D/3D drawing, editing, measurement, validation, batch automation, real-time collaboration (CRDT + WebSocket).221Apache 2.0
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Kentucky-ai/opentakeoff'
If you have feedback or need assistance with the MCP directory API, please join our Discord server