Recognition evidence¶
draftwright.inspect_step(path) returns what Draftwright saw in a STEP file, without building a
drawing. Script generation writes the same document to disk beside the script it produces.
It exists so a person or an agent can find and correct two different kinds of failing:
- recognition failings — the recogniser found the wrong thing, or missed something; and
- conversion failings — recognition found it, and the drawing does nothing with it.
from draftwright import inspect_step
document = inspect_step("part.step")
for entry in document["found"]:
if not entry["draftwright"]["acted_on"]:
print(entry["family"], entry["draftwright"]["reason"])
Version 4 adds report-local source-face identities and Quiddity 0.3.1's rejected-candidate
evidence graph. Version 3 added the bounded same-run aggregate detector lifecycle and residual
diagnostics. Version 2 names the actual recognition provider in producer.quiddity but records
candidate explanation as unavailable. Version 3,
Version 2, and
Version 1, which used
producer.b123d-recognisers, remain available for existing documents. Readers must select the
schema using schema_version; a Quiddity version is not a b123d-recognisers version.
The current closed schema is published as
draftwright-step-inspection-v4.schema.json.
schema is always "draftwright-step-inspection"; check schema_version before interpreting
the document. Every object is closed except found[].feature, which is the recogniser's own
record.
found — what recognition produced, and what became of it¶
One entry per record in the recogniser's evidence roster, in its order. This includes accepted
geometry and SectionRecessRefusal evidence where Quiddity could not establish supported
recess geometry. Each carries two halves that must not be confused:
defining_face_idsandconstituent_face_idslink the accepted occurrence to the source-face roster. Defining faces prove the classification; constituent faces are the wider set proved to belong physically to it. Equality means no wider membership was proved, not that membership is unavailable.featureis the recogniser's record, forwarded exactly as it stated it, withfeature_typeandfeature_schema_versionnaming its format. Draftwright never edits it. If it is wrong, the finding belongs upstream.draftwrightis what this engine did with it.acted_onis the plain answer —truewhen the finding became an IR feature of its own or was absorbed into one.dispositionis the precise one (represented,absorbed,unsupported,deferred,evidence_only,unexpectedly_missing), with a stablereasoncode and theownersit maps to.
acted_on: false means no drawing feature represents the record, and reason says why.
For accepted geometry this identifies a consumer limitation; for SectionRecessRefusal,
reason: recognition_refused identifies a provider limitation, not a drawable feature.
IDs are deterministic within one document, allocated from source geometry and its complete
reported association signature, the recogniser's order, and Draftwright's IR order. They are not
persistent identities and must not be stored across runs.
Document-owned IDs are never derived from provider references or topology. The open feature
payload preserves the provider's public JSON, including any run-local body or face indices;
those are not persistent references. Runtime reference objects, object addresses and absolute
paths are never serialized.
missed — what nothing claimed¶
The top-level faces roster describes every source face by report-local id, surface kind, area,
a representative position on the face and a bounding box. unclaimed_faces repeats the rows
for faces no accepted occurrence claimed, with face_count giving the totals. This is the
recogniser's own accounting, and it is a place to start looking — not a defect list. Stock,
background and deliberately plain faces are unclaimed too, and they are in the denominator.
rejected_candidates is the other half of the story. Version 3 projects Quiddity's
RecognitionEvidence.report from this exact run: coverage, every detector family's
evaluation, proposed/accepted/rejected counts, closed reconciliation dispositions, and
bounded residual diagnostics. evaluated with zero proposals is distinct from
not-applicable; neither means a feature was proved absent.
These numbers count detector candidates before public projection and deduplication. Several detectors can contribute to one accepted public occurrence, and one rejected candidate can refer to another candidate. They are not feature counts or recognition recall.
Version 4's rejected_candidates.candidates is the closed public graph rooted at Quiddity's
rejected-candidate roster. Each row has a report-local candidate:* ID, family, final outcome and
reason, defining and constituent face IDs, and direct related-candidate IDs. source distinguishes
the rejected roots from candidates reached through reconciliation links; a related candidate may
be accepted or rejected and does not necessarily map one-to-one to a public found occurrence.
The rejected roots and relationship counts are checked against the aggregate family lifecycle;
a disagreement refuses the document.
This extra evidence permits a bounded distinction between unclaimed faces retained on rejected
candidates and unclaimed faces not named by accepted or rejected evidence. The second state does
not prove a detector never examined the face or that a failed predicate did not consult it.
Candidate evidence is not a trace of every face read during discovery, a missed-feature label, or
an independent recall oracle. Quiddity's section-recess refusals remain accepted evidence records
in found; they are not a substitute for the complete family lifecycle.
source, producer and run¶
source names the file by basename and its SHA-256. The path is resolved once and read once,
and recognition consumes a private copy of those exact bytes, so replacing a mutable or
symlinked source mid-run cannot make the document describe two different files. An absolute path
is caller-machine detail and never appears.
producer gives the Draftwright and recogniser versions, so a finding can be reproduced or
filed upstream against the right release.
run records the options that determined the content. Today that is pmi_mode. It matters
because PMI lowering can rewrite a grouped hole member into a singleton owner, so two runs over
identical bytes can disagree about what Draftwright did with a finding — source.sha256 alone
would imply a reproducibility the document does not have. inspect_step always records off;
a sidecar records the mode its script was generated with. Two documents agreeing on source,
producer and run agree entirely.
Beside a generated script¶
generate_sheet_script(...) — the CLI's --script — writes
<stem>.draftwright-inspection.json next to <stem>.py. It already reads and hashes its source
and already runs recognition once, so the document is projected from that run; there is no second
aggregate. The script itself contains none of this: it is a drawing declaration a person edits,
and evidence about the run belongs in a file that can be diffed and re-read without parsing
Python.
When that Python is replayed, it writes a separate
<stem>.draftwright-assessment.json. The inspection remains the
generation-time recognition baseline; the assessment identifies the exact edited script and
records what its current declared drawing delivered. Editing or replaying the script never
rewrites this inspection document.
Pass inspect=False (the CLI's --no-report) to skip it and, by default, the replay
assessment. Skipping also removes any
document already at that path, because an earlier run's evidence beside a new script describes a
different part and nothing in it would say so.
Two sources produce a script but no document: a live build123d object has no STEP bytes and so
no source identity, and a source that cannot state its evidence truthfully logs a warning at
draftwright.sheet_emit and is skipped. A missing document never fails script generation.
What it does not avoid¶
Inspection builds no drawing: no view projection, annotation placement, render, export or lint score runs, and a guard test pins the exact set of engine modules it executes.
It is not free. Draftwright has one detect seam, shared with the drawing path, and that seam sizes the part while detecting — it picks a page and scale, runs the dimension planner, builds hole-callout specs and arranges views, all discarded. A leaner inspect-only path would be a second seam whose divergence from the drawing path could not be checked; issue #1462 tracks whether that trade should be revisited. Order of magnitude on a developer machine: a plain block inspects in a couple of seconds, a dense AP242 part such as NIST CTC-02 in tens of seconds.
What it never claims¶
Nothing here is completeness or manufacturing readiness. An empty found list means recognition
accepted nothing, not that the part has no features. An empty unused list means nothing found was
ignored, not that nothing was missed. Likewise, an evaluated family with zero rejected
candidates only describes the detector's bounded supported domain; recognition_recall remains
not-assessed. Material, process, finish, thread, fit and tolerance intent are separately
authored and never inferred here.
Failures¶
- An unreadable path (missing, a directory, permissions) raises the underlying
OSError. InspectionUnavailableErrorcovers anything that would make the document untruthful: bytes that are not readable STEP geometry, a source with no solid body, an unclassified ownership ledger, a face that cannot be described, a non-raw recognition frame, or a value JSON cannot state.
Version 1 reports raw caller coordinates only. A run that recognised in a provider working frame is refused rather than reported as caller coordinates.