Skip to main content
Glama

symbol_sweep

Find every instance of a repeated plan symbol from one marqueed example, scoring placements by geometry across sheets and flagging near-matches for review.

Instructions

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. RICHER VARIANTS are named, never silent: a placement that reproduces the whole seed but carries >30% extra linework fully inside its footprint (a register against a grille seed — the same outline plus louvers) comes back with its measured extra fraction on the row — LOOK at those first, they are the classic mislabel; background lines CROSSING the symbol and coincident duplicate ink never trip this. By default such placements still COUNT, because the contained-seed workflow below depends on supersets matching (seed a bare sub-shape, count the richer symbols that contain it, exclude what you don't mean). Pass variant_guard: true when your seed is the WHOLE symbol — grilles, drains, fixtures marqueed complete — and extra-ink placements demote to withheld as questions instead of counting; the guard stands down automatically when exclude counter-examples are in play, since supplying negatives is manual variant discrimination. 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.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
scopeNo"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
sheetYesThe 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)
commitNoCommit every MATCH center as one EA count marker (withheld placements never commit)
mirrorNoAlso match mirrored placements
excludeNoCounter-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
conditionNoFinish tag to commit match markers under (minted on first use), e.g. 'FD-1'. Required when commit is true
rotationsNoAlso match 90/180/270-rotated placements
seed_rectYesMarquee around ONE example instance, [[x0,y0],[x1,y1]] in image px — tight: segments fully inside define the symbol
commit_seedNoSheet 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_pxNoEndpoint match tolerance in image px (default 2 — CAD jitter, not drift)
variant_guardNoWhole-symbol mode: demote richer-variant placements (>30% extra linework inside the footprint) to withheld instead of counting them with an `extra` disclosure. Use when the seed is a COMPLETE symbol (a grille, a drain); leave off when seeding a contained sub-shape. Stands down when exclude counter-examples are passed
luminance_toleranceNoStroke-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

TableJSON Schema
NameRequiredDescriptionDefault
noteNo
seedYes
foundYesPlacements that cleared the commit bar — across every swept sheet in set scope
scopeYes"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[])
sheetsNoSet scope only: one entry per swept PLAN-role sheet, load order
matchesNoSheet scope only. Deterministic reading order (y, then x). The seed's own location is never listed here
skippedNoSet scope only: every sheet excluded from counting, with role and reason — including the seed's own sheet when it is not a plan
warningNoPresent when the work cap dropped candidates — what a tighter seed rect would recover
completeYesTrue 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_totalNocommit mode: the condition's total EA after this call
lum_gateNoSheet 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[]
rejectedNoSheet 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
withheldNoSheet 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
committedNocommit mode: count shapes committed — one per match (0 when commit_refused is present)
conditionNocommit mode: the finish tag the markers counted under
negativesNoWhat each `exclude` rect was read as, in the order you passed them (#259)
shape_idsNo
candidatesNoSheet scope only — set scope accounts per sheet in sheets[]
commit_refusedNocommit mode (#376): present when the seed was too small and too common to commit on shape alone — fewer than 40 segments of seed linework and more than 50 placements cleared the bar. NOTHING was committed; the placements are still listed in matches (or per sheet in sheets[]) so you can look, and the text says what stands the guard down: variant_guard: true, exclude counter-examples, or a seed rect that captures more of the symbol
rejected_totalNoSet scope: placements counter-examples rejected across every swept sheet
seed_committedNoPresent when commit_seed: true minted the seed instance into the batch (#296) — ea_total then includes it

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed8 schema fields changedv0.1.21
    • addedInput schema / properties / variant_guard
      Added value: +{
      +  "default": false,
      +  "description": "Whole-symbol mode: demote richer-variant placements (>30% extra linework inside the footprint) to withheld instead of counting them with an `extra` disclosure. Use when the seed is a COMPLETE symbol (a grille, a drain); leave off when seeding a contained sub-shape. Stands down when exclude counter-examples are passed",
      +  "type": "boolean"
      +}
    • addedOutput schema / properties / commit_refused
      Added value: +{
      +  "description": "commit mode (#376): present when the seed was too small and too common to commit on shape alone — fewer than 40 segments of seed linework and more than 50 placements cleared the bar. NOTHING was committed; the placements are still listed in matches (or per sheet in sheets[]) so you can look, and the text says what stands the guard down: variant_guard: true, exclude counter-examples, or a seed rect that captures more of the symbol",
      +  "type": "string"
      +}
    • changedOutput schema / properties / committed / description
      Previous value: -"commit mode: count shapes committed — one per match"New value: +"commit mode: count shapes committed — one per match (0 when commit_refused is present)"
    • addedOutput schema / properties / matches / items / properties / extra
      Added value: +{
      +  "description": "Richer-variant disclosure: the fraction of the seed's total length found as UNMATCHED extra linework fully inside this placement's footprint, present when past the 0.30 bar — the classic grille-counted-as-register shape; LOOK at these first. Under variant_guard such placements demote to withheld instead of matching",
      +  "type": "number"
      +}
    • addedOutput schema / properties / rejected / items / properties / extra
      Added value: +{
      +  "$ref": "#/properties/matches/items/properties/extra"
      +}
    • addedOutput schema / properties / sheets / items / properties / matches / items / properties / extra
      Added value: +{
      +  "$ref": "#/properties/matches/items/properties/extra"
      +}
    • addedOutput schema / properties / sheets / items / properties / withheld / items / properties / extra
      Added value: +{
      +  "$ref": "#/properties/matches/items/properties/extra"
      +}
    • addedOutput schema / properties / withheld / items / properties / extra
      Added value: +{
      +  "$ref": "#/properties/matches/items/properties/extra"
      +}
  2. Changed20 schema fields changedv0.1.19
    • addedInput schema / properties / commit_seed
      Added 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"
      +}
    • addedInput schema / properties / exclude
      Added 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"
      +}
    • addedInput schema / properties / luminance_tolerance
      Added 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"
      +}
    • addedOutput schema / properties / lum_gate
      Added 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"
      +}
    • addedOutput schema / properties / matches / items / properties / label
      Added 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"
      +}
    • addedOutput schema / properties / matches / items / properties / label_via
      Added 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"
      +}
    • addedOutput schema / properties / negatives
      Added 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"
      +}
    • addedOutput schema / properties / rejected
      Added 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"
      +}
    • addedOutput schema / properties / rejected_total
      Added value: +{
      +  "description": "Set scope: placements counter-examples rejected across every swept sheet",
      +  "type": "integer"
      +}
    • addedOutput schema / properties / seed / properties / label
      Added 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"
      +}
    • addedOutput schema / properties / seed / properties / label_via
      Added value: +{
      +  "enum": [
      +    "adjacent",
      +    "leader"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / seed_committed
      Added value: +{
      +  "description": "Present when commit_seed: true minted the seed instance into the batch (#296) — ea_total then includes it",
      +  "type": "boolean"
      +}
    • addedOutput schema / properties / sheets / items / properties / lum_gate
      Added value: +{
      +  "$ref": "#/properties/lum_gate",
      +  "description": "This sheet's stated-luminance-gate accounting (#260) — present only when luminance_tolerance was stated"
      +}
    • addedOutput schema / properties / sheets / items / properties / matches / items / properties / label
      Added value: +{
      +  "$ref": "#/properties/matches/items/properties/label"
      +}
    • addedOutput schema / properties / sheets / items / properties / matches / items / properties / label_via
      Added value: +{
      +  "$ref": "#/properties/matches/items/properties/label_via"
      +}
    • addedOutput schema / properties / sheets / items / properties / rejected
      Added value: +{
      +  "description": "Placements a counter-example rejected on this sheet (#259) — never counted, always named",
      +  "items": {
      +    "$ref": "#/properties/rejected/items"
      +  },
      +  "type": "array"
      +}
    • addedOutput schema / properties / sheets / items / properties / withheld / items / properties / label
      Added value: +{
      +  "$ref": "#/properties/matches/items/properties/label"
      +}
    • addedOutput schema / properties / sheets / items / properties / withheld / items / properties / label_via
      Added value: +{
      +  "$ref": "#/properties/matches/items/properties/label_via"
      +}
    • addedOutput schema / properties / withheld / items / properties / label
      Added value: +{
      +  "$ref": "#/properties/matches/items/properties/label"
      +}
    • addedOutput schema / properties / withheld / items / properties / label_via
      Added value: +{
      +  "$ref": "#/properties/matches/items/properties/label_via"
      +}
  3. Changed5 schema fields changedv0.1.18
    • addedOutput schema / properties / complete
      Added 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"
      +}
    • changedOutput schema / properties / sheets / items / properties / candidates / description
      Previous 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"
    • addedOutput schema / properties / sheets / items / properties / complete
      Added 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"
      +}
    • changedOutput schema / properties / sheets / items / required
      Previous value: -[
      -  "sheet",
      -  "found",
      -  "matches",
      -  "withheld",
      -  "candidates",
      -  "elapsed_ms"
      -]New value: +[
      +  "sheet",
      +  "found",
      +  "matches",
      +  "withheld",
      +  "candidates",
      +  "complete",
      +  "elapsed_ms"
      +]
    • changedOutput schema / required
      Previous value: -[
      -  "scope",
      -  "found",
      -  "seed"
      -]New value: +[
      +  "scope",
      +  "found",
      +  "seed",
      +  "complete"
      +]
  4. Changed13 schema fields changedv0.1.12
    • addedInput schema / properties / scope
      Added 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"
      +}
    • addedInput schema / properties / sheet / description
      Added 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)"
    • addedOutput schema / properties / candidates / description
      Added value: +"Sheet scope only — set scope accounts per sheet in sheets[]"
    • changedOutput schema / properties / found / description
      Previous value: -"Placements that cleared the commit bar — matches.length"New value: +"Placements that cleared the commit bar — across every swept sheet in set scope"
    • changedOutput schema / properties / matches / description
      Previous 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"
    • addedOutput schema / properties / scope
      Added 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"
      +}
    • addedOutput schema / properties / seed / properties / role
      Added 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"
      +}
    • addedOutput schema / properties / seed / properties / sheet
      Added value: +{
      +  "description": "The sheet the seed rect was marqueed on",
      +  "type": "string"
      +}
    • changedOutput schema / properties / seed / required
      Previous value: -[
      -  "segments",
      -  "center",
      -  "rect",
      -  "length_px"
      -]New value: +[
      +  "sheet",
      +  "segments",
      +  "center",
      +  "rect",
      +  "length_px"
      +]
    • addedOutput schema / properties / sheets
      Added 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"
      +}
    • addedOutput schema / properties / skipped
      Added 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"
      +}
    • changedOutput schema / properties / withheld / description
      Previous 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"
    • changedOutput schema / required
      Previous value: -[
      -  "found",
      -  "matches",
      -  "withheld",
      -  "seed",
      -  "candidates"
      -]New value: +[
      +  "scope",
      +  "found",
      +  "seed"
      +]
  5. Addedv0.1.11

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full behavioral burden and does so extensively: it discloses deterministic scoring, withheld bands, richer-variant handling, scope behavior, commit side effects as one undo step, seed exclusion, scale-assumption limits, refusal conditions, and luminance/label disclosures.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with purpose and key mechanics, but it is extraordinarily long and dense with parenthetical asides and repeated warnings. Many details earn their place for a 12-parameter tool, yet the overall size is not concise and risks overwhelming an agent trying to extract invocation parameters.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, 12 parameters, full schema coverage, no annotations, and an output schema, the description is complete enough to call the tool correctly. It explains edge cases, refusals, scale behavior, commit semantics, and audit steps without needing to explain return values because an output schema exists.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline would be 3, but the description adds substantial meaning beyond the schema for seed_rect marquee discipline, exclude mechanics, variant_guard demotion, luminance_tolerance gating, commit_seed minting, and scale-dependent matching semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: finding every instance of a repeated plan symbol from one example rectangle. It distinguishes itself from siblings by naming place_count, view_sheet, and set_scale, and by explaining the deterministic geometry approach versus vision.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly covers when to use the tool, how to seed, when to pass variant_guard, how counter-examples interact, and what to do with withheld matches. It also names alternatives and the conditions that select them, such as using place_count to reinstate rejected placements and view_sheet to audit committed markers.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.