dleerdefi/claude-code-construction/tag-audit-and-takeoff
Claude Code and agent skill by dleerdefi/claude-code-construction
- community
Checking that the SKILL.md file is still in the repository.
Count-based quantity takeoff and tag completeness auditing for construction drawings. Vision + OCR reconciliation, sheet markup, Excel QTO output. Triggers: 'tag audit', 'quantity takeoff', 'QTO', 'count fixtures'.
Install
npx vskill@latest install dleerdefi/claude-code-construction/tag-audit-and-takeoffSKILL.md
Tag Audit & Quantity Takeoff
Purpose
Count-based QTO for tagged construction elements — fixtures, devices, doors, equipment, and any element identified by a tag/symbol on drawings. Does NOT do area (sqft) or linear (lf) takeoffs — only discrete counts.
The QTO is a byproduct of the audit. The real value: the completeness engine that finds what's tagged, what's missing, and produces a defensible count with full provenance for every number.
Does NOT: do area/linear takeoffs, create outbound documents autonomously, override user-approved groupings, or assume quantities without provenance.
Step 0: Detect Operating Mode
Read .construction/project.yaml at the project root: present and readable means AgentCM mode (note its contract_version), absent means flat-file mode. A listing that shows the file does not decide it: read it.
AgentCM mode (.construction/project.yaml exists):
- Follow
${CLAUDE_PLUGIN_ROOT}/reference/agentcm-mode.md: which source wins, what a missing file or an empty search means, sheet ids, and how to record a correction of AgentCM's data (a missed tag goes through the ingest in Step 8) - Read
.construction/CLAUDE.mdfor project context and where things are,
except its database section: never run query.sh or query.ps1, whatever the guide offers
- Read
.construction/database.yamlforproject_idandapi_url, nothing else in it - Everything this skill reads comes from AgentCM's files, never its API, so it
runs the same with AgentCM closed; only the ingest (Step 8) needs AgentCM
- The
{sheet_id}in Step 8's payload is
the id of the entry in .construction/index/sheet_index.yaml whose sheet_number is that sheet. Never guess it from the number. If that file is missing, or its entries have no id (an older sheet-splitter wrote a pages: list with filename and page_index), say so and stop.
- Look at a sheet through AgentCM's crop script, a box at a time:
sh .construction/crop.sh <sheet> <x> <y> <w> <h> (or powershell -File .construction/crop.ps1 <sheet> <x> <y> <w> <h>) renders that box of the sheet's PDF page as a PNG and prints its path. <sheet> is the sheet's number as .construction/INDEX.md names it; the box is 0 to 1 from the top left of the sheet. Never rasterize whole sheets, and never ask for rasters/ or graph/export: they are not part of AgentCM's contract.
- A sheet's text, each line with its box:
.construction/sheets/<sheet>.tsv(Steps 3-4) - Write results back via API:
POST {api_url}/projects/{project_id}/tag-detections/ingest, sent from the outbox (Step 8)
Flat File mode (no .construction/project.yaml):
- Discover sheet images from CLAUDE.md or user-provided paths; rasterize sheet
PDFs with "${CLAUDE_PLUGIN_ROOT}/bin/construction-python" "${CLAUDE_PLUGIN_ROOT}/scripts/pdf/rasterize_page.py" "<pdf>" <page> --dpi 200 --output ".construction/skills/tag-audit-and-takeoff/pages/<sheet>.png" (working data: never /tmp or any other folder outside the project)
- Vision-only pipeline (Steps 2-3 skipped)
- Write marked-up PNGs and the Excel workbook to the project directory; the QTO
JSON goes to .construction/skills/tag-audit-and-takeoff/qto/ (Step 8)
Step 1: User Scopes the Task
User provides: (1) tag type — e.g., "plumbing fixtures", "doors", "light fixtures" (see references/tag-types.md for full list), and (2) sheet scope — specific sheets, a discipline, or "all sheets". Confirm scope before proceeding.
REQUIRED: Tag type resolution (AgentCM mode). The tag_type sent to AgentCM is one of contract 1's canonical names, listed in ${CLAUDE_PLUGIN_ROOT}/reference/agentcm-mode.md (Tag types), and nothing else. Never invent one, and never send the takeoff's grouping as a type:
- room tags →
room_tag; door tags →door_tag - single-reference callouts (detail bubbles, section marks, interior
elevations; detail_callout, section_callout, callout_single_ref) → simple_callout; multi-reference callouts (elevation markers with N/E/S/W spokes; multi_callout, callout_multi_ref) → multi_detail_callout
- fixtures, devices and equipment (plumbing fixtures, light fixtures,
diffusers, any other element tag) → element_tag
Report to the user: "Resolved type: '{name}' (from '{user_input}')" and get confirmation. If the request fits none of these, or two, ask the user.
The takeoff's grouping comes from the files (AgentCM mode). Plumbing fixtures, light fixtures and equipment are groups of the report, never types: a tag's code (WC-1, L2) belongs to the schedule whose key column holds it, .construction/schedules/<sheet>-<n>.tsv (INDEX.md's Schedules and legends table names each), and .construction/tags.tsv gives its kind (the lines starting with the code and a tab). Each line item's group is that schedule, named as INDEX.md names it, with its sheet; a code found in no schedule is grouped as not in any schedule and listed (references/qto-output-format.md). A takeoff of an element_tag group counts the codes of its schedule.
Step 1.5: Check Existing Coverage [REQUIRED GATE] (AgentCM mode only)
HARD GATE — you MUST complete this step before proceeding to Step 2. Do not skip this step.
Before scanning sheets, read what's already tagged per sheet, from AgentCM's files (never its API; the same whether AgentCM is open or closed):
- accepted: the lines of
.construction/sheets/<sheet>.groups.tsvwhose
type is the resolved type (box x y w h, text), and the lines of .construction/tags.tsv for that sheet with accepted in how and the kind of that tag (room for room_tag, door for door_tag; for element_tag, the codes in the takeoff's scope). A tags.tsv line is a point (x y): one inside a .groups.tsv box of the type is that group, the same tag, whatever its text's word order ("101 OFFICE" there is "OFFICE 101" in .groups.tsv). One inside no such box gets the box Step 8 gives a tag: around its own lines in the sheet's .tsv (the lines its text is made of, at that point)
- pending: the lines of
.construction/sheets/<sheet>.tsvwith the
resolved type in tag and pending in how, grouped into tags as Step 5 groups ("STORAGE" over "202" is one tag)
- waiting: the detections in the payloads in
.construction/skills/tag-audit-and-takeoff/outbox/ (an earlier run's, saved while AgentCM was closed): not yet in AgentCM
- behind: the sheets
.construction/pending.tsvlists, when it exists
(read its columns by their header names; sheet names the sheet): their files are behind or were edited outside AgentCM. Every tag on such a sheet is to confirm, reason "this sheet's file is behind or edited", never confirmed, even when its file says accepted
Report to user: "Sheet A-1.4: 12 tags already detected (8 accepted, 4 pending, 0 waiting)."
Reconciliation rules (the count, `references/qto-output-format.md`):
- Confirmed is the accepted tags, and only those: the accepted existing
tags, and a tag AgentCM answers is held by an accepted card (Step 8), on a sheet pending.tsv doesn't list.
- To confirm in AgentCM is every tag with a card not yet accepted, or on
its way to one: the pending existing tags, the tags waiting in the outbox, the tags this run sends that become cards, and the tags on a sheet that is behind. A tag the PE rejected in AgentCM is in no group (Step 8).
- Seen on the drawing, not in AgentCM is every tag the drawing shows that
the sheet's .tsv has no text for (Step 8): no card, so nothing to accept.
- Possible total is all three.
- One tag, once. A tag found in this run is an existing tag when it is on
the same sheet, its text matches (upper case, spaces collapsed) and one box's center falls inside the other box. Keep the existing tag, with its detection_id, and drop this run's sighting of it.
- Send only the new ones: the tags found in this run that match no
existing tag, and nothing else, go to ingest (Step 8). Nothing new, no POST.
- EXCLUDE from the neighborhood search (Steps 3-4) the lines an existing
tag already holds: a line whose box's center falls inside an existing tag's box
- AgentCM checks again at ingest: texts a card already holds come back
already_held with that card, never as a second card (Step 8)
Run it twice, get the same numbers. A second run matches its sightings to the first run's tags: pending in AgentCM's files once they are refreshed, waiting in the outbox, or, while the files have not caught up, through AgentCM's answer: a tag sent again comes back already_held with the first run's card and counts once. The same confirmed, to confirm and possible total.
Before proceeding to Step 2, report coverage and get confirmation: "Existing coverage: {N} sheets have {M} accepted, {P} pending detections for this tag type. {Q} sheets have zero coverage. Proceed with the vision scan of the {S} sheets in scope?" Every sheet in scope is looked at, covered or not: existing tags are matched, never skipped (one tag, once), and coverage only narrows the neighborhood search within a sheet (the EXCLUDE rule above), never which sheets are scanned. Wait for explicit user confirmation before continuing.
Step 2: Vision Tag Identification
CRITICAL — Vision is MANDATORY for this step.
- You MUST read each sheet's image via vision to identify tags
- You may NOT substitute text pattern matching (regex, searches of the
sheet files) for visual identification — a text search finds text, not tags
- The sheet's text is used ONLY in Steps 3-4 to spatially anchor
vision-identified tags, not to replace the visual identification step
- If you cannot get an image of a sheet, FLAG it as
"skipped — image unavailable" and move to the next sheet
- Do NOT report OCR text matches as "detected tags"
Examine each sheet image. Identify all visible tags of the requested type. Return tag texts and view type classification — no coordinates.
In AgentCM mode a sheet is read in boxes through crop.sh (Step 0): one box per view, from the ## view headings in .construction/sheets/<sheet>.txt (each heading gives the view's area as x, y, w, h), or quadrants (0 0 0.5 0.5, 0.5 0 0.5 0.5, 0 0.5 0.5 0.5, 0.5 0.5 0.5 0.5) for a sheet without views. If crop.sh refuses a box as too large to read (exit 2), split it in four and crop those. Keep each PNG with its box: a point at pixel (px, py) in a crop of box (x, y, w, h) sized W × H pixels is at sheet position (x + px / W × w, y + py / H × h).
The views or quadrants cover the whole sheet. Never crop only around the tags the sheet's text names: a tag drawn as linework, with no text layer, has no line in the sheet files, and only these boxes show it. It is a seen tag (Step 8). In the 2026-10-14 runs, crops around the text-known room tags missed STAIR 107, drawn as outlines at the sheet's right edge.
Vision must classify each tag's view: FLOOR PLAN, ENLARGED PLAN, DETAIL, SECTION, or ELEVATION. This determines record type in Step 5. Classification signals are in references/tag-types.md.
In Flat File mode: This is the primary (and only) identification step. Provide rough locations ("upper-left quadrant") for markup purposes.
Step 3: Text-Anchor Search (AgentCM mode only)
For each vision-identified tag, find its spatial anchor in the sheet's .construction/sheets/<sheet>.tsv (one line of text per row: x y w h source tag how label para view zone text, the text last) using the most distinctive word in the tag text. "WALK-IN FREEZER" → search "FREEZER". "WOMEN'S RESTROOM" → search "WOMEN".
Multiple hits expected — each is a potential tag location. Zero hits → try alternate words. Still zero → flag "vision-only, no OCR anchor".
Step 4: Spatial Neighborhood Query (AgentCM mode only)
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 95 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/tag-audit-and-takeoff/SKILL.md
Popularity
- 7-day trend score
- 5
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/dleerdefi/claude-code-construction/tag-audit-and-takeoff)