WillTools: precast shop-ticket QC and panel handling tools
ci / test (push) Has been cancelled

Public demo snapshot of an in-house pyRevit extension for precast concrete
shop-ticket QC (37 checks / 46 checklist rows) and a standalone panel
handling solver (PCI Chapter 8).

- No client, job, plant or product data: knowledge layers ship as
  structural templates only.
- Ticket QC's model-read escalation targets a LAN model server endpoint
  (configurable), not a cloud provider.
- Built from the private development repo via tools/make_dist.py.
This commit is contained in:
2026-09-03 16:33:47 -05:00
commit 52d94303bc
281 changed files with 155656 additions and 0 deletions
+66
View File
@@ -0,0 +1,66 @@
# `knowledge/` in this checkout
The plant layer and the job layer are not here — never were, never will be
in a colleague distribution. `catalogue.yaml` **is** here, but it is a
**carve-out**, not the owner's real file: `tools/make_dist.py`'s
`build_catalogue_carveout()` reads the real one and re-emits only the
branches that are trade-standard or that are this tool's own built-in
configuration, never a manufacturer's product rating. Read the header at
the top of `catalogue.yaml` itself for the exact list of what's in and
what's out, and why. `catalogue.yaml.template` is a separate thing: a
blank-value authoring template for building a real knowledge layer from
scratch, not what this carve-out is built from.
## Why
WillTools reads its facts in layers, each overriding the one before it:
| layer | where | what it is |
| --- | --- | --- |
| `knowledge.DEFAULTS` | in the code | what the tool assumes when nobody has said |
| `knowledge/catalogue.yaml` | here, carved out | published hardware ratings — manufacturer data |
| plant layer | `%APPDATA%\WillTools\knowledge.yaml` | one office's naming conventions, casting-bed rails, parameter names |
| job layer | `<model folder>/_ticketqc/knowledge.yaml` | what this job does differently |
| project config | wherever the engineer put it | the pinned, merged answer for one job |
The plant and job layers, and `catalogue-rationale.md`, are either a
specific office's private template or narrate real measured incidents —
neither belongs in a colleague trial. The catalogue's two real capacity
tables (`catalogue.inserts`, `connectors.products`) ship **empty** for the
same reason a manufacturer's rating is not this tool's to redistribute.
Ask whoever gave you this checkout for the real layers, and the real
catalogue, once you are ready to point the tools at a real job.
## What runs without the missing pieces
Everything that does not need a lookup: unit conversion, the ribbon itself,
Self Check, and — outside Revit entirely — `panellift`'s handling
calculations, which read a frozen job file and never touch `knowledge/` at
all (see `examples/example-panel.job.json`).
Everything the shipped carve-out covers also runs for real: face
designations, the up-end orientation rule, ASTM rebar grades, and the whole
shop-ticket QC checklist (row ids, grades, which check answers which row) —
none of that needs the real, private file. What stands down cleanly is only
what actually needs a manufacturer's number: a lifting insert's rated
capacity, a sandwich-wall tie's rated capacity, and anything downstream of
either. `python tools/setup_machine.py --check` reports this state as a
pass, not a failure — a distribution with the carve-out and no product
data is an expected, first-class state, not a broken one. Self Check
reports the same thing inside Revit, row by row.
### Running the test suites here
`CONTRIBUTING.md`'s "What does not run in a trial install" has the full
accounting. Short version: both suites' `tests/conftest.py` carries an
appended, auto-detecting block (written by `tools/make_dist.py`'s
`append_test_exclusions()`) that skips, by name and with a reason, the
handful of tests that assert the owner's real published numbers rather
than this tool's logic. Drop in the real `knowledge/catalogue.yaml` and
those tests un-skip on their own — nothing to edit.
## Getting real knowledge layers
Ask the owner. They come from a second, private repository that is not part
of this one on purpose — see the top of `.gitignore` in the repo root for
why the split exists at all.
+570
View File
@@ -0,0 +1,570 @@
# ============================================================================
# knowledge/catalogue.yaml -- DISTRIBUTION CARVE-OUT, not the real file
# ============================================================================
# Written by tools/make_dist.py's build_catalogue_carveout(), which reads
# the owner's real knowledge/catalogue.yaml and re-emits ONLY the branches
# below -- parsed and re-dumped from the real file, never hand-typed, so
# this cannot drift out of step with what the real file actually publishes.
# The real file itself never leaves the private checkout. See
# knowledge/README.md and CONTRIBUTING.md for the full accounting.
#
# SHIPPED, AND WHY EACH PIECE IS SAFE TO:
#
# face_designations trade abbreviations (N.F., F.F., T.I.F., ...)
# printed on shop tickets everywhere. The real file's
# own header already calls this branch public --
# identifies no client, job, or plant.
# orientation.up_end a fact about precast assemblies IN GENERAL (which
# Revit axis is "up" once an assembly's origin is
# correctly configured), not one plant's naming
# convention -- the owner's own ruling for keeping it
# in the public layer rather than a plant file.
# catalogue.rebar ASTM A706 / A615 grade tables -- a published
# national standard, not a manufacturer's rating.
# catalogue.safety_factors both null, by design -- see the note inline.
# checklist.master the shop-ticket QC checklist's SKELETON: row ids,
# names, grades, and which check function answers
# each one. This tool's own built-in configuration,
# not knowledge about any job -- identifies nobody.
#
# NOT SHIPPED:
#
# catalogue.inserts real lifting-insert capacities (ALP catalog,
# 02/2024), cited to a page and a date. EMPTY here.
# catalogue.slotted_inserts real JVI slotted-insert capacities. Omitted
# entirely (nothing in this distribution's
# tests needs its structure present at all).
# connectors.products real sandwich-wall tie capacities. EMPTY here.
#
# A handful of tests read those omitted tables directly and assert the
# owner's real published numbers. They are excluded from this
# distribution's test run, by name, with that reason -- see
# tools/make_dist.py's append_test_exclusions() and CONTRIBUTING.md.
# ============================================================================
face_designations:
frames:
view:
means: Relative to the viewing direction of the drawing. It flips when the view flips, so resolving
one of these tokens requires knowing which way the elevation looks at the piece.
sides:
- near
- far
- both
form:
means: Relative to the casting bed -- how the piece lies while it is poured. Absolute for the piece;
a flipped view does not change it.
sides:
- top
- bottom
- side
- both
vocabularies:
near_far:
frame: view
measured_from: the direction the elevation looks at the piece
tokens:
- token: N.F.
means: near face
side: near
- token: F.F.
means: far face
side: far
- token: E.L.
means: each layer
side: both
in_form:
frame: form
measured_from: the casting bed the piece is poured on
tokens:
- token: T.I.F.
means: top in form
side: top
- token: B.I.F.
means: bottom in form
side: bottom
- token: S.I.F.
means: side in form
side: side
orientation:
up_end:
source: assembly_origin
also_called: TOT
stated_by: Will
stated_on: '2026-08-30'
means: Which end of a precast piece is up once it is erected is a property of the assembly's origin,
which is placed with the assembly and carries an up end. It is not derived from where the piece
happens to sit in the model, and it is not a producer's convention.
origin_up: BasisY
origin_up_as_stated: '"assembly origin 里面有一个朝上的头" -- the assembly origin itself carries an up end.'
origin_up_verified: true
origin_up_verified_by: the bridge holder, against 282 assemblies
origin_up_verified_on: '2026-08-30'
also_seen_as:
horizontal_panels:
stated_by: Will
stated_on: '2026-08-30'
status: confirmed against 282 assemblies -- see origin_up above
green:
axis: BasisY
means: the erected up direction
purple:
axis: BasisZ
means: top-in-form
red:
axis: BasisX
means: the long direction
note: The API exposes no colour at all; this three-arrow mapping is a reading of the owner's words
(green / purple / red), and it is CONFIRMED because the measurement behind origin_up above --
BasisY exactly global (0,0,1) on 281 of 282 assemblies, BasisZ the smallest extent on 25 of
26 sampled, BasisX the longest on 23 of 26 -- is the same measurement that stands the colours
up.
vertical_wall_panels:
stated_by: Will
stated_on: '2026-09-01'
status: ONE of three arrows confirmed. Do not treat this frame as complete and do not borrow purple
or red from horizontal_panels above -- the owner's own words are that the arrows are different
between the two frames to begin with, so nothing here may be assumed from there.
green:
axis: BasisY
means: world up (world Z+) when the panel is correctly configured
confirmed: true
stated_as: '"vertical wall panel里面,世界的上方应该是绿色箭头" -- in a vertical wall panel, world up should
be the green arrow.'
purple:
axis: null
means: null
confirmed: false
open_question: Not yet stated. A vertical wall panel's casting orientation (top-in-form) is
not necessarily on the same axis as a horizontal panel's -- do not infer BasisZ here. Asked
of the owner 2026-09-01; record the answer here, cited and dated, the day it arrives.
red:
axis: null
means: null
confirmed: false
open_question: Not yet stated. Do not infer BasisX (the horizontal frame's "long direction")
here -- on a vertical wall panel BasisY already carries a different meaning (world up) from
the horizontal frame's (erected up when stood on end), so nothing about red follows from the
horizontal row either. Asked of the owner 2026-09-01; record the answer here, cited and dated,
the day it arrives.
applies_to: horizontal_panels
up_tolerance: 0.99
near_square_ratio: 1.25
declines_because: A piece whose origin does not agree with its geometry, a vertical panel whose frame
nobody has explained yet, and a near-square piece that could be either are three different reasons
to have no answer. All three are answers; none of them is a default.
wp401_2026_09_01: 'WP401 is NOT an instance of applies_to declining -- it never reaches that third
check. Its measured basis_up is (0, 1, 0), so up_end_from_origin()''s FIRST refusal (the up basis
must actually be world up, within up_tolerance) fires before up_axis or applies_to are ever read;
the report''s fallback-to-global-elevation notice on WP401 is that first refusal, not the third.
Under the green-arrow rule above (vertical_wall_panels), a correctly configured vertical wall panel''s
BasisY IS world up -- so WP401''s (0, 1, 0) is a real defect on that assembly''s origin (TOT), not
merely an unconfirmed frame; the report''s red line follows from this fact, not from applies_to.
Consequence for the code, worked out and not acted on: widening applies_to to admit vertical_wall_panels
would NOT change WP401''s verdict -- refusal #1 still fires first on it regardless. It WOULD change
any piece whose origin genuinely has BasisY at world up AND whose up_axis is not ''width'': such
a piece currently declines at refusal #3 (fallback to global elevation, disclosed) and would start
being read off the origin instead (the ruled mechanism, undisclosed because it is no longer a fallback).
No such piece has been surveyed. That survey, and the decision whether to widen applies_to on the
strength of it, is the owner''s, not made here.'
origin_point_meaning: null
origin_point_unverified_because: Nobody has measured where Transform.Origin sits relative to the piece,
and nobody has asked whether the owner's "up end" means the point or the direction. Until one of
those happens the point is not read at all.
catalogue:
safety_factors:
tension_fos: null
shear_fos: null
unknown_because: There is no trade-wide safety factor on a lifting insert to publish here. A factor
belongs to the part that was tested, so it is written inside each `inserts:` entry against the page
that publishes it. The 2.67 that used to sit here had no source and the manufacturer's own page
gives 4:1 in shear for the parts it covers; see the block above. A part with no entry of its own
is NOT judged on an ultimate rating -- it is named as unjudged, because inheriting one manufacturer's
factor is exactly the failure this null exists to prevent.
inserts: {}
rebar:
stock_length_ft: 40.0
astm_grades:
A706:
grades_ksi:
- 60
- 80
default_fy_ksi: 60
default_declared_by: Will
default_declared_on: '2026-08-30'
note: Grade 60 is the default, not the definition -- A706 Grade 80 exists.
A615:
grades_ksi:
- 60
- 80
- 100
default_fy_ksi: null
note: the designation alone does not give a grade -- ask, do not assume.
connectors:
what_it_is: The part that crosses the insulation of a sandwich (insulated) wall panel and ties the two
concrete wythes together. Declared by the owner 2026-08-31.
products: {}
checklist:
master:
- id: '1.1'
name: Sheet name equals the piece mark
grade: A
owner_codes:
- SHEET-NAME
not_run_reason: null
checks: check_sheet_name
- id: '1.2'
name: Sheet design-number reduces to the piece design
grade: A
owner_codes:
- DSN-MATCH
- DSN-SUFFIX
- DSN-PREFIX
not_run_reason: null
checks: null
- id: '1.3'
name: Exactly one ticket set per piece
grade: A
owner_codes:
- TICKET-SET
not_run_reason: null
checks: check_ticket_set
- id: '1.4'
name: Every placed view name refers to this design
grade: B
owner_codes:
- VIEW-DSN
not_run_reason: null
checks: check_view_name_design
- id: '1.5'
name: Printed call-outs do not overlap each other
grade: C
owner_codes:
- CALLOUT-OVERLAP
not_run_reason: null
checks: check_callout_overlap
- id: '2.1'
name: Printed length/width/depth vs modelled
grade: B
owner_codes:
- SIZE-LENGTH
- SIZE-WIDTH
- SIZE-MAX-
- SIZE-MIN-
not_run_reason: null
checks: null
- id: '2.2'
name: Weight three ways agree, and which one is odd is named
grade: A
owner_codes:
- WGT-
not_run_reason: null
checks: check_weight_three_way
- id: '2.3'
name: Unit weight ticket vs design summary
grade: A
owner_codes:
- PCF-NOTE
- PCF-MODEL
not_run_reason: null
checks: null
- id: '2.4'
name: Cast extras (a ledge is its own family) counted in the weight
grade: A
owner_codes:
- CAST-EXTRA
not_run_reason: null
checks: check_cast_extras
- id: '3.1'
name: f'c ticket vs summary
grade: A
owner_codes:
- FC
not_run_reason: null
checks: null
- id: '3.2'
name: f'ci ticket vs summary
grade: A
owner_codes:
- FCI
not_run_reason: null
checks: null
- id: '3.3'
name: f'ci/f'c release ratio in band
grade: B
owner_codes:
- FCI-RATIO
not_run_reason: null
checks: check_release_ratio
- id: '4.1'
name: Printed strand count vs modelled
grade: B
owner_codes:
- STR-QTY
not_run_reason: null
checks: null
- id: '4.2'
name: Strand diameter & grade vs design
grade: A
owner_codes:
- STR-DIA
- STR-DIA-EXTRA
not_run_reason: null
checks: null
- id: '4.3'
name: Jacking force on ticket vs design
grade: A
owner_codes:
- JACK-FORCE
not_run_reason: null
checks: check_jacking_force
- id: '4.4'
name: Strand positions vs design chain from stated rail
grade: B
owner_codes:
- STRAND-POS
not_run_reason: null
checks: check_strand_layout
- id: '4.5'
name: Debonded strands called out where the design debonds
grade: C
owner_codes:
- STRAND-CALLOUT
not_run_reason: null
checks: check_debond_callout
- id: '4.6'
name: Row count & spacing plausible
grade: A
owner_codes:
- STRAND-SPACING
- STRAND-ROWS
- STRAND-GRID
- BED-WIDTH
- BED-OFFSET
not_run_reason: null
checks: check_strand_layout
- id: '4.7'
name: Strand debonded wherever the concrete runs short
grade: B
owner_codes:
- STRAND-DEBOND
not_run_reason: null
checks: check_strand_debond
- id: '4.8'
name: BOM strand row agrees with the section print and the model
grade: A
owner_codes:
- BOM-STRAND
not_run_reason: null
checks: check_bom_strand_count
- id: '5.1'
name: BED PLAN callout embed exists in model
grade: A
owner_codes:
- BOM-MEMBERS
- DETAIL-CALLOUT
not_run_reason: null
checks:
- check_bom_members
- check_detail_callouts
- id: '5.2'
name: Embed in model appears on ticket
grade: A
owner_codes:
- BED-REVERSE
not_run_reason: null
checks: check_bed_plan_reverse
- id: '5.3'
name: Face designations consistent with model side
grade: C
owner_codes: null
not_run_reason: 'grade C: a person reads the face designations against the model side -- by design
no check computes this row, so it is a prompt to look and never a verdict'
checks: null
- id: '5.4'
name: Mesh / WWF designation vs design
grade: A
owner_codes:
- WWF
not_run_reason: null
checks: check_wwf_designation
- id: '5.5'
name: Bar marks legible on the drawing
grade: D
owner_codes: null
not_run_reason: 'grade D: a person reads the drawing; code only guarantees the question was asked'
checks: null
- id: '5.6'
name: Bar marks unique, and model and print name the same bars
grade: B
owner_codes:
- BARMARK-UNIQUE
not_run_reason: null
checks: check_bar_mark_uniqueness
- id: '5.7'
name: No bar longer than the stock it is cut from
grade: B
owner_codes:
- BAR-LENGTH
not_run_reason: null
checks: check_bar_stock_length
- id: '5.8'
name: Connectors present on an insulated panel
grade: B
owner_codes:
- CONNECTOR-EXIST
not_run_reason: null
checks: check_connectors_exist
- id: '5.9'
name: Enough connectors for what the design calls for
grade: B
owner_codes:
- CONNECTOR-COUNT
not_run_reason: null
checks: check_connectors_sufficient
- id: '5.10'
name: Connector edge distances
grade: B
owner_codes:
- CONNECTOR-EDGE
not_run_reason: null
checks: check_connector_edge_distance
- id: '5.11'
name: Every lifter's solid zone is a member of the assembly
grade: B
owner_codes:
- SOLID-ZONE-ASM
- SOLID-ZONE-ABSENT
not_run_reason: null
checks: check_solid_zone_members
- id: '5.12'
name: Solid zones appear on the ticket's insulation layout
grade: B
owner_codes:
- SOLID-ZONE-TICKET
not_run_reason: null
checks: check_solid_zone_on_ticket
- id: '5.13'
name: Solid zone hatch matches the legend swatch
grade: B
owner_codes:
- SOLID-ZONE-HATCH
not_run_reason: null
checks: check_solid_zone_hatch
- id: '5.14'
name: Insulation recess regions present where the design calls for them
grade: C
owner_codes:
- RECESS-PRESENT
not_run_reason: null
checks: check_recess_regions
- id: '5.15'
name: Recess region hatch matches the legend swatch
grade: B
owner_codes:
- RECESS-HATCH
not_run_reason: null
checks: check_recess_hatch
- id: '6.1'
name: Insert capacity vs load
grade: A
owner_codes:
- LIFT-CAPACITY
not_run_reason: null
checks: check_lifter_capacity
- id: '6.2'
name: Insert edge distances
grade: A
owner_codes:
- LIFT-EDGE
not_run_reason: null
checks: check_lifter_edge_distance
- id: '6.3'
name: Current handling result exists & passes
grade: A
owner_codes:
- HANDLE-
not_run_reason: null
checks: check_handling_result
- id: '6.4'
name: Dunnage not under an opening
grade: A
owner_codes:
- DUN-OPENING
not_run_reason: null
checks: check_dunnage_openings
- id: '6.5'
name: DUNNAGE / ERECTION / PICKING notes say something
grade: B
owner_codes:
- NOTE-CONTENT
not_run_reason: null
checks: check_note_views
- id: '6.6'
name: Odd part count carries the BED PLAN note it requires
grade: B
owner_codes:
- PARITY-NOTE
not_run_reason: null
checks: check_parity_note
- id: '6.7'
name: Minimum spacing between lifting anchors
grade: A
owner_codes:
- LIFT-SPACING
not_run_reason: null
checks: check_lifter_anchor_spacing
- id: '6.8'
name: Printed picking stations match the modelled picking inserts
grade: B
owner_codes:
- PICK-STATIONS
- PICK-UNREADABLE
not_run_reason: null
checks: check_picking_match
- id: '6.9'
name: Lifting-point count vs panel length (MC)
grade: A
owner_codes:
- LIFT-POINTS
- LIFT-POINTS-UNREAD
not_run_reason: null
checks: check_lifting_points
- id: '6.10'
name: Lifting-point position vs PCI recommendation (MC)
grade: A
owner_codes:
- LIFT-POS
- LIFT-POS-UNREAD
not_run_reason: null
checks: check_lift_positions
- id: '6.11'
name: Dunnage-point count vs stripping-point count (MC)
grade: A
owner_codes:
- DUN-STRIP
- DUN-STRIP-UNREAD
not_run_reason: null
checks: check_dunnage_stripping_match
- id: '7.1'
name: Interference check
grade: B
owner_codes:
- CLASH
- CLASH-UNREADABLE
not_run_reason: null
checks: check_member_interference
- id: '8.1'
name: No unresolved design-summary lines
grade: C
owner_codes:
- SPEC-UNRESOLVED
not_run_reason: null
checks: check_spec_unresolved
- id: '8.2'
name: No invisible characters in ticket text
grade: A
owner_codes:
- INVISIBLE
not_run_reason: null
checks: check_invisible
+102
View File
@@ -0,0 +1,102 @@
# TEMPLATE ONLY -- not read by anything. Copy this file's SHAPE, never its
# content, into a real knowledge/catalogue.yaml if the owner gives you one.
#
# This is what `knowledge/catalogue.yaml` looks like: the "published" layer,
# meant to hold manufacturer data and trade-standard vocabulary that is the
# same for everyone -- no client, job or plant name belongs in it either.
# This particular distribution ships with NO catalogue at all (see
# tools/make_dist.py), so every value below is a placeholder and every
# section is a skeleton. Do not fill this file in and expect the tools to
# read it -- see knowledge/README.md for how to get the real one.
#
# knowledge._merge_file() treats this file's absence as ordinary: nothing
# crashes without it, but a few things stand down --
#
# * knowledge.load() falls back to knowledge.DEFAULTS for anything this
# file would have overridden -- silent, unremarkable, by design.
# * vibe.ticketpage's master shop-ticket checklist (the `checklist.master`
# key below) is read from THIS file, and is the one thing that does NOT
# degrade quietly if the file is present but broken -- see the comment
# above `CHECKLIST_ITEMS` in lib/vibe/ticketpage.py. With no file at
# all (this distribution's state) it reports a clear, named reason
# instead of a checklist that looks like everything passed.
# ============================================================== face_designations
# The vocabulary a ticket uses for "which face of the piece" -- near/far
# (measured from the viewing direction) vs. top/bottom-in-form (measured
# from the casting bed). Trade abbreviations, not client data.
face_designations:
frames: [] # e.g. [near_far, top_bottom_in_form]
vocabularies: {}
# SAMPLE_VOCAB_ID:
# frame: near_far
# measured_from: 'the direction the view looks'
# tokens:
# - {token: 'N.F.', means: 'near face', side: near}
# - {token: 'F.F.', means: 'far face', side: far}
# ==================================================================== orientation
# How a plant's model expresses which way a piece is turned. Shape only --
# every value a real file carries under here is plant knowledge, never here.
orientation: {}
# ====================================================================== catalogue
# Manufacturer ratings: lifting inserts, rebar stock lengths, and the safety
# factors that came with them from a published source. Every real entry
# carries a `source:` citation (catalogue page, date) because a rating with
# no citation is not verifiable by the next person who reads it.
catalogue:
safety_factors:
tension_fos: null
shear_fos: null
unknown_because: 'no trade-wide factor to publish -- see a real file for why'
inserts: {}
# SAMPLE_INSERT_PART_NUMBER:
# working_load_lb: 0
# shear_load_lb: 0
# ultimate_tension_lb: 0
# min_edge_distance_in: 0
# safety_factors:
# tension_fos: 0.0
# shear_fos: 0.0
# source: '<manufacturer catalog>, <page>, <date> -- <quoted line>'
# basis_fc_psi: 0
# basis_pcf: 0
rebar:
stock_length_ft: null
stock_by_size: {}
stock_exempt_marks: []
# ===================================================================== connectors
# Sandwich-wall wythe-tie products: manufacturer, designation, and every
# published capacity. Same discipline as `catalogue.inserts` -- a `source:`
# per entry, an ultimate kept separate from a working load when the vendor
# never published one.
connectors:
what_it_is: 'the part that crosses a sandwich panel and ties its wythes together'
products: {}
# 'SAMPLE Manufacturer (SAMPLE-DESIGNATION)':
# manufacturer: 'SAMPLE Manufacturer'
# designation: SAMPLE-DESIGNATION
# tie_length_in: 0.0
# ultimate_tensile_pullout_lb: 0
# capacity_lb: null
# capacity_unknown_because: 'vendor publishes no working load or factor'
# ====================================================================== checklist
# The master shop-ticket checklist: what exists, how each row is worded, and
# what grade (A/B/C/D) it carries. Same at every plant -- a plant selects
# and mutes from here, never redefines a row. See `MASTER_KEY` in
# lib/vibe/ticketpage.py for exactly how this is read, and its own comment
# for why a file that is PRESENT but broken refuses loudly rather than
# quietly running a shorter checklist.
checklist:
master: []
# - id: '1.1'
# name: 'Sheet name equals the piece mark'
# grade: 'A'
# owner_codes: ['SAMPLE-CODE']
# not_run_reason: null
# checks: 'check_sample_thing' # a function name in ticketchecks.py