# Published hardware data. THE PUBLIC LAYER. # # This file IS part of the repository, and it is the only knowledge layer that # is. Nothing here identifies a client, a job or a plant: it is manufacturers' # catalogue data, the same for everyone who buys the part. # # It is still data rather than code. A rated load written into a .py file is a # number nobody can correct without a release, and it will be wrong eventually. # # Read FIRST, before the plant file and the job file, so either can override an # entry -- for a part tested differently, or a rating an engineer will not # accept. # # The plant layer says which catalogue part its own part number means: # # family in the model -> lifters.catalogue_alias -> catalogue.inserts # (plant knowledge) (here) # ========================================================================== # FACE DESIGNATIONS -- the parenthetical suffix on a bar or embed call-out. # ========================================================================== # # A ticket calls a bar out as `SR324 (F.F.)`, `SR310 (E.L.)`, # `P76 W/ SR5602 (S.I.F.)`. The suffix says WHICH FACE of the piece the part # goes on. Checklist 5.3 asks whether it agrees with the side the model # actually puts the part on. # # WHY THESE ARE PUBLIC. They are the trade's abbreviations, printed on # tickets everywhere, and they identify no client, no job and no producer -- # which is the whole test for what belongs in this file. What is NOT public # is WHICH SET a given producer uses: that is a house habit, so the plant # layer SELECTS from here by id (`face_designations.use: [near_far, in_form]`) # and never copies the tokens down. # # A producer using a token nobody published states its own vocabulary in its # own plant file -- `vocabularies` is a MAP, so it deep-merges and a new # vocabulary is an addition, not an edit to these. # # # THE ONE THING THIS SHAPE EXISTS TO PRESERVE # ------------------------------------------- # The two families below are measured FROM DIFFERENT THINGS and are not # interchangeable: # # near / far measured from the VIEWING DIRECTION. It flips when # the view flips. If an elevation looks at the panel # from the opposite side than was assumed, every # N.F./F.F. on that sheet is backwards -- and each one # individually still reads perfectly normal. # # top / bottom in form # measured from the CASTING BED. Absolute for the # piece. A flipped view does not touch it. # # Flattening both into one token -> meaning map would throw away exactly the # distinction that makes 5.3 decidable, so every vocabulary DECLARES ITS # FRAME and a reader resolves a token against that frame's evidence. # # # SHAPE # ----- # frames: the closed set of frames of reference. A vocabulary # naming a frame that is not here is a mistake, and a # loud one -- see knowledge.config_problems. # vocabularies: id -> { frame, measured_from, tokens } # tokens: a LIST of { token, means, side }. # `token` exactly as printed, dots and all. # `means` what it is short for, in words, so a # report can print it to a reader who has # never seen the abbreviation. # `side` the normalised symbol a check compares on, # read WITHIN this vocabulary's frame. Never # compare a `side` from one frame against a # `side` from another. # # A LIST rather than a map, on purpose: a token is written `N.F.` and a # dotted key cannot be reached by `knowledge.get(know, 'a.b.c')` at all. # It also makes a published vocabulary ATOMIC -- lists replace, so nobody # half-edits one of these; they declare their own instead. # # Adding a third vocabulary is a new entry under `vocabularies`. No schema # change, no code change. # # # THE MODEL-SIDE HALF (shape only -- every value below is plant knowledge) # ----------------------------------------------------------------------- # A face designation is printed on a ticket AND recorded on the element, and # 5.3 asks whether the two agree. The keys a plant states for the second one, # all of them siblings of `use` and `vocabularies`: # # model_tokens what the element's field holds -> the printed # designation. A MAP, so it deep-merges. SEPARATE from the # vocabularies rather than an alias on a token, because # `tokens` is a list and lists replace -- a plant aliasing # a published token would replace the whole published # vocabulary. # read_from where that field is read. `[instance]`, and NOT # `[instance, type]` the way the reinforcing rule is: # measured over a real ticket corpus, the same part code # carries different face tokens on different sheets, so # the face is a property of the PLACEMENT. A type read # collapses distinct facts onto one answer and looks # clean doing it. # modifiers tokens that STACK AFTER a face token in the same # call-out -- `P106 (S.I.F.) (E.S.)` -- and say something # other than which face. A modifier is orthogonal to the # face axis, so it has no (frame, side) and cannot be a # vocabulary entry; a reader strips it and resolves the # face underneath. A reader expecting exactly one # parenthesised group mis-parses the pair, which is the # whole reason this key exists. # not_face_tokens groups that pattern-match a face designation and are # not one. The negative has to be written down or it # returns as a false positive. # attaches_to token -> the member kinds it may attach to ('embed', # 'bar'). Real grammar, measured, and a check waiting to # be written; DATA rather than a rule buried in one. # recorded_on the member kinds whose elements record a face at all. # Empty means NOBODY HAS SAID -- never "none". It is what # tells a blank on a kind that does record faces # (unknown) from a blank on a kind that never does (does # not apply). # top_in_form the LABEL under which a piece states which building # face ends up on top in the form. That value is PER # PIECE and it varies, so a form-anchored token cannot be # resolved to a building face without reading it -- # `knowledge.building_face_of()` refuses instead of # assuming a handedness, because assuming one is not # slightly wrong, it is inverted on half a job. # # NOTHING HERE EVER READS ABSENCE AS A VALUE, and no key may be added that # does. An unmarked call-out is annotation that was not repeated; a blank # field is a kind that does not use it. `same_face()` answering None rather # than False is the instrument for that. # # This section sits ABOVE `catalogue:` because `vibe.yamlkit` -- the host-side # reader the test suite uses -- used to stop at the first folded scalar (`>-`), # and there is one under `inserts`, so everything below it was invisible to # every test that read this file. That is fixed (yamlkit now reads block # scalars) and the section stays here anyway: face designations are not # hardware, and the file reads better with them beside `catalogue:` rather # than buried under it. face_designations: # -> rationale.md § face_designations.frames 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: # -> rationale.md § face_designations.vocabularies.near_far 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 # ------------------------------------------------------------------ # CASTING ORIENTATION, seen from the bed. T.I.F. / B.I.F. are as common # at other producers as N.F. / F.F. are here, and S.I.F. turns up # alongside the near/far set on the same ticket -- which is exactly why # a plant may select two vocabularies at once. # ------------------------------------------------------------------ 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 # sources: 2026-08-30 # -> rationale.md § orientation orientation: up_end: # THE RULE. `assembly_origin` is the only value this reads today; another # string reads as "somebody wrote a mechanism nobody implemented", and # `up_end_rule()` stands down and quotes it back rather than guessing. 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. # -> rationale.md § orientation.up_end.origin_up 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' # -> rationale.md § orientation.up_end.also_seen_as # TWO DISTINCT FRAMES, not one rule with wider coverage. Owner, # 2026-09-01, on being shown this file: "分开,horizontal panel跟 # vertical panel,三个箭头的方向本来就是不一样的。这个应该进世界知识。" # -- separate them; horizontal and vertical panels have different # meanings for all three arrows to begin with; this belongs in world # knowledge. So: this key used to be one string covering one (inferred) # mapping. It is now a mapping of TWO frames, each with its own three # arrows, each fact cited to who said it and when, and NEITHER frame may # borrow a colour's meaning from the other -- that borrowing is exactly # the mistake this restructure exists to prevent. 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. # -> rationale.md § orientation.up_end.applies_to # CODE READS THIS FIELD VERBATIM -- up_end_from_origin() in # WillTools.extension/lib/vibe/knowledge.py (~line 3269) declines its # THIRD check when applies_to == 'horizontal_panels' and the piece's # up_axis is not 'width' (i.e. the piece is not lying/erected horizontal). # LEFT UNCHANGED here on purpose. also_seen_as above is DATA about what # the three arrows mean on each frame; it is not a claim that the ruled # mechanism is now confirmed to run on a vertical panel, and widening # this value would make up_end_from_origin() stop declining there -- a # change to real verdicts on real pieces, and the owner's to make, not # this landing's. See wp401_2026_09_01 below for the one real piece this # was checked against and what widening would and would not change on it. 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. # -> rationale.md § orientation.up_end.wp401_2026_09_01 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. # -> rationale.md § orientation.up_end.origin_point_meaning 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: # * 2.67 HAD NO SOURCE. Nothing published it, nobody typed it, and no # sources: p53; 2026-08-30 2026-08-31 # -> rationale.md § catalogue.safety_factors 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: # sources: p53 # -> rationale.md § catalogue.inserts.QLPD2T3WG QLPD2T3WG: working_load_lb: 4000 shear_load_lb: 4000 ultimate_tension_lb: 16000 min_edge_distance_in: 6 safety_factors: tension_fos: 4.0 shear_fos: 4.0 source: 'ALP catalog p53, 02/2024 -- "Capacity in Concrete 4:1 SWL", the table heading, spanning both load columns' min_spacing_rule: multiple_of_edge_distance: 2.0 published_in: null source: 'ALP catalog p53, 02/2024 -- footnote, "Minimum anchor spacing is 2x the published edge distance"' basis_fc_psi: 3500 basis_pcf: 145 lifting_wythe_in: 3 clutch: QL001 (2T-3T) QLPD4T3WG: working_load_lb: 6257 shear_load_lb: 6257 ultimate_tension_lb: 32000 min_edge_distance_in: 6 safety_factors: tension_fos: 4.0 shear_fos: 4.0 source: 'ALP catalog p53, 02/2024 -- "Capacity in Concrete 4:1 SWL", the table heading, spanning both load columns' min_spacing_rule: multiple_of_edge_distance: 2.0 published_in: null source: 'ALP catalog p53, 02/2024 -- footnote, "Minimum anchor spacing is 2x the published edge distance"' basis_fc_psi: 3500 basis_pcf: 145 lifting_wythe_in: 3 clutch: QL002 (4T-6T) QLPD4T4WG: working_load_lb: 7200 shear_load_lb: 7200 ultimate_tension_lb: 32000 min_edge_distance_in: 7.5 safety_factors: tension_fos: 4.0 shear_fos: 4.0 source: 'ALP catalog p53, 02/2024 -- "Capacity in Concrete 4:1 SWL", the table heading, spanning both load columns' min_spacing_rule: multiple_of_edge_distance: 2.0 published_in: null source: 'ALP catalog p53, 02/2024 -- footnote, "Minimum anchor spacing is 2x the published edge distance"' basis_fc_psi: 3500 basis_pcf: 145 lifting_wythe_in: 4 clutch: QL002 (4T-6T) QLPD4T5WG: working_load_lb: 8000 shear_load_lb: 8000 ultimate_tension_lb: 32000 min_edge_distance_in: 9 safety_factors: tension_fos: 4.0 shear_fos: 4.0 source: 'ALP catalog p53, 02/2024 -- "Capacity in Concrete 4:1 SWL", the table heading, spanning both load columns' min_spacing_rule: multiple_of_edge_distance: 2.0 published_in: null source: 'ALP catalog p53, 02/2024 -- footnote, "Minimum anchor spacing is 2x the published edge distance"' basis_fc_psi: 3500 basis_pcf: 145 lifting_wythe_in: 5 clutch: QL002 (4T-6T) note: >- shear must act perpendicular to the anchor face and the centreline of the recess; pulling in shear parallel to the face can spall the concrete and overload the anchor QLPD8T6WG: working_load_lb: 11019 shear_load_lb: 11019 ultimate_tension_lb: 64000 min_edge_distance_in: 11 safety_factors: tension_fos: 4.0 shear_fos: 4.0 source: 'ALP catalog p53, 02/2024 -- "Capacity in Concrete 4:1 SWL", the table heading, spanning both load columns' min_spacing_rule: multiple_of_edge_distance: 2.0 published_in: null source: 'ALP catalog p53, 02/2024 -- footnote, "Minimum anchor spacing is 2x the published edge distance"' basis_fc_psi: 3500 basis_pcf: 145 lifting_wythe_in: 6 clutch: QL003 (8T-12T) QLPD8T7WG: working_load_lb: 14640 shear_load_lb: 14640 ultimate_tension_lb: 64000 min_edge_distance_in: 12.5 safety_factors: tension_fos: 4.0 shear_fos: 4.0 source: 'ALP catalog p53, 02/2024 -- "Capacity in Concrete 4:1 SWL", the table heading, spanning both load columns' min_spacing_rule: multiple_of_edge_distance: 2.0 published_in: null source: 'ALP catalog p53, 02/2024 -- footnote, "Minimum anchor spacing is 2x the published edge distance"' basis_fc_psi: 3500 basis_pcf: 145 lifting_wythe_in: 7 clutch: QL003 (8T-12T) QLPD8T8WG: working_load_lb: 16000 shear_load_lb: 16000 ultimate_tension_lb: 64000 min_edge_distance_in: 14 safety_factors: tension_fos: 4.0 shear_fos: 4.0 source: 'ALP catalog p53, 02/2024 -- "Capacity in Concrete 4:1 SWL", the table heading, spanning both load columns' min_spacing_rule: multiple_of_edge_distance: 2.0 published_in: null source: 'ALP catalog p53, 02/2024 -- footnote, "Minimum anchor spacing is 2x the published edge distance"' basis_fc_psi: 3500 basis_pcf: 145 lifting_wythe_in: 8 clutch: QL003 (8T-12T) # sources: p16-18 (LIFTING PIN SYSTEM, "ALP Lifting Pin Anchors" / # "ALP Lifting Pin Anchors capacity in concrete"); 2026-09-07 # -> rationale.md § catalogue.inserts.LPA4T512G LPA4T512G: manufacturer: ALP ton: 4T clutch: QL002 (4T-6T) dimensions_in: {L: 5.5, HD: 1.4375, RD: 0.75, FD: 2} ultimate_mechanical_tension_lb: 32000 weight_lb: 1.05 qty_per_bag: 50 qty_per_crate: 2000 head_marking: ALP 4T 512 # NOT one basis + sqrt(f'c) -- published at FOUR discrete concrete # strengths, and capacity keeps RISING past 3,500 psi (6,950 lb at # 3,500 -> 8,000 lb at 5,000, +15%), which concrete_derate()'s "no # gain above basis" rule would silently cap at 6,950. So no # working_load_lb / basis_fc_psi is set on this entry -- that would # invite the generic derate formula to run on a table it does not # fit -- and the four points land as their own table instead. working_load_lb: null basis_fc_psi: null unrated_because: >- rated at four discrete concrete strengths (by_fc_psi below), not a single basis + sqrt(f'c) -- see the comment above this entry by_fc_psi: - {fc_psi: 1500, tension_lb: 4550} - {fc_psi: 2500, tension_lb: 5850} - {fc_psi: 3500, tension_lb: 6950} - {fc_psi: 5000, tension_lb: 8000} # TENSION edge distance -- what lifter_edge_distance() reads. min_edge_distance_in: 11 # SHEAR edge distance is larger and is its own field on purpose: the # page prints two DIFFERENT minimums (11in tension / 17in shear) and # folding them into one number would either overstate the tension # case or understate the shear one. No reader consumes this scalar # yet -- lifter_edge_distance() is single-valued and reads the # tension figure above. min_edge_distance_shear_in: 17 # No published shear LOAD for this part -- p18's SWL columns are # TENSION only (headed "Capacity in Concrete, 4:1 SWL", four PSI # columns); shear appears only as the larger minimum edge distance # above, never as a rated load. Naming it unjudged rather than # borrowing another product's shear or guessing a factor. shear_load_lb: null shear_unknown_because: >- ALP Catalog 2024 p18 tabulates tension by concrete strength only for the LPA series; it publishes a minimum shear EDGE DISTANCE (17in) for this part but no shear LOAD anywhere on the page. safety_factors: tension_fos: 4.0 shear_fos: null source: 'ALP Catalog 2024 p18, "Capacity in Concrete, 4:1 SWL" -- the table heading. No shear factor is published for this part; see shear_unknown_because.' min_spacing_rule: multiple_of_edge_distance: 2.0 published_in: null source: 'ALP Catalog 2024 p18 footnote, "Minimum anchor spacing is 2x the published edge distance"' source: 'ALP Catalog 2024 p16-17 (pdf p17-18, "ALP Lifting Pin Anchors" -- dimensions, ultimate mechanical tension, weight; Revised 04/30/25), p18 (pdf p18, "ALP Lifting Pin Anchors capacity in concrete" -- SWL by concrete strength, edge distances; Revised 03/28/2024). Read 2026-09-07, PDF text layer. This is the LPA4T512G row only -- the same two pages publish the full LPA family (1T through 20T, 30 rows); only the one row this job actually models is landed.' # sources: p35; 2026-09-01 # -> rationale.md § catalogue.inserts.QL589SPG QL589SPG: working_load_lb: 16000 shear_load_lb: 4010 basis_fc_psi: 3500 basis_pcf: 145 clutch: QL003 (8T-12T) # p35 publishes a 'Min. Corner Distance', which is NOT an edge # distance -- a corner is two free edges at once and the two numbers # are not interchangeable. It is recorded under its own name; this # entry still has no published edge distance, and # `lifter_spacing_minimum` is right to say it has nothing to double. min_corner_distance_in: 18 requires_tension_bar_for_rating: true tension_without_tension_bar_lb: 7690 # the 8 in row, for contrast source: 'ALP Catalog 2024 p35, QuikLift Tech Erection Anchor capacity table -- "Capacity in Concrete", columns "4:1 SWL Tension w/o Tension Bar" and "4:1 SWL Tension w/ Tension Bar". Table basis 3,500 psi and 145 PCF. Read 2026-09-01; which column applies is chosen by the plant layer, not here' # A list of records, not a mapping keyed by thickness -- 2026-09-01, a # bare float key here (`7.5:`) broke `json.dumps(..., sort_keys=True)` # on the whole document with `TypeError: '<' not supported between # instances of 'float' and 'str'` the moment any other section had a # string key, and a float is a poor key for a reader doing a lookup # with measured-thickness noise anyway (a string key sidesteps the # TypeError but is still matched against float noise by whoever reads # it). Six rows, each thickness explicit as its own field. by_panel_thickness_in: - {thickness_in: 7.5, shear_266_lb: 6032, shear_41_lb: 4010, tension_no_bar_lb: 7220, tension_with_bar_lb: 16000} - {thickness_in: 8.0, shear_266_lb: 6032, shear_41_lb: 4010, tension_no_bar_lb: 7690, tension_with_bar_lb: 16000} - {thickness_in: 9.0, shear_266_lb: 6195, shear_41_lb: 4120, tension_no_bar_lb: 8640, tension_with_bar_lb: 16000} - {thickness_in: 10.0, shear_266_lb: 6436, shear_41_lb: 4280, tension_no_bar_lb: 9580, tension_with_bar_lb: 16000} - {thickness_in: 11.0, shear_266_lb: 6647, shear_41_lb: 4420, tension_no_bar_lb: 10610, tension_with_bar_lb: 16000} - {thickness_in: 12.0, shear_266_lb: 6842, shear_41_lb: 4550, tension_no_bar_lb: 11680, tension_with_bar_lb: 16000} note: >- used on edge at the top of a wall panel to stand it up, so shear is the rating that governs while the piece is tilting and tension is the one that governs once it hangs plumb. The 2.66:1 shear column is available "at the discretion of the engineer for one-time lifts" (p35 footnote) and is deliberately NOT the one carried as shear_load_lb -- a one-time-lift allowance is the engineer's call, not the checker's # sources: p35; 2026-09-07 # -> rationale.md § catalogue.inserts.QL589G QL589G: # THE BASE ANCHOR -- no shear plate, no SPW/SPL/SPT dimensions, # lighter and packed 250/crate against QL589SPG's 200/crate. Same # ring clutch (QL003, 8T-12T), same page family (QL527G / QL548G / # QL589G), a DIFFERENT physical part from QL589SPG above -- see the # rationale note for why the two must not be read as one product. working_load_lb: 16000 shear_load_lb: 4010 basis_fc_psi: 3500 basis_pcf: 145 clutch: QL003 (8T-12T) dimensions_in: {W: 3.75, L: 13.5, T: 0.75, HD: 1, A: 1, B: 3.625, N: 3.125} ultimate_mechanical_tension_lb: 64000 weight_lb: 8.44 qty_per_crate: 250 min_corner_distance_in: 18 requires_tension_bar_for_rating: true tension_without_tension_bar_lb: 7690 # the 8 in row, for contrast source: 'ALP Catalog 2024 p35 (pdf p35, "QuikLift Tech Erection Anchor" -- QL527G / QL548G / QL589G table, footer "34 QUIKLIFT SYSTEM 02/2024"). Read 2026-09-07 off the PDF text layer, cross-checked at 8x zoom against the rendered page image.' by_panel_thickness_in: - {thickness_in: 7.5, shear_266_lb: 6032, shear_41_lb: 4010, tension_no_bar_lb: 7220, tension_with_bar_lb: 16000} - {thickness_in: 8.0, shear_266_lb: 6032, shear_41_lb: 4010, tension_no_bar_lb: 7690, tension_with_bar_lb: 16000} - {thickness_in: 9.0, shear_266_lb: 6195, shear_41_lb: 4120, tension_no_bar_lb: 8640, tension_with_bar_lb: 16000} - {thickness_in: 10.0, shear_266_lb: 6436, shear_41_lb: 4280, tension_no_bar_lb: 9580, tension_with_bar_lb: 16000} - {thickness_in: 11.0, shear_266_lb: 6647, shear_41_lb: 4420, tension_no_bar_lb: 10610, tension_with_bar_lb: 16000} - {thickness_in: 12.0, shear_266_lb: 6842, shear_41_lb: 4550, tension_no_bar_lb: 11680, tension_with_bar_lb: 16000} note: >- used on edge at the top of a wall panel to stand it up, so shear is the rating that governs while the piece is tilting and tension is the one that governs once it hangs plumb. The 2.66:1 shear column is available "at the discretion of the engineer for one-time lifts" (p35 footnote) and is deliberately NOT the one carried as shear_load_lb -- a one-time-lift allowance is the engineer's call, not the checker's # sources: p48 p49; 2026-09-01 # -> rationale.md § catalogue.inserts.QL184G QL184G: manufacturer: ALP ton: 8T clutch: QL003 (8T-12T) dimensions_in: W: 6 L: 4.75 T: 0.75 A: 3.5 B: 4.375 # Ultimate, not SWL -- see the capacity matrix below for the SWL # values this anchor is actually rigged to. Provenance for the # `reinforced_tension_4_1_lb` identity check on the shaded rows, not # an input to a working-load check by itself. ultimate_mechanical_tension_lb: 64000 weight_lb: 5.62 qty_per_crate: 350 # SOURCE: ALP Catalog 2024 p.48-49 (pdf p49-50), judge-verified 2026-09-01 QL184SPG: manufacturer: ALP ton: 8T clutch: QL003 (8T-12T) dimensions_in: W: 6 L: 4.75 T: 0.75 A: 3.5 B: 4.375 SPW: 3.5 SPL: 3 SPT: 0.375 ultimate_mechanical_tension_lb: 64000 weight_lb: 6.84 qty_per_crate: 350 # SOURCE: ALP Catalog 2024 p.48-49 (pdf p49-50), judge-verified 2026-09-01 # ---- QuikLift Plate Anchors -- ALP Catalog 2024 p52 (pdf p53), footer # "52 QUIKLIFT SYSTEM 02/2025". Back-stripping anchors, a plate # welded to the foot rather than the LPA/QuikLift ring-clutch # shapes above. sources: p52; 2026-09-07 # -> rationale.md § catalogue.inserts.QL846G QL846G: manufacturer: ALP ton: 4T clutch: QL002 (4T-6T) dimensions_in: {W: 1.5, H: 3, T: 0.625, FL: 3} ultimate_mechanical_tension_lb: 32000 weight_lb: 1.23 qty_per_crate: 2000 working_load_lb: 3570 shear_load_lb: 3570 min_edge_distance_in: 6 # Published edge distance is TENSION only. The page's own footnote: # "Shear edge distances should be based on ACI calculations or # testing in plain concrete" -- do not read min_edge_distance_in as # a shear edge distance too. shear_edge_distance_published: false note: >- rebar should not be placed between the recess member and the base plate; this may reduce the capacity of the system (page dagger note, marked on this part) safety_factors: tension_fos: 4.0 shear_fos: 4.0 source: 'ALP Catalog 2024 p52, "Capacity in Concrete 4:1 SWL" -- the table heading, spanning both load columns' min_spacing_rule: multiple_of_edge_distance: 2.0 published_in: null source: 'ALP Catalog 2024 p52 footnote, "Minimum anchor spacing is 2x the published edge distance"' basis_fc_psi: 3500 basis_pcf: 145 source: 'ALP Catalog 2024 p52 (pdf p53, "QuikLift Plate Anchors" -- dimensions and "Capacity in Concrete 4:1 SWL" tables). Read 2026-09-07 off the PDF text layer, cross-checked at 8x zoom against the rendered page image.' QL847G: manufacturer: ALP ton: 4T clutch: QL002 (4T-6T) dimensions_in: {W: 1.5, H: 3.5, T: 0.625, FL: 3} ultimate_mechanical_tension_lb: 32000 weight_lb: 1.42 qty_per_crate: 1700 working_load_lb: 4700 shear_load_lb: 4700 min_edge_distance_in: 7 shear_edge_distance_published: false note: >- rebar should not be placed between the recess member and the base plate; this may reduce the capacity of the system (page dagger note, marked on this part) safety_factors: tension_fos: 4.0 shear_fos: 4.0 source: 'ALP Catalog 2024 p52, "Capacity in Concrete 4:1 SWL" -- the table heading, spanning both load columns' min_spacing_rule: multiple_of_edge_distance: 2.0 published_in: null source: 'ALP Catalog 2024 p52 footnote, "Minimum anchor spacing is 2x the published edge distance"' basis_fc_psi: 3500 basis_pcf: 145 source: 'ALP Catalog 2024 p52 (pdf p53, "QuikLift Plate Anchors" -- dimensions and "Capacity in Concrete 4:1 SWL" tables). Read 2026-09-07 off the PDF text layer, cross-checked at 8x zoom against the rendered page image.' QL044G: manufacturer: ALP ton: 4T clutch: QL002 (4T-6T) dimensions_in: {W: 1.5, H: 4.375, T: 0.625, FL: 3.875} ultimate_mechanical_tension_lb: 32000 weight_lb: 1.81 qty_per_crate: 1000 working_load_lb: 4730 shear_load_lb: 4730 min_edge_distance_in: 9 shear_edge_distance_published: false # No dagger note on this row -- the rebar-placement caution above is # marked on QL846G / QL847G (and QL128G) only, not on this part. safety_factors: tension_fos: 4.0 shear_fos: 4.0 source: 'ALP Catalog 2024 p52, "Capacity in Concrete 4:1 SWL" -- the table heading, spanning both load columns' min_spacing_rule: multiple_of_edge_distance: 2.0 published_in: null source: 'ALP Catalog 2024 p52 footnote, "Minimum anchor spacing is 2x the published edge distance"' basis_fc_psi: 3500 basis_pcf: 145 source: 'ALP Catalog 2024 p52 (pdf p53, "QuikLift Plate Anchors" -- dimensions and "Capacity in Concrete 4:1 SWL" tables). Read 2026-09-07 off the PDF text layer, cross-checked at 8x zoom against the rendered page image.' QL054G: manufacturer: ALP ton: 8T clutch: QL003 (8T-12T) dimensions_in: {W: 2.5, H: 6.25, T: 0.75, FL: 5} ultimate_mechanical_tension_lb: 64000 weight_lb: 5.43 qty_per_crate: 400 working_load_lb: 6350 shear_load_lb: 6350 min_edge_distance_in: 12 shear_edge_distance_published: false # No dagger note on this row -- see QL044G's comment above. safety_factors: tension_fos: 4.0 shear_fos: 4.0 source: 'ALP Catalog 2024 p52, "Capacity in Concrete 4:1 SWL" -- the table heading, spanning both load columns' min_spacing_rule: multiple_of_edge_distance: 2.0 published_in: null source: 'ALP Catalog 2024 p52 footnote, "Minimum anchor spacing is 2x the published edge distance"' basis_fc_psi: 3500 basis_pcf: 145 source: 'ALP Catalog 2024 p52 (pdf p53, "QuikLift Plate Anchors" -- dimensions and "Capacity in Concrete 4:1 SWL" tables). Read 2026-09-07 off the PDF text layer, cross-checked at 8x zoom against the rendered page image.' # sources: p48 p49 p50 p35; 2026-09-01 # -> rationale.md § catalogue.insulated_panel_anchor_capacities insulated_panel_anchor_capacities: _source: 'ALP Catalog 2024 p.48-49 (pdf p49-50), judge-verified 2026-09-01' _basis_fc_psi: 4500 _rebar_grade: 60 panel_group_triple_semantics_uncertain: true panel_group_triple_semantics_unknown_because: >- The panel_group caption's three numbers (e.g. `(2" x 2" x 4")`) are never defined on the page -- the catalogue does not say which position is the exterior wythe, the insulation, or the interior wythe, and does not say the order is even consistent between groups. The left-side thumbnail sketches make this worse rather than better: the "(2\" x 2\" x 4\")" group's thumbnail dimensions read 4/2/2 top-to-bottom, and the "(4\" x 2\" x 2\")" group's thumbnail reads 2/2/4 -- the two thumbnails do not even agree with each other on an order, let alone with the caption string. Landed as the verbatim caption string only. The order's meaning is an open question for Will or ALP, never a parsed value. rows: - panel_group: 8" insulated panel (2" x 2" x 4") part_number: QL184SPG ton: 8T shear_2_66_1_lb: 6900 shear_4_1_lb: 4600 shear_perpendicular_4_1_lb: 9870 reinforced_tension_4_1_lb: 16000 reinforced_tension_mechanically_limited: true rebar_size: '#5' bottom_bend_tl_in: 49 bottom_bend_detail: B top_bend_tl_in: 39 top_bend_detail: A - panel_group: 8" insulated panel (3" x 2" x 3") part_number: QL184G ton: 8T shear_2_66_1_lb: 5775 shear_4_1_lb: 3850 shear_perpendicular_4_1_lb: 9900 reinforced_tension_4_1_lb: 16000 reinforced_tension_mechanically_limited: true rebar_size: '#5' bottom_bend_tl_in: 39 bottom_bend_detail: A top_bend_tl_in: 39 top_bend_detail: A - panel_group: 8" insulated panel (3" x 2" x 3") part_number: QL184SPG ton: 8T shear_2_66_1_lb: 6600 shear_4_1_lb: 4400 shear_perpendicular_4_1_lb: 8800 reinforced_tension_4_1_lb: 16000 reinforced_tension_mechanically_limited: true rebar_size: '#5' bottom_bend_tl_in: 39 bottom_bend_detail: A top_bend_tl_in: 39 top_bend_detail: A - panel_group: 8" insulated panel (4" x 2" x 2") part_number: QL184G ton: 8T shear_2_66_1_lb: 5175 shear_4_1_lb: 3450 shear_perpendicular_4_1_lb: 10500 reinforced_tension_4_1_lb: 16000 reinforced_tension_mechanically_limited: true rebar_size: '#5' bottom_bend_tl_in: 39 bottom_bend_detail: A top_bend_tl_in: 49 top_bend_detail: B # sources: 2026-08-30 # -> rationale.md § catalogue.rebar rebar: stock_length_ft: 40.0 # -> rationale.md § catalogue.rebar.astm_grades astm_grades: # Low-alloy, the weldable spec. Grade 60 only for most of its life, and # the current A706/A706M also defines Grade 80 -- so 60 is a sound # DEFAULT and not an identity. The default is Will's ruling, # 2026-08-30, asked as a question ("A706 不就是 60ksi?") and recorded # as the answer to one. 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. # Carbon steel. The current standard covers Grades 60, 80 and 100, and # historically 40 and 75 as well. NO DEFAULT IS DEFENSIBLE: a bar # listed only as A615 could be any of them, and a check that assumed 60 # would be confidently wrong on a job that uses 80. A615: grades_ksi: [60, 80, 100] default_fy_ksi: null note: the designation alone does not give a grade -- ask, do not assume. # Welded wire reinforcement (WWF / mesh): the DESIGNATION GRAMMAR, which # is public (ASTM A1064/A1064M, size-number definition; also the trade # explainers, e.g. mcswusa.com "how to read welded wire mesh sizes"), # landed 2026-09-07 for issue #23 item 9g. Which STYLES a plant stocks is # plant knowledge and lives in each plant layer (`mesh:`), never here. # # "6x6-W2.9xW2.9" -> longitudinal spacing 6 in, transverse spacing 6 in, # longitudinal wire W2.9, transverse wire W2.9 # W = plain (smooth) wire, D = deformed wire; the number after the letter # is the wire's nominal cross-sectional area in HUNDREDTHS of a square # inch (W2.9 = 0.029 in2, D3 = 0.03 in2, D16 = 0.16 in2). # area per foot of width, one layer = (100 / spacing_in) ... i.e. # area_in2_per_ft = wire_area_in2 * 12 / spacing_in # (6x6-W2.9xW2.9 -> 0.029 * 12 / 6 = 0.058 in2/ft each way). # Older "gauge" designations (e.g. 6x6-10/10) are a different system and # are NOT converted here; a gauge string is reported unparsed, never # guessed into an area. wwf: designation_pattern: 'x-x' wire_letter_meaning: {W: plain, D: deformed} wire_number_unit: hundredths of a square inch of nominal wire area area_per_ft_formula: wire_area_in2 * 12 / spacing_in gauge_designations_convertible: false # SOURCE: ASTM A1064/A1064M size-number footnote ("the number of one # hundredth of a square inch in the nominal area of the wire cross # section"), read 2026-09-07 via the ASTM store abstract; the # designation layout per Mid Continent Steel & Wire's reading guide. # sources: 2026-08-31 # -> rationale.md § catalogue.slotted_inserts slotted_inserts: _source: 'JVI PSA catalogue 2016-5, p.3 (capacities), p.6 (dimensions), p.7 (edge distance, weights), p.5 (strap anchors, materials, finishes)' _read_on: '2026-08-31' # THE FACTOR THAT IS NOT HERE, said once for the whole block. safety_factors: tension_fos: null shear_fos: null unknown_because: >- The catalogue publishes ULTIMATE pull-out and shear and states no safety factor anywhere -- no heading, no footnote, no test-report note. A working load cannot be derived from these without one, and the 4:1 the disk anchors above publish belongs to a different manufacturer's parts and may not be borrowed. A check that needs a working load stands down naming this key. capacity_is_ultimate: true # The concrete these were tested in is not stated either, so there is no # `basis_fc_psi` to scale from. Absent, not assumed. basis_fc_psi: null basis_fc_unknown_because: >- The catalogue states no concrete strength for the published capacities. Nothing here scales with sqrt(f'c) because there is no basis to scale from. # -> rationale.md § catalogue.slotted_inserts.edge_distance edge_distance: along_slot_axis_in: 6 across_slot_axis_in: 10 basis: without additional reinforcing steel reducible_with_extra_reinforcement: true reduction_source: 'PSA Test Report #2' reduced_value_in: null reduced_value_unknown_because: >- The page says the edge distance CAN be reduced with extra reinforcement and cites Test Report #2 for how far. The report is not in hand, so there is no reduced number -- only the fact that a reduction exists and where it is published. note: >- 6 in. is measured along the slot's long axis and 10 in. perpendicular to it. Both figures on the page show the same invariant with the insert turned 90 degrees. NOT a standard/ reinforced pair -- both are the unreinforced minimum. # -> rationale.md § catalogue.slotted_inserts.unreinforced_top_edge_reduction unreinforced_top_edge_reduction: fraction: 0.25 applies_to: ultimate_load condition: unreinforced insert placed at the top edge of the panel removed_by: added reinforcement -- restores full pull-out capacity source: 'PSA Test Report #2' not_a_general_unreinforced_penalty: true # ---- the recommended edge reinforcement, as prose ---------------- # # The page draws it rather than tabulating it: stepped hairpins near the # edge, 3 1/2" from the edge, 3/4" minimum cover all round, 2 - #3 # stepped hairpins in the suggested corner detail, described as being IN # ADDITION TO the panel's own reinforcement. Carried as text because it # is a detail, not a rating. edge_reinforcement: >- Stepped hairpins near the edge -- the suggested corner detail draws 2 - #3 stepped hairpins at 3 1/2 in. from the edge with 3/4 in. minimum cover, explicitly in addition to the panel's own reinforcement. Drawn, not tabulated; no capacity is attached to it. # ---- finishes ---------------------------------------------------- # # Three, and they are three different material specifications rather # than three coatings on one body -- a stainless insert has a stainless # body and stainless nuts, not a coated carbon body. Material specs are # carried per finish for that reason. finishes: j_finish: what_it_is: >- A patented three-step coating -- zinc plate to ASTM B633 Type II at 0.0005 in. thickness, trivalent clear chromate (RoHS and ELV compliant), then a seal coat. It reaches the threads of a strap anchor, which is the area a dipped coating leaves most exposed. plating_spec: 'ASTM B633, Type II' plating_thickness_in: 0.0005 corrosion_test: 'ASTM B 117' hours_to_red_rust: 500 hours_to_white_rust: 96 insert_body: 'Hot Rolled, P & O, HSLA SAE J2340 490XLF' insert_square_nuts: '3/4 in. dia. NC thread, SAE J995 GR-5' insert_studs: 'AISI 1016 or 1018 (round head)' strap_body: 'ASTM A36/A529 GR 50, CSA G40.21 44W' strap_jam_nuts: '3/4 in. dia. 10 UNC-2, SAE GR 5 hex, SAE J995' strap_thread_stud: '3/4 in. dia. 10 UNC-2, AISI 1038 CRS' hot_dipped_galvanized: note: >- Shares the J-finish material specification block on the page -- the same heading covers "J Finish & Hot Dipped Galvanized" for both inserts and threaded strap anchors. No separate corrosion figures are published for it. corrosion_hours: null corrosion_hours_unknown_because: >- The 500 h / 96 h ASTM B 117 figures are printed under the J-finish heading only. Nothing publishes an equivalent for the galvanized option and it is not inherited. stainless_steel: insert_body: 'AISI 304 stainless steel' insert_square_nuts: '3/4 in. dia. NC thread, AISI 304, ASTM A194 GR 8' insert_studs: 'AISI 430 stainless steel' strap_body: 'AISI 304 stainless steel' strap_jam_nuts: 'AISI 304 stainless steel, ASTM A194 GR 8' strap_thread_stud: '3/4 in. dia. 10 UNC-2, AISI 304 rolled formed' plastic_closers: non-structural plastic, all finishes welding: 'robot welding with minimum penetration of 0.8 mm (threaded strap anchors)' # -> rationale.md § catalogue.slotted_inserts.models models: '4525': series: '4500' ultimate_pull_out_lb: 13400 ultimate_shear_lb: 20000 _stated_as: {pull_out: '>13,400 lbs.', shear: '>20,000 lbs.'} available_adjustment_in: 2.875 depth_no_stud_in: 2.5 stud_length_in: null stud_length_note: the shallowest model has no stud; the M dimension is blank on the page weight_lb: 1.5 weight_kgf: 0.680 '4535': series: '4500' ultimate_pull_out_lb: 18800 ultimate_shear_lb: 20000 _stated_as: {pull_out: '>18,800 lbs.', shear: '>20,000 lbs.'} available_adjustment_in: 2.875 depth_no_stud_in: 3.5 stud_length_in: 2.25 weight_lb: 1.9 weight_kgf: 0.862 '4545': series: '4500' ultimate_pull_out_lb: 21500 ultimate_shear_lb: 20000 _stated_as: {pull_out: '>21,500 lbs.', shear: '>20,000 lbs.'} available_adjustment_in: 2.125 depth_no_stud_in: 4.5 stud_length_in: 3.25 weight_lb: 2.3 weight_kgf: 1.043 '6025': series: '6000' ultimate_pull_out_lb: 13400 ultimate_shear_lb: 20000 _stated_as: {pull_out: '>13,400 lbs.', shear: '>20,000 lbs.'} available_adjustment_in: 4.375 depth_no_stud_in: 2.5 stud_length_in: null stud_length_note: as 4525 -- no stud, M dimension blank weight_lb: 2.0 weight_kgf: 0.907 '6035': series: '6000' ultimate_pull_out_lb: 18800 ultimate_shear_lb: 20000 _stated_as: {pull_out: '>18,800 lbs.', shear: '>20,000 lbs.'} available_adjustment_in: 4.375 depth_no_stud_in: 3.5 stud_length_in: 2.25 weight_lb: 2.3 weight_kgf: 1.043 '6045': series: '6000' ultimate_pull_out_lb: 21500 ultimate_shear_lb: 20000 _stated_as: {pull_out: '>21,500 lbs.', shear: '>20,000 lbs.'} available_adjustment_in: 3.625 depth_no_stud_in: 4.5 stud_length_in: 3.25 weight_lb: 2.5 weight_kgf: 1.134 # ---- the dimensions the two series share ------------------------ # # Everything on the dimensional sketch that is NOT per model. Split out # rather than repeated on six entries, because a value repeated six # times is a value that goes out of step five times. series_dimensions: '4500': length_overall_in: 5.75 length_excluding_plastic_tab_in: 4.5 width_overall_in: 4.125 width_at_base_in: 2.0 centre_to_centre_same_side_in: 1.5 centre_to_centre_across_insert_in: 2.75 top_of_insert_to_wing_in: 1.25 hole_diameter_in: 0.5625 stud_head_diameter_in: 0.875 '6000': length_overall_in: 7.25 length_excluding_plastic_tab_in: 6.0 width_overall_in: 4.125 width_at_base_in: 2.0 centre_to_centre_same_side_in: 2.5 centre_to_centre_across_insert_in: 2.75 top_of_insert_to_wing_in: 1.25 hole_diameter_in: 0.5625 stud_head_diameter_in: 0.875 # -> rationale.md § catalogue.slotted_inserts.strap_anchors strap_anchors: tension_capacity_lb: 30000 tension_stated_as: 'Tension capacity exceeds 30 KIPS' widths_in: [2] widths_note: >- The published shear table is for the 2 in. width, which the page calls the highest-shear option ("Our standard 2" wide welded strap anchor provides the Highest shear capacity"). Other widths exist -- the page says a range of strap anchors gives a range of shear capacities -- and none of them is published here. thickness_in: 0.375 lengths_in: [6, 8, 10, 12] # The four catalogue numbers, and they read as the length: 675 is the # 6 in. strap, 875 the 8 in., and so on. The 75 is the 3/4 in. thread. models: '675': {length_in: 6, weight_lb: 1.2, weight_kgf: 0.544} '875': {length_in: 8, weight_lb: 1.6, weight_kgf: 0.726} '1075': {length_in: 10, weight_lb: 2.0, weight_kgf: 0.907} '1275': {length_in: 12, weight_lb: 2.4, weight_kgf: 1.089} # Average tested shear against eccentricity, 2 in. width. The page # heads the column "KIPS - Avg", so these are means and not the # guaranteed minimums the insert capacities are. # Keys are STRINGS ('1.0', not 1.0) for the same reason as # catalogue.inserts.QL589SPG.by_panel_thickness_in -- a bare float key # here mixes with the string keys `_units` / `_basis` in the same # mapping and breaks `json.dumps(..., sort_keys=True)` on the whole # document with `TypeError: '<' not supported between instances of # 'float' and 'str'`. Found and fixed 2026-09-01 alongside that table. shear_by_eccentricity_2in_width: _units: kips _basis: average '1.0': 20.2 '1.5': 17.9 '2.0': 14.2 '2.5': 13.2 '3.0': 11.3 # Two erection facts with no numeric home except one clearance. threaded_vs_notched: >- A threaded strap rotates to any angle to take out tolerance without losing pull-out capacity; a notched strap installs at 90 degrees to the insert and concentrates the shear over its 3/8 in. notch width, where the threaded strap spreads it across the insert lips. notched_strap_rotation_clearance_in: 2 notched_strap_erection_caution: >- A notched strap anchor must be placed after the panel is set and needs at least 2 in. of clearance to rotate to 90 degrees. If the insert sits too low, tipping the panel away from the frame to clear the insert opening puts the installer's hands in the gap. # ---- where the numbers come from, and what is not published ----- test_reports: published_by: 'JVI (www.jvi-inc.com)' list: '1': pull-out capacity '2': pull-out capacity near edge '3': shear capacity '4': pull-out capacity, sandwich panels '5': pull-out capacity, end of insert '6': corrosion resistance '7': 30 KIP load capacity note: >- The catalogue cites these seven for its capacities and its edge rules but reproduces none of them. Report #2 is the one this file needs and does not have -- it is what would give a reduced edge distance a number. # -> rationale.md § catalogue.shear_grid shear_grid: c_grid: manufacturer: Altus product: C-GRID what_it_is: >- A carbon-fibre grid cast across the insulation of a sandwich wall panel to transfer horizontal shear between the wythes. Capacity is a shear flow -- pounds per inch of grid -- because the grid is continuous, not a discrete part. source: >- Altus C-GRID Table 4.1, "Test Data and Nominal Strength Values", reproduced in a 2022 C-GRID insulated-wall-panel design deck. Basis stated on the same page: "To date Altus has performed over 200 double shear tests to arrive at values listed below." test_basis: over 200 double shear tests test_method: double shear on rectangular specimens, VQ/I source_year: 2022 read_on: '2026-08-31' recommended_value_rule: >- recommended = average of (average test value) and (maximum minus 3 standard deviations), rounded half up. Verified to hold on all 37 published rows -- which is what checks this transcription. maximum_rule: >- The published maximum is 1.5 times the average, based on VQ/I shear on the rectangular test specimens -- it is derived, not a largest-observed. values_are_nominal: true values_are_nominal_note: >- Nominal, unfactored. The phi a designer applies depends on the check and on the insulation and is design practice, not product data -- it belongs in a plant layer, never here. single_test_per_spacing_types: [POLY ISO] single_test_caveat: >- The page footnotes POLY ISO with "Single test for each spacing. Standard deviation is across tests" -- i.e. that family's spread is borrowed across spacings rather than measured within one. Its three rows are weaker evidence than the rest of the table and the page says so. # A note printed above the table, for a condition the table does not # cover: "Design value of C-GRID with debonded insulation is # approximately 100 pli." debonded_insulation_pli: 100 debonded_insulation_note: >- Stated as "approximately", for debonded insulation, outside the keyed table. It is not an entry in it and must not be read as one. # -> rationale.md § catalogue.shear_grid.c_grid.shear_flow shear_flow: _units: pli _columns: [average_test, max_minus_3sd, recommended_nominal] EPS: 2: {12: [280, 381, 331], 24: [288, 414, 351], 48: [512, 711, 612]} 4: {12: [163, 223.5, 193], 24: [243, 334.5, 289], 48: [425, 496.5, 461]} 6: {12: [186, 258, 222], 24: [213, 286.5, 250], 48: [314, 360, 337]} XPS: 2: {12: [212, 178, 195], 18: [180, 207, 194], 24: [160, 152, 156], 36: [142, 202, 172]} 4: {12: [176, 221, 199], 18: [198, 203, 201], 24: [161, 128, 145], 36: [163, 155, 159]} XPS_sand_blasted: 2: {12: [290, 325, 308], 24: [540, 658, 599], 48: [586, 469, 528]} 4: {12: [314, 462, 388], 24: [444, 607, 526], 48: [593, 712, 653]} XPS_machined: 3: {12: [243, 247, 245]} POLY_ISO: 4: {12: [205, 289.5, 247], 24: [224, 276, 250], 48: [102, 105, 104]} Kooltherm: 2: {12: [222, 234, 228], 24: [211, 277, 244], 48: [269, 144, 207]} 4: {12: [175, 223.5, 199], 24: [212, 270, 241], 48: [219, 259.5, 239]} NeoPor_low_density: 2: {24: [438, 513, 476]} 6: {24: [323, 370.5, 347]} NeoPor_high_density: 2: {24: [534, 735, 635]} 4: {24: [533, 750, 642]} # ---- what this table does NOT publish -------------------------- # # STIFFNESS. A partial-composite calculation needs a shear stiffness # as well as a strength, and Table 4.1 is strengths only. Nothing on # the page carries a k/in. A module that assumes a stiffness because # it has a strength is inventing the half that governs deflection. stiffness_k_per_in: null stiffness_unknown_because: >- Table 4.1 publishes shear flow STRENGTH only. No stiffness value appears on the page in any form, so a partial-composite calculation that needs one has to get it elsewhere and may not derive it from these. # FAILURE MODE. 200+ tests, and the page classifies none of them. failure_modes: null failure_modes_unknown_because: >- The page states the test count and the derived values and never classifies the failures. Whether a row is governed by the grid, the bond or the insulation is not published. # sources: 2026-09-07 # -> PCI Design Handbook 9th ed. (MNL-120-25), Section 5.8 "Loss of # Prestress", pdf viewer p293-296, printed p5-42 to 5-45. Read via the # Apryse WebViewer text layer, with Tables 5.8.1/5.8.2 and Eq. 5-105/ # 5-106 SCREEN-VERIFIED 2026-09-07 at 93% zoom (Eq. 5-105/5-106 also # cross-checked numerically against Table 5.8.2 at three points, agree # within rounding). Full extraction: # knowledge/library/notes/PCI9-5.8-losses.md. Method: ACI-ASCE Committee # 423 task group report (Zia et al. 1979), reprinted PCI Design Handbook # 9th ed. Section 5.8.3 -- this is what will/REVIT #24 needs: the engine # today assumes a lump-sum loss percentage, and the owner ruled "不要 # assume prestress loss 了,要算的,根据 PCI 算". Applies to pretensioned # components; the handbook itself notes more detailed analysis may be # warranted for unusual situations. # -> rationale.md § catalogue.prestress_losses prestress_losses: method: 'PCI/Zia 1979 (ACI-ASCE Committee 423 task group report), PCI Design Handbook 9th ed. Section 5.8.3' source: 'PCI Design Handbook 9th ed. (MNL-120-25) Section 5.8.3, p294-295 / printed 5-43 to 5-44' applicability: pretensioned components total_loss: formula: 'TL = ES + CR + SH + RE' source: 'Eq. 5-98, p294 / 5-43' range_normalweight_psi: {min: 25000, max: 50000} range_sand_lightweight_psi: {min: 30000, max: 55000} load_table_lower_limit_psi: 30000 note: >- "Total loss of prestress in typical components will range from about 25,000 to 50,000 psi for normalweight concrete components, and from about 30,000 to 55,000 psi for sand-lightweight components... The load tables in Chapter 3 have a lower limit on loss of 30,000 psi." Section 5.8.2, p294 / 5-43. elastic_shortening: formula: 'ES = Kes * Eps * fcir / Eci' kes_pretensioned: 1.0 eps_psi: 28500000 eps_note: "about 28.5 x 10^6 psi -- Eps is a stated APPROXIMATION on this page, not itself a strand-specific modulus" fcir_formula: 'fcir = Kcir*(Pi/Ag + Pi*e^2/Ig) - Mg*e/Ig' kcir_pretensioned: 0.9 source: 'Eq. 5-99/5-100, p294-295 / 5-43 to 5-44' creep: formula: 'CR = Kcr * (Eps/Ec) * (fcir - fcds)' kcr_normalweight: 2.0 kcr_sand_lightweight: 1.6 fcds_formula: 'fcds = Msd*e/Ig' source: 'Eq. 5-101/5-102, p295 / 5-44. SCREEN-VERIFIED 2026-09-07.' composite_note: >- For composite components, if some dead load is applied to the composite section, fcds is calculated in two operations -- one for the noncomposite condition, one for the composite condition. shrinkage: formula: 'SH = 8.2e-6 * Ksh * Eps * (1 - 0.06*VS) * (100 - RH)' ksh_pretensioned: 1.0 vs_definition: 'volume-to-surface ratio' rh_definition: 'average ambient relative humidity, see PCI Design Aid 4-12' source: 'Eq. 5-103, p295 / 5-44' relaxation: formula: 'RE = (Kre - J*(SH + CR + ES)) * C' source: 'Eq. 5-104, p295 / 5-44' kre_j_by_steel_type: # Table 5.8.1, SCREEN-VERIFIED 2026-09-07 at 93% zoom against # p295 / printed 5-44. grade_270_low_relaxation_strand: {kre: 5000, j: 0.040} grade_250_low_relaxation_wire: {kre: 4630, j: 0.037} grade_240_or_235_low_relaxation_wire: {kre: 4400, j: 0.035} grade_145_or_160_stress_relieved_bar: {kre: 6000, j: 0.050} c_coefficient: # Table 5.8.2 and its closed-form Eq. 5-105/5-106, both # SCREEN-VERIFIED 2026-09-07 and cross-checked numerically # against each other (three spot checks, agree within rounding -- # see knowledge/library/notes/PCI9-5.8-losses.md). # STRING keys -- see catalogue.slotted_inserts.strap_anchors for # why a bare float key here would break json.dumps(sort_keys=True). table_by_fpi_over_fpu_ge_054: # stress-relieved bar or low-relaxation strand/wire '0.80': 1.28 '0.79': 1.22 '0.78': 1.16 '0.77': 1.11 '0.76': 1.05 '0.75': 1.00 '0.74': 0.95 '0.73': 0.90 '0.72': 0.85 '0.71': 0.80 '0.70': 0.75 '0.69': 0.70 '0.68': 0.66 '0.67': 0.61 '0.66': 0.57 '0.65': 0.53 '0.64': 0.49 '0.63': 0.45 '0.62': 0.41 '0.61': 0.37 '0.60': 0.33 formula_ge_054: 'C = (fpi_over_fpu / 0.21) * (fpi_over_fpu / 0.9 - 0.55)' formula_ge_054_condition: 'low-relaxation strand, fpi/fpu >= 0.54, Eq. 5-105' formula_le_054: 'C = fpi_over_fpu / 4.25' formula_le_054_condition: 'low-relaxation strand, fpi/fpu <= 0.54, Eq. 5-106' fpi_definition: 'fpi = Pi / Aps' elastic_gain: formula: 'fEG = (Eps/Ec) * fcgp' source: 'Eq. 5-107, p296 / 5-45' note: 'decrease in prestress loss from a transient superimposed load; in effect only while the transient load is active' critical_locations: note: >- ES and CR losses are computed at the point of maximum flexural tensile stress -- midspan for straight/parabolic/approximate- parabolic strand, near the 0.4l point for strand deflected at midspan only. Only the critical-point stresses need be considered for bonded strand, unless a critical section falls within the development length (Section 5.2.3). source: 'Section 5.8.4, p295-296 / 5-44 to 5-45' # sources: 2026-09-07 # -> PCI Design Handbook 9th ed., Section 15.2 "Material Properties -- # Prestressing Steel", Design Aids 15-9/15-10/15-11/15-12, pdf viewer # p862-863, printed p15-31/15-32. SCREEN-VERIFIED 2026-09-07 at 93% # zoom -- full-page render matches the text-layer transcription # exactly. Full extraction: # knowledge/library/notes/PCI9-ch15-reference.md. # -> rationale.md § catalogue.strand strand: astm_a416_properties: source: 'ASTM A416/A416M-18 via PCI Design Handbook 9th ed. Design Aid 15-9, p862 / 15-31' grade_270: fpu_ksi: 270 sizes: '3/8': {diameter_in: 0.375, area_in2: 0.085, weight_lb_ft: 0.29} '7/16': {diameter_in: 0.438, area_in2: 0.115, weight_lb_ft: 0.39} '1/2': {diameter_in: 0.500, area_in2: 0.153, weight_lb_ft: 0.52} '1/2 special': {diameter_in: 0.520, area_in2: 0.167, weight_lb_ft: 0.57} '9/16': {diameter_in: 0.563, area_in2: 0.192, weight_lb_ft: 0.65} '0.6': {diameter_in: 0.600, area_in2: 0.217, weight_lb_ft: 0.74} '0.62': {diameter_in: 0.620, area_in2: 0.231, weight_lb_ft: 0.78} '0.7': {diameter_in: 0.700, area_in2: 0.294, weight_lb_ft: 1.00} grade_250: fpu_ksi: 250 sizes: '1/4': {diameter_in: 0.250, area_in2: 0.036, weight_lb_ft: 0.12} '5/16': {diameter_in: 0.313, area_in2: 0.058, weight_lb_ft: 0.20} '3/8': {diameter_in: 0.375, area_in2: 0.080, weight_lb_ft: 0.27} '7/16': {diameter_in: 0.438, area_in2: 0.108, weight_lb_ft: 0.37} '1/2': {diameter_in: 0.500, area_in2: 0.144, weight_lb_ft: 0.49} '0.6': {diameter_in: 0.600, area_in2: 0.216, weight_lb_ft: 0.74} astm_a421_wire_properties: source: 'ASTM A421/A421M-21 via PCI Design Handbook 9th ed. Design Aid 15-10, p862 / 15-31' entries: - {diameter_in: 0.192, area_in2: 0.0289, weight_lb_ft: 0.1, fpu_ksi: 250} - {diameter_in: 0.196, area_in2: 0.0302, weight_lb_ft: 0.104, fpu_ksi: 250} - {diameter_in: 0.25, area_in2: 0.0491, weight_lb_ft: 0.169, fpu_ksi: 240} - {diameter_in: 0.276, area_in2: 0.0598, weight_lb_ft: 0.206, fpu_ksi: 235} astm_a722_bar_properties: source: 'ASTM A722/A722M-18 via PCI Design Handbook 9th ed. Design Aid 15-11, p862 / 15-31' type_1_plain: fpu_ksi: 150 fpy_fraction_of_fpu: 0.85 sizes: '3/4': {area_in2: 0.44, weight_lb_ft: 1.50} '7/8': {area_in2: 0.60, weight_lb_ft: 2.04} '1': {area_in2: 0.78, weight_lb_ft: 2.67} '1-1/8': {area_in2: 0.99, weight_lb_ft: 3.38} '1-1/4': {area_in2: 1.23, weight_lb_ft: 4.17} '1-3/8': {area_in2: 1.48, weight_lb_ft: 5.05} type_2_deformed: fpu_ksi: 150 fpy_fraction_of_fpu: 0.80 sizes: '5/8': {area_in2: 0.28, weight_lb_ft: 0.98} '3/4': {area_in2: 0.42, weight_lb_ft: 1.49} '1': {area_in2: 0.85, weight_lb_ft: 3.01} '1-1/4': {area_in2: 1.25, weight_lb_ft: 4.39} '1-3/8': {area_in2: 1.58, weight_lb_ft: 5.56} '1-3/4': {area_in2: 2.58, weight_lb_ft: 9.10} '2-1/2': {area_in2: 5.16, weight_lb_ft: 18.20} '3': {area_in2: 6.85, weight_lb_ft: 24.09} design_stress_strain: source: 'PCI Design Handbook 9th ed. Design Aid 15-12, p863 / 15-32. SCREEN-VERIFIED 2026-09-07.' astm_a416_min_yield_at_1pct_elongation_ksi: {grade_270: 243, grade_250: 225} approx_strain_at_rupture_in_per_in: {min: 0.05, max: 0.07} min_elongation_at_ultimate_pct: 3.5 formula_250ksi_below_0_0076: 'fps = 28500 * eps' formula_250ksi_above_0_0076: 'fps = 250 - 0.04/(eps - 0.0064)' formula_270ksi_below_0_0085: 'fps = 28500 * eps' formula_270ksi_above_0_0085: 'fps = 270 - 0.04/(eps - 0.007)' # sources: 2026-09-07 # -> PCI Design Handbook 9th ed., Section 5.9 "Camber and Deflection" # and Design Aids 5-1/5-2, pdf viewer p293-300 (Section 5.9) and # p448-449 (Design Aids), printed p5-45 to 5-49 and 5-197/5-198. # Full extraction: knowledge/library/notes/PCI9-5.8-losses.md (§5.9 is # in the same note as §5.8, both pulled from the same page range) and # knowledge/library/notes/PCI9-5.17-design-aids.md. # -> rationale.md § catalogue.camber_and_bowing camber_and_bowing: time_dependent_multipliers: source: >- Table 5.9.2, p297 / 5-46. SCREEN-VERIFIED 2026-09-07. Derivation: Martin (1977). "Note that there is no corresponding multiplier for prestressed concrete in ACI 318" -- these are PCI-original, not an ACI 318 table. at_erection: deflection_downward_self_weight: {without_topping: 1.85, with_topping: 1.85} camber_upward_prestress: {without_topping: 1.80, with_topping: 1.80} final: deflection_downward_self_weight: {without_topping: 2.70, with_topping: 2.40} camber_upward_prestress: {without_topping: 2.45, with_topping: 2.20} deflection_downward_superimposed_dead_load: {without_topping: 3.00, with_topping: 3.00} deflection_downward_composite_topping: {without_topping: null, with_topping: 2.30} nonprestressed_reinforcement_adjustment_formula: null nonprestressed_reinforcement_adjustment_unknown_because: >- Eq. 5-114 (Shaikh and Branson 1970, Martin 1977, p299 / 5-48) is C2 as a function of C1 and Asd/Aps, but the text-layer extraction garbled the fraction nesting and it was not screen-verified -- confidence C, not landed as a formula. Question for the owner / next landing pass: re-render p299 and confirm before coding this. camber_equations: source: 'Design Aid 5-1, p448 / 5-197. SCREEN-VERIFIED 2026-09-07.' straight_strands: 'Delta = Po*e*l^2 / (8*E*I)' single_point_depressed: 'Delta = Po*ee*l^2/(8*E*I) + Po*e_end*l^2/(12*E*I)' two_point_depressed: 'Delta = Po*ee*l^2/(8*E*I) + (Po*e_end/(E*I)) * (l^2/8 - a^2/6)' variables: 'e/ee = strand eccentricity at midspan; e_end = eccentricity change from end to depression point; a = distance from each end to its depression point (two-point case)' bowing: formula: 'Delta = alpha * l^2 / (8*h)' alpha_formula: 'alpha = C_thermal * (T1 - T2)' source: 'Eq. 5-115/5-116, p299 / 5-48' seasonal_temp_diff_open_structures_f: {min: 30, max: 40} seasonal_note: >- Seldom exceeds 30-40 F in open structures such as parking-deck roofs; can be higher in an insulated wall panel, tempered by concrete thermal lag. restraint_forces_and_moments: source: 'Design Aid 5-2, p449 / 5-198. SCREEN-VERIFIED 2026-09-07.' effective_modulus_et: short_term_fraction_of_ec: 0.75 note: >- Consistent with the thermal-displacement reduction for equivalent volume change, Section 4.4.3.1. Earlier editions of this handbook used 0.5*Ec for long-term seasonal cases -- the 9th edition gives no current numeric value for that case. intermediate_restraint: case_1_single_midspan: {formula_p: 'P = 48*Et*I*Delta/l^3', formula_moment: 'M = P*l/4'} case_2_two_points: {formula_p: 'P = 24*Et*I*Delta/(3*a*l^2 - 4*a^3)', formula_moment: 'M = P*a'} case_3_three_or_more: {formula_sum_p: 'sum(P) = w*l = 77*Et*I*Delta/l^3', formula_moment: 'M = w*l^2/8 = sum(P)*(l/8)'} end_restraint: case_4_both_ends: {formula_moment: 'M = 8*Et*I*Delta/l^2'} case_5_one_end: {formula_moment: 'M = 16*Et*I*Delta/l^2'} # sources: 2026-09-07 # -> PCI Design Handbook 9th ed., Section 5.12 "Insulated Wall Panels", # pdf viewer p304-309, printed p5-53 to 5-58. Full extraction: # knowledge/library/notes/PCI9-5.12-insulated.md. # -> rationale.md § catalogue.insulated_panel insulated_panel: insulation_properties: source: 'Table 5.12.1, p305 / 5-54. SCREEN-VERIFIED 2026-09-07.' note: >- Only the maximum-use-temperature and the moisture caution are landed here as the numbers a check might reasonably gate on; the full per-density property matrix (density, water absorption, compressive/tensile/shear/flexural strength, thermal conductivity) is in the note file, not landed -- see the debt list in the ledger. max_use_temperature_f: eps: 165 xps: 165 polyisocyanurate_unfaced: 250 polyisocyanurate_faced: 250 phenolic: 300 cellular_glass: 900 unfaced_felt_faced_polyiso_moisture_caution: >- "Unfaced and felt-faced polyisocyanurates should not be used in insulated wall panels because moisture absorption causes them to expand." (footnote a, Table 5.12.1) structural_design_assumptions: source: 'Section 5.12.2, p305-306 / 5-54 to 5-55' noncomposite: 'wythes act independently for the full life of the panel; flexible wythe connectors offer insignificant shrinkage/temperature restraint' composite: 'wythes act as a fully composite unit for the full life; needs shear-resisting connectors, welded-wire trusses, or solid concrete zones -- thermal bowing tends to be GREATER than noncomposite; solid zones significantly compromise thermal efficiency' partially_composite: 'between noncomposite and composite; may differ by check (deflection vs cracking vs ultimate strength)' semicomposite: 'composite during handling (early insulation bond + flexible connectors), noncomposite in service (bond assumed unreliable long-term); wind loads split by wythe stiffness; slenderness uses the SUM of individual moments of inertia (Example 5-16)' wythe_and_strand_limits: source: 'Section 5.12.2 / 5.12.3 / 5.12.4, p306/p308, printed 5-55/5-57' min_wythe_thickness_rule: 'no thinner than 3x the maximum aggregate size (some manufacturers use a 2-in. wythe with 3/4-in. aggregate)' max_strand_diameter_by_wythe_thickness: '3/8_in_ok_wythe_2_in': true '1/2_in_needs_wythe_3_in_min': true note: 'same underlying rule as catalogue.handling.stripping.max_strand_diameter_by_slab_thickness (Table 8.3.2), stated independently in two chapters -- both agree' wythe_connector_spacing_in_oc: 24 wythe_connector_wire_gauge: '12 to 14 gauge, galvanized or stainless steel' connection_center_third_of_span: true camber_and_bowing_notes: source: 'Section 5.12.5, p308-309 / 5-57 to 5-58' thermal_bowing_greater_in_composite: true bowing_usually_outward: true # sources: 2026-09-07 # -> PCI Design Handbook 9th ed., Chapter 8 "Component Handling and # Erection Bracing", pdf viewer p620-642, printed p8-3 to 8-23. Full # extraction: knowledge/library/notes/PCI9-ch8-handling.md. # -> rationale.md § catalogue.handling handling: stripping: equivalent_static_load_multipliers: source: 'Table 8.3.1, p629 / 8-10. SCREEN-VERIFIED 2026-09-07.' note: 'For MEMBER FLEXURAL DESIGN ONLY -- NOT applied to lifting-device safety factors (which use the 4x-load factor below instead).' exposed_aggregate_with_retarder: flat_no_false_joints: 1.2 flat_false_joints_or_reveals: 1.3 fluted_proper_draft: 1.4 sculptured_other: 1.5 yard_handling_and_erection: 1.2 transportation: 1.5 smooth_form_release_agent_only: flat_no_false_joints: 1.3 flat_false_joints_or_reveals: 1.4 fluted_proper_draft: 1.6 sculptured_other: 1.7 yard_handling_and_erection: 1.2 transportation: 1.5 allowable_tensile_stress: formula: "ft_allowable = 7.5*sqrt(fc) / 1.5 = 5*sqrt(fc)" factor_of_safety: 1.5 source: "Eq. 8-1, p629 / 8-10. Substitute f'ci for f'c during stripping." prestressing_benefit_limits: recommended_tensile_stress_limit: "5*sqrt(fci)" recommended_avg_compression_after_losses_nonflexural_psi: {min: 125, max: 800} source: 'Section 8.3.4.4, p630 / 8-11' max_strand_diameter_by_slab_thickness: source: 'Table 8.3.2, p630 / 8-11' less_than_3_in: '3/8' '3_in_or_more': '1/2 or larger' post_tensioning: transverse_reinforcement_exemption_max_width_ft: 12 monostrand_friction_coefficient: {min: 0.03, max: 0.05} source: 'Section 8.3.4.4, p630 / 8-11 (ACI CODE-318-19 Section 24.4.3.5; Tanner 1977)' moment_coefficients_edge_rotation: source: 'Figure 8.3.1, p625 / 8-6' two_point: {mx: 'w*a^2/8', my: '0.0107*w*a*b^2'} four_point: {mx: 'w*a^2/8', my: '0.0027*w*a*b^2'} moment_coefficients_flat_stripping: source: 'Figure 8.3.2, p626 / 8-7' two_point: {mx: '0.0107*w*a^2*b', my: '0.0214*w*a*b^2', mx_resisting_width: 'lesser of 15t or b/2'} four_point: {mx: '0.0054*w*a^2*b', my: '0.0054*w*a*b^2', mx_resisting_width: 'lesser of 15t or b/4'} rigging: sling_angle_multiplier: source: 'Figure 8.3.3, p627 / 8-8. Consistent with F = 1/sin(theta) to printed precision.' '90_deg': 1.00 '75_deg': 1.04 '60_deg': 1.16 min_recommended_sling_angle_deg: 60 source_angle: 'Figures 8.3.4 / 8.3.11 / 8.6.2, p627 / p633 / p639' lifting_devices: aircraft_cable_wire_rope: source: 'Table 8.3.3, p631 / 8-12. SCREEN-VERIFIED 2026-09-07. A safety factor of 4 (OSHA 1926.704) is already applied to these safe-load values.' '3/8_in_aircraft_cable_safe_load_kip': 3.6 '7/16_in_wire_rope_safe_load_kip': 4.4 '1/2_in_wire_rope_safe_load_kip': 5.7 min_embedment_per_leg_in: 48 do_not_use_multiple_loops_single_location: true strand_loops: source: 'Section 8.3.5.2, p631 / 8-12' safety_factor: 4 min_embedment_per_leg_in: 24 min_hook_diameter_x_strand_diameter: 4 min_bend_in: 2 default_safe_load_half_in_270ksi_single_loop_kip: 10 multi_strand_multiplier: {double: 1.7, triple: 2.2} self_consolidating_concrete_caution: >- Vertically inserted loops show lower pull-out strength (air-bubble attraction around the strand); PCI TR-6-15 indicates longer embedment may be needed. storage: warpage_deflection: formula: 'ymax = 22.5*w*sin(theta)*(l^4 + a^4) / (t^3 * Eci)' time_dependent_formula: 'yt = ymax * (1 + lambda_delta)' source: 'Eq. 8-2/8-3, p634-635 / 8-15 to 8-16' support_at_0_2l_reduces_warpage_to_pct_of_end_support: 10 dimension_a_effect_pct_of_l_effect: 1 source: >- p635 / 8-16 -- "most precast concrete producers will store panels with support points at about 0.2l from each end, which will reduce the warpage to approximately 10% of that of supports at the ends" and "the effect of the dimension a is typically about 1% of that of l and can be ignored for usual storage conditions." transportation: common_payload_no_permit_tons: 24 standard_dimension_limits_ft: {vertical_horizontal: 8, length: 40} lowboy_height_ft: {min: 10, max: 12} total_height_no_permit: '13 ft 6 in (some states)' max_width_with_permit_ft: {min: 10, max: 14} length_permit_thresholds: 'varies by state -- some allow a simple permit over 70 ft, others require a special permit for any load over 55 ft' weight_permit_thresholds: 'varies by state -- some allow up to 100 tons with a permit, others restrict severely above 25 tons' source: 'Section 8.5, p635-636 / 8-16 to 8-17' erection_stability: safety_factors: source: 'Table 8.7.1, p641 / 8-22. SCREEN-VERIFIED 2026-09-07.' bracing_inserts_cast_in: 2 lifting_inserts: 4 lifting_hardware: 5 rotation_pick_coefficients: source: 'Figure 8.6.1, p638 / 8-19. SCREEN-VERIFIED 2026-09-07.' two_point_general: reactions: ['w*l/(2a)', 'w*l*(1 - 1/(2a))'] neg_moment: '0.5*w*b^2*l^2' pos_moment: '(w/2)*l^2*(1 - 1/(2a))^2' constraint: 'a + b = 1.0' two_point_equal_moments: reactions_fraction: [0.70, 0.30] positions_fraction_of_l: [0.29, 0.71] neg_moment_coeff: 0.042 pos_moment_coeff: 0.045 two_point_four_point_stripping_locations: reactions_fraction: [0.63, 0.37] positions_fraction_of_l: [0.21, 0.79] neg_moment_coeff: 0.022 pos_moment_coeff: 0.069 three_point_eight_point_stripping_locations: reactions_fraction: [0.67, 0.33] positions_fraction_of_l: [0.1, 0.3, 0.6] positions_confidence: 'B -- read off the text layer, not screen-verified; the moment/reaction coefficients above ARE screen-verified' neg_moment_coeff: -0.005 pos_moment_coeff: 0.055 three_point_alternate: min_sling_length_x_l: 1.0 reactions_fraction: [0.77, 0.23] neg_moment_coeff: -0.005 pos_moment_1_coeff: 0.036 pos_moment_2_coeff: 0.026 caution: 'see the source note on uncontrolled rolling, Section 8.6 p637 / 8-18' asce_37_construction_loads: >- Not adopted by IBC 2021 or ACI CODE-318-19; using ASCE 37 meets the recognized standard of care absent contract-specific construction-loading requirements. Source: Section 8.7.6, p640 / 8-21. # sources: 2026-09-07 # -> ACI 318-19, Chapter 25 "Reinforcement Details", §25.4 Development of # reinforcement and §25.5 Splices. Read from the code's own text layer # (knowledge/ACI318-19-ch25_decoded.txt, cross-checked against the cleaned # extraction knowledge/ACI318-19-ch25.md). PUBLIC: this is the model code, # the same for every producer and every job -- it identifies nobody. # Cites below are the ACI PRINTED page (the number in the page footer); # the raw text layer's own convention is printed = PDF page - 2. # Full extraction and the page-level confidence marks: # knowledge/library/notes/ACI318-19-ch25-development.md # -> rationale.md § catalogue.development_length development_length: sqrt_fc_cap_psi: 100.0 # §25.4.1.4, p.471 -- use sqrt(100) when fc > 10,000 psi minimums_in: # The floor under every §25.4 calculated length -- "greater of (a)/(b)/(c)". tension_general_or_simplified: 12.0 # §25.4.2.1(b), p.471 hooked_bar_db: 8.0 # §25.4.3.1(b), p.474 hooked_bar_in: 6.0 # §25.4.3.1(c), p.474 headed_bar_db: 8.0 # §25.4.4.2(b), p.477 headed_bar_in: 6.0 # §25.4.4.2(c), p.477 compression_in: 8.0 # §25.4.9.1(b), p.487 wwr_deformed_in: 8.0 # §25.4.6.1(b), p.483 wwr_plain_in: 6.0 # §25.4.7.1(b), p.484 high_grade_transverse_requirement: # fy >= 80,000 psi bars spaced closer than 6 in. o.c. need transverse # reinforcement sized so Ktr is never smaller than 0.5*db. Same rule # stated twice, once for development and once for lap splices. fy_threshold_psi: 80000 spacing_threshold_in: 6.0 ktr_min_factor_db: 0.5 source_development: 'ACI 318-19 §25.4.2.2, p.471' source_lap_splice: 'ACI 318-19 §25.5.1.5, p.488' tension: general: formula: 'ld = (3*fy*psi_t*psi_e*psi_s*psi_g / (40*lambda*sqrt(fc))) * (db / ((cb+Ktr)/db))' source: 'ACI 318-19 §25.4.2.4, Eq. 25.4.2.4a, p.472' confinement_term_max: 2.5 # (cb+Ktr)/db shall not exceed 2.5 source_confinement_max: 'ACI 318-19 §25.4.2.4, p.472' ktr_formula: 'Ktr = 40*Atr/(s*n)' source_ktr: 'ACI 318-19 §25.4.2.4b, p.473' ktr_zero_permitted: true # design simplification, even if transverse reinf. present cb_definition: 'lesser of: distance from bar/wire centre to nearest concrete surface; one-half the c-c spacing of the bars/wires' source_cb: 'ACI 318-19 §25.4.2.4, p.472' modification_factors: psi_t: {top_bar: 1.3, other: 1.0} # >12in fresh concrete below reinforcement psi_e: {epoxy_low_cover_or_spacing: 1.5, epoxy_other: 1.2, uncoated_or_galvanized: 1.0} psi_s: {no7_and_larger: 1.0, no6_and_smaller: 0.8} psi_g: {grade_40_or_60: 1.0, grade_80: 1.15, grade_100: 1.3} lambda: {lightweight: 0.75, normalweight: 1.0} psi_t_times_psi_e_max: 1.7 # footnote, p.473 source: 'ACI 318-19 Table 25.4.2.5, p.473-474' simplified: # Table 25.4.2.3. ld is based on two preselected (cb+Ktr)/db values # (1.5 for case1, 1.0 for case2) rather than the actual ratio -- see # R25.4.2.3, p.472. case1 = clear spacing/cover/ties meet the # favourable row; case2 = everything else. no6_and_smaller: case1: 'ld = (fy*psi_t*psi_e*psi_s*psi_g / (25*lambda*sqrt(fc))) * db' case2: 'ld = (3*fy*psi_t*psi_e*psi_s*psi_g / (50*lambda*sqrt(fc))) * db' no7_and_larger: case1: 'ld = (fy*psi_t*psi_e*psi_g / (20*lambda*sqrt(fc))) * db' case2: 'ld = (3*fy*psi_t*psi_e*psi_g / (40*lambda*sqrt(fc))) * db' source: 'ACI 318-19 Table 25.4.2.3, p.472' worked_example_check: >- R25.4.2.3's own worked example (fy=60,000 psi, lambda=1, uncoated, bottom bar, fc=4,000 psi) gives no7_and_larger.case1 = 47*db and case2 = 71*db off these same coefficients -- p.472. hooked_bar: formula: 'ldh = (fy*psi_e*psi_r*psi_o*psi_c / (50*lambda*sqrt(fc))) * db' source: 'ACI 318-19 §25.4.3.1(a), p.474' measured_from: 'critical section to the outside end (edge) of the hook' modification_factors: psi_e: {coated: 1.2, uncoated_or_galvanized: 1.0} psi_r: {no11_or_smaller_confined: 1.0, other: 1.6} # Ath >= 0.4*Ahs or s >= 6db psi_o: {inside_column_core_or_side_cover_ge_6db: 1.0, other: 1.25} psi_c: {fc_lt_6000: 'fc/15000 + 0.6', fc_ge_6000: 1.0} lambda: {lightweight: 0.75, normalweight: 1.0} source: 'ACI 318-19 Table 25.4.3.2, p.474-475' discontinuous_end_tie_spacing_max_db: 3 # both side + top/bottom cover < 2.5in source_discontinuous_end: 'ACI 318-19 §25.4.3.4, p.476' headed_bar: formula: 'ldt = (1.5*fy*psi_e*psi_p*psi_o*psi_c / (75*sqrt(fc))) * db' source: 'ACI 318-19 §25.4.4.2(a), p.477' no_lambda_normalweight_only: true # §25.4.4.1(d) requires normalweight concrete conditions: max_bar_size: 11 abrg_min: '4*Ab' # net bearing area of head clear_cover_min_db: 2 ctc_spacing_min_db: 3 source_conditions: 'ACI 318-19 §25.4.4.1, p.477' modification_factors: psi_e: {coated: 1.2, uncoated_or_galvanized: 1.0} psi_p: {no11_or_smaller_confined: 1.0, other: 1.6} # Att >= 0.3*Ahs or s >= 6db psi_o: {inside_column_core_or_side_cover_ge_6db: 1.0, other: 1.25} psi_c: {fc_lt_6000: 'fc/15000 + 0.6', fc_ge_6000: 1.0} source: 'ACI 318-19 Table 25.4.4.3, p.480' compression: formula: 'ldc = max((fy*psi_r/(50*lambda*sqrt(fc)))*db, 0.0003*fy*psi_r*db)' source: 'ACI 318-19 §25.4.9.2, p.487' modification_factors: psi_r: {enclosed_spiral_tie_or_hoop: 0.75, other: 1.0, permitted_default: 1.0} lambda: {lightweight: 0.75, normalweight: 1.0} source: 'ACI 318-19 Table 25.4.9.3, p.487' excess_reinforcement_reduction: ratio: '(As_required / As_provided)' source: 'ACI 318-19 §25.4.10.1, p.487' applies_to: ['tension.general (§25.4.2.1a)', 'wwr.deformed (§25.4.6.1a)', 'wwr.plain (§25.4.7.1a)', 'compression (§25.4.9.1a)'] floor: 'never below the matching entry in minimums_in' not_permitted: noncontinuous_supports: true anchorage_or_development_for_fy_required: true bars_required_continuous: true hooked_headed_or_mechanically_anchored: true seismic_design_category_c_d_e_or_f: true pile_anchorage_seismic_design_category_c_d_e_or_f: true source_exclusions: 'ACI 318-19 §25.4.10.2, p.487-488' excluded_from_lap_splices: true # §25.5.1.4 -- never permitted when calculating a lap length source_lap_exclusion: 'ACI 318-19 §25.5.1.4, p.488' wwr: deformed: formula: 'ld = ld_per_tension_simplified_or_general * psi_w' source: 'ACI 318-19 §25.4.6.2, p.483' measured_from: 'critical section to the end of wire' requires_all_wires_le_D31: true psi_w: case_a: 'psi_w = (fy - 35000) / fy' case_b: 'psi_w = 5*db/s' max: 1.0 applies_when: 'at least one cross wire within ld, at least 2in from the critical section' no_qualifying_cross_wire: 1.0 source: 'ACI 318-19 §25.4.6.3 / §25.4.6.4, p.483-484' confidence: >- B -- case_a is reconstructed from a fragmented raw-text-layer equation image (two occurrences of fy, one occurrence of 35,000, one subtraction, no other terms; no rendered page was available to confirm placement). case_b (5*db/s) reads unambiguously off the same page. Re-verify case_a against a rendered page before treating it as A. epoxy_psi_e_permitted: 1.0 # for epoxy-coated WWR meeting §25.4.6.3 source_epoxy: 'ACI 318-19 §25.4.6.2, p.483' no_qualifying_wire_or_zinc_coated_use: 'plain WWR provisions (§25.4.7)' source_fallback: 'ACI 318-19 §25.4.6.5 / §25.4.6.6, p.484' plain: formula: 'ld = max(spacing_of_cross_wires + 2, 0.27*(fy*Ab)/(s*lambda*sqrt(fc)))' source: 'ACI 318-19 §25.4.7.2, p.484-485' measured_from: 'critical section to the outermost cross wire' requires_min_two_cross_wires: true source_min_cross_wires: 'ACI 318-19 §25.4.7.1, p.484' variables: 'Ab = area of the individual wire being developed; s = spacing between the wires being developed; lambda per Table 25.4.2.5' strand: # Pretensioned seven-wire strand, clean, 1/4in / 3/8in / 1/2in diameter, # bonded applications only. development: formula: 'ld = (fse/3000)*db + ((fps - fse)/1000)*db' source: 'ACI 318-19 §25.4.8.1, Eq. 25.4.8.1, p.485' applies_to: 'seven-wire strand, clean, 1/4in/3/8in/1/2in diameter, bonded' does_not_apply_to: ['plain wires', 'end-anchored tendons', 'unstressed strand'] source_scope: 'ACI 318-19 §25.4.8, p.485' max_fps_tested_psi: 275000 transfer_length_formula: '(fse/3000)*db' transfer_db: 50.0 # shorthand at fse = 150,000 psi: (150000/3000) = 50 transfer_db_basis_fse_psi: 150000 additional_bonded_length_formula: '((fps - fse)/1000)*db' doubling_required_when_not_bonded_to_end_and_tension_at_service: true source_doubling: 'ACI 318-19 §25.4.8.1(b), p.485' doubling_exception_zero_tension_service_loads: true source_doubling_exception: 'ACI 318-19 §24.5.2, discussed at R25.4.8.3, p.486' reduced_embedment_permitted: condition: 'design strand stress at the section does not exceed the bilinear Eq. 25.4.8.1 relationship' source: 'ACI 318-19 §25.4.8.2 / §25.4.8.3, p.485-486' correction: >- The extraction note filed the doubling requirement under §25.4.8.3; the code text puts it at §25.4.8.1(b) (conditioned on "bonding does not extend to end of member AND design includes tension at service loads"). §25.4.8.3 is the separate, related provision permitting embedment SHORTER than ld at a section, and the "twice ld" / zero- tension-exception language the note quoted from there is R25.4.8.3 COMMENTARY explaining the §25.4.8.1(b) rule, not a second code rule. lap: max_bar_size: 11 # bars larger than No. 11 may not be lap spliced source_max_bar_size: 'ACI 318-19 §25.5.1.1, p.488' tension: class_a_factor: 1.0 class_b_factor: 1.3 min_in: 12.0 formula: 'lst = max(factor*ld, min_in)' ld_basis: 'ld per §25.4.2.1(a)' class_a_requires: 'As_provided/As_required >= 2.0 over the splice length, AND <= 50 percent of As spliced within the required lap length' class_b_requires: 'anything not meeting Class A (includes every case where As_provided/As_required < 2.0)' source: 'ACI 318-19 §25.5.2.1, Table 25.5.2.1, p.488-489' different_size_bars: 'lst = greater of ld(larger bar) and lst(smaller bar)' source_different_size: 'ACI 318-19 §25.5.2.2, p.489' compression: fy_le_60000: 'lsc = max(0.0005*fy*db, 12 in.)' fy_60000_to_80000: 'lsc = max((0.0009*fy - 24)*db, 12 in.)' fy_gt_80000: 'lsc = max((0.0009*fy - 24)*db, lst_per_25.5.2.1)' source: 'ACI 318-19 §25.5.5.1(a-c), p.491' fc_lt_3000_increase_factor: 1.3333 # "increased by one-third" source_increase: 'ACI 318-19 §25.5.5.1, p.491' no14_no18_to_no11_or_smaller_permitted: true # the only exception to max_bar_size above source_exception: 'ACI 318-19 §25.5.5.3, p.491' different_size_bars: 'lsc = greater of ldc(larger bar, per §25.4.9.1) and lsc(smaller bar, per §25.5.5.1)' source_different_size: 'ACI 318-19 §25.5.5.4, p.491' wwr_deformed: formula: 'lst = max(1.3*ld, 8 in.)' source: 'ACI 318-19 §25.5.3.1, p.489-490' wwr_plain: formula: 'lst = max(spacing_of_cross_wires + 2, 1.5*ld, 6 in.)' formula_when_As_ratio_ge_2: 'lst = max(1.5*ld, 2 in.)' source: 'ACI 318-19 §25.5.4.1 / §25.5.4.2, p.490-491' # sources: 2026-08-30 2026-08-31 # -> rationale.md § connectors 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: # KEYED BY THE FULL PRODUCT NAME, manufacturer included, because that # is how the owner wrote it and -- measured 2026-08-31 -- it is also # verbatim what the model's connector TYPE carries in its description # field. (Which field that is, is plant knowledge and is named in the # plant layer through the `member.identity_description` role, never # here.) One spelling, and the alias table has nothing to translate. # ---- THE STANDARD (NON-COMPOSITE) LINE ----------------------------- # # SOURCE FOR EVERY VALUE IN THE THREE ENTRIES BELOW: W1 (hkties.com # standard / non-composite products page, captured 2026-08-30), re-read # against the live page by the landing agent 2026-09-01 -- the whole # ST50/ST75/ST100 comparison table and its three footnotes, transcribed # a second time and compared cell by cell. Every cell agreed. The # capture is a clue; the page is the source, and this line is the # difference (CLAUDE.md, the three-stage law). # # A CAPTURE HASH IS NOT A DOCUMENT HASH. The vendor can edit the page # tomorrow and nothing here would notice, so read these as "what that # page said on those dates", and re-fetch before relying on one in an # engineering decision. # # THE TABLE'S OWN IDENTITY, ASSERTED AT LANDING: insulation thickness # (2/3/4 in) tracks the designation number (ST50/ST75/ST100 = 50/75/100 # mm), and tie length grows with it (5.31 / 6.25 / 7.25 in). Both hold # across all three columns, which is what makes a row-shift detectable. # ONE COLUMN DOES NOT RISE MONOTONICALLY AND IT IS NOT AN ERROR: # `ultimate_panel_shear_per_tie_lb` reads 648 / 924 / 780 -- ST75 is the # highest and ST100 is BELOW it. That was checked twice against the page # for exactly the reason the identity rule exists, and the page really # does print it that way. Do not "correct" it into a rising series. 'HK Composites (ST50)': manufacturer: HK Composites designation: ST50 system: standard_non_composite design_basis: non_composite tie_length_in: 5.31 embedment_head_in: 1.5 embedment_tip_in: 1.81 foam_plug_diameter_in: 0.5 insulation_thickness_in: 2.0 structural_wythe_min_in: 2.0 fascia_wythe_range_in: [2.0, 4.0] ultimate_tensile_pullout_lb: 1260 shear_strength_lb: 1300 ultimate_panel_shear_per_tie_lb: 648 basis_fc_psi: 3000 test_criteria: 'ICC-ES AC 320' thermal_conductivity_btu_in_hr_ft2_f: 2.1 fire_performance: >- Resists a 300 lb tension load for over 90 minutes when fire tested per ASTM E1512-01 section 7.4.3. alkaline_resistance: >- Excellent -- 99.1% retained strength after 3,000 hours in pH 12 solution. service_temperature_f: [-40, 200] # THE ONE NUMBER THAT DID NOT ARRIVE WITH THE REST. The page publishes # no working load and no safety factor; see `safety_factors` below. capacity_lb: null capacity_unknown_because: >- The three capacities above are ULTIMATES and are named as such. The page publishes no working load and no safety factor, so a single `capacity_lb` -- which every reader would take for an allowable -- cannot be derived from them. A check that needs a working load stands down naming this key; a check that can consume an ultimate reads the `ultimate_*` keys and declares that it is doing so. min_edge_distance_in: null min_edge_distance_unknown_because: >- Still not published. The vendor's own product page carries embedment depths and wythe thicknesses but NO edge distance for any designation, so the 2026-09-01 landing did not fill this and did not weaken the rule that filled it for ST100: it is a property of the PRODUCT, the check that needs it stands down naming this key, and it may never be filled from another product's datasheet. # ST75 IS NEW HERE, added 2026-09-01 off the same table. It was not in # this file before because nobody had read the page; it is neither # evidenced on a job nor named by the owner, and it is landed because # the table publishes it, which is what a public catalogue is for. 'HK Composites (ST75)': manufacturer: HK Composites designation: ST75 system: standard_non_composite design_basis: non_composite tie_length_in: 6.25 embedment_head_in: 1.5 embedment_tip_in: 1.73 foam_plug_diameter_in: 0.57 insulation_thickness_in: 3.0 structural_wythe_min_in: 2.0 fascia_wythe_range_in: [2.0, 4.0] ultimate_tensile_pullout_lb: 1660 shear_strength_lb: 1700 ultimate_panel_shear_per_tie_lb: 924 basis_fc_psi: 3000 test_criteria: 'ICC-ES AC 320' thermal_conductivity_btu_in_hr_ft2_f: 2.1 fire_performance: >- Resists a 300 lb tension load for over 90 minutes when fire tested per ASTM E1512-01 section 7.4.3. alkaline_resistance: >- Excellent -- 99.1% retained strength after 3,000 hours in pH 12 solution. service_temperature_f: [-40, 200] capacity_lb: null capacity_unknown_because: >- As ST50 -- the published figures are ultimates and the page states no safety factor. min_edge_distance_in: null min_edge_distance_unknown_because: >- Not published on the page, and NOT inherited from ST100's 3.0 in. That number came from the owner about ST100 specifically. 'HK Composites (ST100)': manufacturer: HK Composites designation: ST100 system: standard_non_composite design_basis: non_composite tie_length_in: 7.25 embedment_head_in: 1.5 embedment_tip_in: 1.73 foam_plug_diameter_in: 0.57 insulation_thickness_in: 4.0 structural_wythe_min_in: 2.0 fascia_wythe_range_in: [2.0, 4.0] ultimate_tensile_pullout_lb: 1660 shear_strength_lb: 1700 # 780, and it is LOWER than ST75's 924. See the identity note above. ultimate_panel_shear_per_tie_lb: 780 basis_fc_psi: 3000 test_criteria: 'ICC-ES AC 320' thermal_conductivity_btu_in_hr_ft2_f: 2.1 fire_performance: >- Resists a 300 lb tension load for over 90 minutes when fire tested per ASTM E1512-01 section 7.4.3. alkaline_resistance: >- Excellent -- 99.1% retained strength after 3,000 hours in pH 12 solution. service_temperature_f: [-40, 200] capacity_lb: null capacity_unknown_because: >- As ST50 -- the published figures are ultimates and the page states no safety factor. The tensile and shear figures happen to equal ST75's; the panel-shear figure does not, and none of the three is inherited from anywhere. Each was read off this designation's own column. # STATED BY THE OWNER, 2026-08-31: "pin edge distance = 3". ST100 的 # 知识。写。" -- a number, for this designation, in his own words. # ST50 and the rest stay null: he gave one product's number and # borrowing it is the thing `min_edge_distance_unknown_because` # exists to forbid. min_edge_distance_in: 3.0 min_edge_distance_source: >- Stated by the owner 2026-08-31 ("pin edge distance = 3"). Not read off a datasheet -- when HK Composites' sheet arrives it is worth checking against, and a disagreement is a question for him, not a silent replacement. # THE 6.0 IN CALIBRATION THAT USED TO SIT HERE HAS BEEN WITHDRAWN, and # withdrawing it is the point rather than an omission. It was the # smallest in-plane distance measured from a pin to the PANEL edge, # and 2026-08-31 the owner defined the measurement differently: the # edge that governs is the INSULATION edge (see `edge_reference` # below). A pin-to-panel-edge number is not a calibration for a # pin-to-insulation-edge check -- the two are different measurements # of different things, and carrying the 6.0 across would have set an # expectation the first live run then looked like a regression # against. The insulation-edge distances on this job are UNMEASURED. # Whatever the first run prints is new information. # ---- THE CA (COMPOSITE ACTION) LINE, landed 2026-09-01 -------------- # # SOURCE FOR EVERY VALUE IN THE THREE ENTRIES BELOW: W2 (hkties.com # CA / composite-action products page, captured 2026-08-30), re-read # against the live page by the landing agent 2026-09-01, cell by cell, # with every footnote. Every cell agreed. # # A DIFFERENT DESIGN BASIS, NOT A BIGGER TIE. These are for panels # designed to act COMPOSITELY -- the vendor's own sentence is in # `design_bases` below -- and they are glass-fibre reinforced where the # Standard line deliberately is not. The capacities are three to five # times the Standard line's, which is exactly why nothing may read one # line's number onto the other's designation. # # THE TABLE'S IDENTITY, ASSERTED AT LANDING: insulation thickness # (2/3/4 in) tracks the designation (CA50/CA75/CA100), tie length rises # (5.5 / 6.5 / 7.5 in), ultimate tension is CONSTANT across all three # (5,900 lb -- it is a property of the tie's own section, not of the # panel), and ultimate shear FALLS as the tie gets longer # (5,315 / 4,902 / 4,489), as does the 5% fractile (5,183 / 4,720 / # 4,257). Every fractile value sits below its own ultimate, on every # row, which is the check that catches a shifted column. 'HK Composites (CA50)': manufacturer: HK Composites designation: CA50 system: composite_action design_basis: composite tie_length_in: 5.5 embedment_head_in: 1.75 embedment_tip_in: 1.75 foam_section_in: [0.47, 3.13] insulation_thickness_in: 2.0 wythe_1_range_in: [2.0, 6.0] wythe_2_range_in: [2.0, 6.0] ultimate_tension_lb: 5900 ultimate_tension_basis_fc_psi: 5000 ultimate_shear_lb: 5315 shear_5pct_fractile_95pct_confidence_lb: 5183 shear_basis_fc_psi: 5000 test_criteria: 'ICC-ES AC 320' thermal_conductivity_btu_in_hr_ft2_f: 1.75 fire_performance: >- Resists a 300 lb tension load for over 90 minutes when fire tested per ASTM E1512-01 section 7.4.3. alkaline_resistance: >- Excellent -- 96.2% retained strength after 3,000 hours in pH 12 solution. service_temperature_f: [-40, 200] capacity_lb: null capacity_unknown_because: >- Ultimates only, and the page states no safety factor. See `safety_factors` below. The 5% fractile is NOT a working load either -- it is a statistical lower bound on the same ultimate test population, with no factor applied. min_edge_distance_in: null min_edge_distance_unknown_because: >- The CA page publishes no edge distance for any designation. Not inherited from the ST100 figure, which came from the owner about a different product on a different design basis. 'HK Composites (CA75)': manufacturer: HK Composites designation: CA75 system: composite_action design_basis: composite tie_length_in: 6.5 embedment_head_in: 1.75 embedment_tip_in: 1.75 foam_section_in: [0.47, 3.13] insulation_thickness_in: 3.0 wythe_1_range_in: [2.0, 6.0] wythe_2_range_in: [2.0, 6.0] ultimate_tension_lb: 5900 ultimate_tension_basis_fc_psi: 5000 ultimate_shear_lb: 4902 shear_5pct_fractile_95pct_confidence_lb: 4720 shear_basis_fc_psi: 5000 test_criteria: 'ICC-ES AC 320' thermal_conductivity_btu_in_hr_ft2_f: 1.75 fire_performance: >- Resists a 300 lb tension load for over 90 minutes when fire tested per ASTM E1512-01 section 7.4.3. alkaline_resistance: >- Excellent -- 96.2% retained strength after 3,000 hours in pH 12 solution. service_temperature_f: [-40, 200] capacity_lb: null capacity_unknown_because: >- As CA50 -- ultimates, no published safety factor. min_edge_distance_in: null min_edge_distance_unknown_because: >- Not published on the page. 'HK Composites (CA100)': manufacturer: HK Composites designation: CA100 system: composite_action design_basis: composite tie_length_in: 7.5 embedment_head_in: 1.75 embedment_tip_in: 1.75 foam_section_in: [0.47, 3.13] insulation_thickness_in: 4.0 wythe_1_range_in: [2.0, 6.0] wythe_2_range_in: [2.0, 6.0] ultimate_tension_lb: 5900 ultimate_tension_basis_fc_psi: 5000 ultimate_shear_lb: 4489 shear_5pct_fractile_95pct_confidence_lb: 4257 shear_basis_fc_psi: 5000 test_criteria: 'ICC-ES AC 320' thermal_conductivity_btu_in_hr_ft2_f: 1.75 fire_performance: >- Resists a 300 lb tension load for over 90 minutes when fire tested per ASTM E1512-01 section 7.4.3. alkaline_resistance: >- Excellent -- 96.2% retained strength after 3,000 hours in pH 12 solution. service_temperature_f: [-40, 200] capacity_lb: null capacity_unknown_because: >- As CA50 -- ultimates, no published safety factor. min_edge_distance_in: null min_edge_distance_unknown_because: >- Not published on the page. # -> rationale.md § connectors.design_bases design_bases: non_composite: applies_to_system: standard_non_composite designations: [DP25, DP30s, DP38, DP40, ST50, ST60, ST75, ST100] # SOURCE: W1 (hkties.com standard page, captured 2026-08-30, # re-read live 2026-09-01). The page's own sentence, verbatim. source_sentence: >- "This system is designed to create a non-composite insulated concrete sandwich panel" -- the two wythes of concrete are tied together but the design "still allow[s] the two layers to shrink or expand independently", the tie flexing to take the differential movement. what_it_means: >- The wythes are NOT designed to act as one section. The ties carry the fascia wythe and flex under the differential thermal movement between the two wythes; the structural wythe carries the panel. source: 'W1 -- hkties.com standard / non-composite products page, captured 2026-08-30, re-read 2026-09-01' composite: applies_to_system: composite_action designations: [CA25, CA40, CA50, CA75, CA100, CA125, CA150] # SOURCE: W2 (hkties.com CA page, captured 2026-08-30, re-read live # 2026-09-01). The page's own sentence, verbatim. source_sentence: >- "The two wythes of concrete are acting together structurally thus allowing a thinner panel to be created." what_it_means: >- The wythes ARE designed to act together, so the section a calculation may use is the composite one -- which is the whole reason the panel can be thinner. source: 'W2 -- hkties.com CA / composite-action products page, captured 2026-08-30, re-read 2026-09-01' # A THIRD ANSWER EXISTS AND IT IS THE HONEST DEFAULT. unknown_is_not_non_composite: >- A panel whose tie designation is not in either list above, or which carries no recognised tie at all, has an UNKNOWN design basis. It is not non-composite by default and it is not composite by default. The lookup returns None with the reason, in the same discipline as knowledge.same_face(). # -> rationale.md § connectors.product_line_no_published_capacity product_line_no_published_capacity: standard_non_composite: # SOURCE: W1, product-line table (captured 2026-08-30, re-read 2026-09-01) DP25: {description: 'Double-Prong 1" Tie', capacity_lb: null} DP30s: {description: 'Double-Prong 1" Tie', capacity_lb: null} DP38: {description: 'Double-Prong 1.50" Tie', capacity_lb: null} DP40: {description: 'Double-Prong 1.50" Tie', capacity_lb: null} ST60: {description: 'Standard 2.5" Tie', capacity_lb: null} composite_action: # SOURCE: W2, product-line table (captured 2026-08-30, re-read 2026-09-01) CA25: {description: 'Quad Tip 1" Tie', capacity_lb: null} CA40: {description: 'Quad Tip 1.5" Tie', capacity_lb: null} CA125: {description: 'Quad Tip 5" Tie', capacity_lb: null} CA150: {description: 'Quad Tip 6" Tie', capacity_lb: null} unknown_because: >- The manufacturer's pages name these model numbers in the product-line list and tabulate capacities only for ST50/ST75/ST100 and CA50/CA75/CA100. No capacity is published for the models above, in any unit, on either page. They exist; nothing else about them is known here, and nothing may be interpolated from the tabulated neighbours. # -> rationale.md § connectors.safety_factors safety_factors: tension_fos: null shear_fos: null unknown_because: >- Both manufacturer pages publish ULTIMATE capacities and state no safety factor anywhere -- no heading, no footnote, no test citation. A working load cannot be derived from an ultimate without one, and the 4:1 published for the disk-anchor inserts belongs to another manufacturer's product and may not be borrowed. Read 2026-09-01 against both live pages by the landing agent; the absence was looked for, not assumed. capacity_is_ultimate: true # sources: 2026-08-31 # -> rationale.md § connectors.edge_reference edge_reference: measured_to: insulation_edge stated_by: Will stated_on: '2026-08-31' what_it_means: >- A connector's edge distance is measured to the edge of the INSULATION, not to the edge of the concrete panel. On a piece whose insulation runs to the panel edge the two are the same number; where they differ, the insulation governs. # A SOLID ZONE THAT GOES THROUGH REDEFINES THE EDGE; A RECESS DOES NOT. # Full depth means concrete has REPLACED the insulation there, so the # zone's border is a real insulation boundary and a pin beside it is at # an edge. A recess is partial depth: insulation still runs under it, # the pin still has insulation on every side, and the original boundary # governs. through_cut_solid_zone_moves_the_edge: true recess_moves_the_edge: false consequences: >- The check's reference geometry is the insulation extent MINUS the solid zones that cut through it. That is the same solid-zone population rows 5.11-5.13 recognise -- ONE recognition, several consumers, and a second reader for the same parts is how two rows come to disagree about what is on a piece. Recesses are excluded from it entirely, on his sentence: they never move the edge, so for this measurement they do not exist. # HOW THROUGH-CUT IS TOLD FROM PARTIAL IS AN INFERENCE, and it is # labelled one everywhere it is used. Depth is not a field on these # parts; it is in the type's description text (`3" SOLID ZONE`, # `4" SOLID ZONE`) beside an insulation type named for its own # thickness. Equal depths read as through-cut. A zone whose depth # cannot be parsed is NOT guessed at either way -- the pins near it go # unjudged and the finding says so. depth_is_inferred_from_names: true depth_inference_caveat: >- Reading a depth out of a description string is a reading, not a measurement. It is marked inferred in every finding built on it, and a solid zone whose description states no depth stands the pins around it down rather than defaulting to either answer. # sources: 2026-08-31 # -> rationale.md § checklist 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' # Re-graded A -> B 2026-08-30. Mechanical, but it needs the plant's # design-token pattern to recognise a design label inside a view name, # and its verdict is never worth failing a ticket over -- a stale name # on a correct note is a trap for a reader, not a defect in the piece. - 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' # Built 2026-08-30 (CALLOUT-OVERLAP, ticketpage.callout_overlap_status / # ticketchecks.check_callout_overlap). Text-bearing annotations on one # placed view whose printed boxes intersect with positive area. Grade C # and not B: a bounding box carries the text's leading and side # bearings, so it is a little larger than the ink -- an intersection is # a place for a person to look, never a defect claim. Text-vs-linework # is deliberately out of scope (a call-out over a drawn edge is the # normal condition on every elevation). Rotated annotations report an # axis-aligned box that overstates the ink, so a pair with a rotated # member is set aside under CALLOUT-OVERLAP-UNREAD, never flagged. - 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 # Printed / reconstructed / measured, and the finding names which of # the three is the odd one out. When only two of them can be read the # check says so (WGT-TWO-WAY, never OK) rather than passing a two-way # off as a three-way; with fewer than two it does not run at all. - 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 # Built 2026-08-30 (CAST-EXTRA, ticketpage.cast_extra_agreement / # ticketchecks.check_cast_extras). It is a SET comparison, not a weight # one: the extras the ticket names against the extras whose volume fed # 2.2's reconstructed weight (liftexport.extra_cast_members, the same # members extra_cast_ft3 sums). That is the question 2.2 cannot ask -- # three weights can agree while all three are wrong the same way, and a # ledge missing from both model and drawing agrees with itself. # The ticket side is TEXT (a BOM row or a view's words naming a # families.cast_include token), never the drawn geometry: no query # answers "does this elevation show a ledge". An unreadable or # un-gathered BOM stands the comparison down -- a row naming the extra # may have been missed -- while an absent BOM is judged from view text # and says so. - 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' # Built 2026-08-30 (STRAND-CALLOUT, ticketpage.debond_callout_status / # ticketchecks.check_debond_callout). Owner's ruling: where the debond # condition is met, expect a call-out; where there is none, WARN. # It CONSUMES 4.7 rather than re-deriving the geometry -- 4.7 owns where # debonding is required, 4.5 owns whether the ticket says so. When 4.7 # could not judge, 4.5 reports NOT RUN with 4.7's own reason instead of # warning about a requirement nobody established. # Grade C stands: the call-out found on these tickets is a bare note # with no station and no strand number, so a present call-out shortlists # rather than proves, and the finding says exactly that. - 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' # ---- THE BOM'S OWN STRAND ROW, added 2026-08-31 ---------------------- # # A CHECK THAT RAN AND COULD REACH NO ROW. `check_bom_strand_count` has # been registered and running since before the master checklist moved into # this file; nothing named it, so its BOM-STRAND finding -- which can be a # FAIL -- landed in `qc-run.json` under a code no row owned and no row # ever turned red. It was recorded as a known exemption # (`ROWLESS_CHECKS`) rather than fixed, because giving a check a row is a # decision about what the report claims and that is the owner's. He made # it: "看看能跑的,能做的,现在都做了去". # # WHY ITS OWN ROW AND NOT A SECOND CHECK ON 4.1. 4.1 asks the printed # section call-out against the model (STR-QTY, emitted by # `ticketqc.check_ticket`). This asks the BOM's LINEAR-FEET row -- a # length, divided by the piece length -- against the same model, and # optionally against the section print as a third leg. Different input, # different failure, different repair: a wrong BOM row is fixed in the # schedule and a wrong section call-out is fixed on the drawing. # # AND THE MULTI-CHECK IDIOM WOULD HAVE COST NOT RUNS, WHICH IS THE # OPPOSITE OF THE ORDER. A row is answered only when EVERY check it names # has spoken, so hanging this off 4.1 would make 4.1 NOT RUN on every # ticket whose BOM could not be read -- turning a row that had a verdict # into a red one, over a silence about a different question. 5.1 pairs two # checks because they are two halves of ONE question and half an answer is # not an answer; these are two independent answers, and either alone is a # real verdict. # # GRADE A: the comparison is arithmetic and its one constant is measured # (`strand_count_from_linear_ft`'s 0.05 ratio tolerance, from 12 real # tickets whose remainders span 0.0032-0.0226). The third leg needs # `legend.section_strand_view` and is optional -- without it the row is # still fully answered two ways. - 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' # 5.1 IS TWO QUESTIONS AND ONLY ONE OF THEM WAS EVER ASKED. # `check_bom_members` compares the BOM's named rows against the # assembly -- a real comparison, and not the one this row is written # as (docs/checklist-code-map.md, disagreement #2). The other half is # the plan's own call-outs, and the part of THAT which needs the # detail sheets is not built: `check_detail_callouts` reads # nomenclature.detail_sheet_prefix, finds the bubbles the plan cites, # and reports NOT RUN naming the sheets nobody reads. It passes only # when the plan cites no detail at all, which is a real verdict. # Two checks, so the row is answered only when BOTH spoke -- see # checklist_states. - 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' # 5.3 before 5.4. They were the wrong way round until 2026-08-30 (the # owner read a real WP133 run and spotted it); the table is read as a # checklist, and a reader who cannot find 5.3 where 5.3 belongs starts # doubting whether the row is there at all. `test_checklist_is_in # _numerical_order` now holds every row to it. # The reason slot is what makes this a FOR ENGINEER row rather than an # unbuilt one -- see CHECKLIST_FOR_ENGINEER -- so it says plainly that # nothing computes it. It used to read "code narrows, person decides", # which promised a narrowing no code performs. - 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' # SPLIT 2026-08-30, on the owner's approval. 5.5 was one row asking two # questions of different kinds. UNIQUENESS is a comparison between the # model's marks and the sheet's printed text, and a comparison is code's # job -- it moved to 5.6. LEGIBILITY is a property of the PICTURE, which # no query answers, so it stays here at grade D and reads FOR ENGINEER. # The vision path (`ticketvision.ESCALATIONS`, AI-BARMARK) is unchanged # and still escalates this row; its question text now overlaps 5.6 on # the uniqueness half, which is deliberate -- a model reading the # rendered sheet is a second, independent look, never the computed # answer, and section 8bis forbids it from claiming a verdict either way. - 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 # The computed half of the old 5.5 (BARMARK-UNIQUE, # ticketpage.bar_mark_uniqueness_status / ticketchecks. # check_bar_mark_uniqueness). Three questions, three defects: one mark # naming two parts, a printed mark no bar carries, a modelled bar never # printed. Grade B -- mechanical, and the only local constant is the # shape of a mark token, derived here rather than added as a plant fact. - 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' # Built 2026-08-30 (BAR-LENGTH, ticketpage.bar_stock_length_status / # ticketchecks.check_bar_stock_length) on the owner's ruling that a bar # is cut from 40 ft stock. The length is READ from the member.length # role and never measured -- a bent bar consumes its developed length # and a bounding box understates every one of them. Grade B: mechanical # comparison against a published stock length, which is why the number # lives in the catalogue and not in this file. - 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' # ---- CONNECTORS ON AN INSULATED PANEL, added 2026-08-31 --------------- # # Owner: "insulated panel 加一个 check,检查 connector 是不是存在,以及够不够" # and, the same day, "再增加 connector edge distance 的 check". # # THE FIRST QUESTIONS EVER ADDED UNDER THE THREE-LAYER ARCHITECTURE, and # the procedure is the point: a new QUESTION is three entries here (public, # because what a connector is and why it matters is trade knowledge), three # check functions in `vibe.ticketchecks` bound by these ids, and a line in # the plant's own board saying where it sits in ITS numbering. Nothing # about them is plant-specific except the part numbers, which live in the # plant layer where part numbers belong. # # ALL THREE ARE INSULATED-ONLY, through `checks.only_applies_to` in the # plant layer -- NOT through anything written here. On a solid panel there # is no insulation for a connector to cross, so the question is not quiet, # it is meaningless; and on a piece nothing could classify they read NOT # RUN in red, because answering an insulated question about a piece that # may not be one is exactly the silent pass this tool exists to refuse. - id: '5.8' name: 'Connectors present on an insulated panel' grade: 'B' owner_codes: ['CONNECTOR-EXIST'] not_run_reason: null checks: 'check_connectors_exist' # Grade B and not A: the count is mechanical, but WHAT THE DESIGN REQUIRES # is a line of the design summary the parser cannot read yet -- WP115's # 8.1 lists `HK ST100'S @1'-4" O.C.` among its uninterpreted lines. The # model honours it exactly (measured 2026-08-31: five rows across at 16.0 # in field spacing, which IS 1'-4"), so the comparison has a calibrated # example waiting for a parser that can state the requirement. - 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' # The 6.2 shape, one part along. The minimum is a property of the PRODUCT # and lives in the public catalogue beside its capacity; it resolves # product-first through the plant's alias, and a plant-wide fallback is # second-class and named as such in the finding, because "6 inches" and # "6 inches for this product" are different claims. - id: '5.10' name: 'Connector edge distances' grade: 'B' owner_codes: ['CONNECTOR-EDGE'] not_run_reason: null checks: 'check_connector_edge_distance' # ---- SOLID ZONES AND INSULATION RECESSES, added 2026-08-31 ----------- # # Owner, twice, having asked for it before: "没有 solid zone 相关的检查。 # lifter 自带 solid zone,没检查这些 solid zone 是不是被加入了 assembly, # 是不是被显示在 ticket 上面,hatch pattern 对不对。这些都没有啊" -- and # then "不光是 solid zone,insulation recess 也得加上。你看看 page 2 里面 # 的那些 hatch pattern 就知道了。" # # WHAT A SOLID ZONE IS, in trade terms and therefore here: a block of # concrete that replaces the insulation locally so a lifting insert has # solid material to pull against. On this job every lifter NESTS one as a # sub-component of its own family, and there are standalone ones at # openings as well. # # WHAT A RECESS IS: a partial-depth pocket in the insulation. It is NOT a # model element on this job -- the recesses are DRAFTED filled regions on # the ticket, of types named for their depth -- so nothing about assembly # membership applies to them and 5.14/5.15 ask a different pair of # questions from 5.11/5.12 on purpose. # # ALL FIVE ARE INSULATED-ONLY, through `checks.only_applies_to` in the # plant layer and NOT through anything written here: a solid panel has no # insulation for a zone to interrupt or a recess to be cut in, so the # question is meaningless rather than quiet. Same ruling as 5.8-5.10. # # WHY 5.11-5.13 ARE THREE ROWS AND NOT ONE. They are three different # failures with three different repairs: a zone modelled but left out of # the assembly (add it), a zone in the assembly but not reaching the # layout view (a view or filter problem), and a zone that draws with the # wrong hatch (a filter override). Collapsing them would tell a modeler # something is wrong and not what to do. - 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' # SOLID-ZONE-ABSENT is a SECOND code on this row, not a second row, and # it is the distinct finding "a lifter that nests NO solid zone at all". # Measured 2026-08-31 it is zero across 192 lifters on 27 insulated # pieces -- and the day it is not zero it is a bigger defect than any # membership question, because there is no zone to add. Two codes, worst # severity wins, so it cannot be hidden behind the membership verdict. - 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' # The reference for the hatch is THE LEGEND'S OWN SWATCH, resolved live # from the sheet where it can be, with the plant's expected-pattern map # named as a fallback when it cannot. The legend is the sheet's own # statement of what each hatch means and it is the only reference that # updates itself when the plant changes one; a table in a YAML file is a # copy of it, and a copy is what goes stale. - 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' # GRADE C AND SCOPED HONESTLY. There is no model counterpart to compare a # drafted region against, and the design side -- which recesses this # piece is supposed to have -- is not parsed by anything today. So this # row's first life is presence-and-inventory, and where it cannot compare # it says what it lacks instead of comparing something else. A row that # counts regions and calls it agreement would be the silent pass this # table exists to refuse. - 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' # THE SIZE DISCREPANCY IS NOT THIS ROW'S QUESTION and the finding says so: # the legend's text says 1" and 2" while the region type names say 2in and # 3in. That is a real thing somebody should answer and the owner has been # asked. This row compares PATTERNS, which is a question with an exact # answer, and refuses to launder the size question through it. - 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' # Counts the MODELLED bars flagged as opening / blockout bars (a scheduled # rebar family member carries a 'bar type blockout' yes/no) at the openings # read off the piece -- the tickets carry no bar list. Short is a WARN a # person resolves (small openings often cannot take the bars), never a FAIL. - id: '5.16' name: 'Door openings / blockouts: opening bar type flagged and #3 diagonals present' grade: 'B' owner_codes: ['OPENING-DIAG'] not_run_reason: null checks: 'check_opening_diagonal_bars' - 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' # Re-graded A -> B 2026-08-30. What is deterministic is EMPTINESS -- a # note view holding nothing but its own heading -- and that needs the # plant's names for those views. Whether present content is the RIGHT # instruction is not decided here and is not claimed to be. - id: '6.5' name: 'DUNNAGE / ERECTION / PICKING notes say something' grade: 'B' owner_codes: ['NOTE-CONTENT'] not_run_reason: null checks: 'check_note_views' # Built 2026-08-31 on the owner's ruling (PARITY-NOTE, # ticketpage.parity_note_status / ticketchecks.check_parity_note): when # the count of one part on a piece is ODD, the BED PLAN must carry a # particular phrase. In section 6 because the trigger is a lifting # insert's count and the consequence is an erection instruction; the # BED PLAN is only where the instruction is printed. # # Grade B, not A, and the reason is not modesty. The count rests on a # family name the PLANT declares and the phrase match proves only that # the words are on the plan, not that they are about this part -- the # same wording covers a temporary erection-only element on a `*.A` # packet. Mechanical rule, plant-local constants: B. - 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' # ---- MINIMUM ANCHOR SPACING, added 2026-08-31 ------------------------- # # ALP catalog p53, 02/2024, footnote under the capacity table: "Minimum # anchor spacing is 2x the published edge distance". The owner supplied # that page; its edge-distance column became 6.2 and its capacities became # `catalogue.inserts`, and this sentence on the same page was read past. # Until today nothing in the tool asked how far apart two anchors must # sit, so two 8T anchors 20 in apart passed every row on the board while # the page asked for 28. # # Grade A, like 6.2, and for the same reason: the measurement is # mechanical and the minimum is PUBLISHED PRODUCT DATA in this file, # reached through the plant's alias. Nothing about the rule is a plant # habit. What is per-plant is only which parts are on the piece. # # The minimum is usually DERIVED rather than printed -- 2x the part's own # published edge distance -- and the finding prints that arithmetic, # because "28 inches" and "twice this part's published 14 inches" are # different claims and only the second can be checked. A part whose sheet # states a spacing outright overrides the multiple # (`min_spacing_rule.published_in`), and a part with no published edge # distance to double is NOT judged and is named: there is no plant-wide # fallback here, because a general "anchors go two feet apart" is not # something any layer states and inventing one is how a safety minimum # gets borrowed from a product it does not belong to. - id: '6.7' name: 'Minimum spacing between lifting anchors' grade: 'A' owner_codes: ['LIFT-SPACING'] not_run_reason: null checks: 'check_lifter_anchor_spacing' # ---- PICKING STATIONS, added 2026-08-31 ------------------------------ # # THE SECOND CHECK THAT RAN AND REACHED NO ROW. `check_picking_match` # emits PICK-STATIONS / PICK-UNREADABLE and NOTHING ON THE CHECKLIST # ASKED ABOUT PICKING STATIONS AT ALL -- so unlike 4.8 there was not even # a neighbouring row that could have claimed it. Same ruling, same day. # # WHY IT IS NOT PART OF 6.5. 6.5 asks whether the PICKING note view says # anything at all; this asks whether the stations printed in the picking # DIAGRAM are where the picking inserts actually are. A note that says # something and a diagram that is right are different claims, and folding # them together would let a full note view carry a wrong station past the # reader. # # SECTION 6 BECAUSE THE SUBJECT IS THE LIFTING INSERTS, and it sits after # 6.7 rather than beside 6.3 for the reason the board's own note gives: # nothing he already reads by number moves. # # GRADE B AND WARN BY RULING. The arithmetic is exact -- the diagram # prints segments from EACH END and `stations_from_ends` resolves them -- # but which families are picking inserts is a plant fact # (`handling.lifter_groups.face.families`), the tolerance is one # (`handling.picking_tolerance_ft`), and reading the diagram at all rests # on a measured drafting habit: picking prints at the highest y of the # size notes. Owner, 2026-08-27: a dunnage-style finding is never the # tool's verdict, so a mismatch is WARN and an engineer judges. - 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' # ---- LIFTING-POINT COUNT vs LENGTH, added 2026-09-02 ------------------- # # An MC plant rule, not a published catalogue number: owner, verbatim, # "ticketqc在MC加一条check:lifting points: MC的规矩是,panel长度小于20ft # 则2p lifting,大于20则是4p lifting。实际上25以内都可以2p。你按照20-25给 # warn,25以上给fail写。" Nominal rule: under 20 ft, 2-point; 20 ft and # over, 4-point. The real ceiling is looser than the nominal rule -- # 2-point actually holds to 25 ft -- so the grey zone between the two is a # WARN and only past 25 ft does 2-point FAIL. 4 or more points is always # OK; this only flags too FEW points for the length, never too many. # # Grade A, like 6.2/6.7/6.4: the arithmetic is mechanical (count the # modelled points, compare to the panel length) once the thresholds are # known, and what is per-plant is only the two numbers themselves -- # `lifters.lifting_points.two_point_max_length_ft` / `.two_point_ # absolute_max_ft` -- landed in MC's own plant layer. A layer that has # not set both -- any non-MC plant -- gets no judgement from this rule at # all: the check stands down naming the missing keys rather than holding # another plant's panels to MC's numbers. - id: '6.9' name: 'Lifting-point count vs panel length (plant rule)' grade: 'A' owner_codes: ['LIFT-POINTS', 'LIFT-POINTS-UNREAD'] not_run_reason: null checks: 'check_lifting_points' # ---- LIFTING-POINT POSITION vs PCI, added 2026-09-02 -------------------- # # DISTINCT FROM 6.9, on the same day. 6.9 grades the COUNT of lifting # points against panel length; this grades WHERE each already-counted # point sits against PCI's recommended station. An MC plant rule again, # not a published catalogue number: owner, verbatim, "follow PCI的意思是 # 检查一下lifter的位置,如果跟PCI相差大于6" 则报WARN。很多时候没办法完全 # follow PCI,但是这件事儿让用户判断可行不可行。" # # WARN-ONLY, NEVER FAIL -- by that same ruling. Real panels often cannot # follow PCI's ideal two-point location exactly, and whether a given miss # is acceptable is the engineer's judgement, not this tool's verdict -- # same posture as 6.8's picking-station mismatch. # # THE 0.207L TWO-POINT LOCATION IS A PCI PUBLIC CONSTANT (the equal-moment # two-point pick, confirmed on the sample_job drawings and mirrored by # `panellift.pci.OPTIMAL_TWO_LINE_FRACTION`), not plant knowledge -- only # the 6" TOLERANCE (`lifters.lifting_points.pci_position_tolerance_in`) is # MC's own number, landed beside the 6.9 thresholds. A plant that has not # set it gets no judgement from this rule, exactly like 6.9's stand-down. # # FOUR-POINT (OR ANY COUNT OTHER THAN TWO) IS NOT YET CONFIGURED. PCI's # recommended stations for a 4-point pick are real published numbers this # tool has not yet been given, so any count other than two stands the row # down naming the count rather than guessing a 4-point rule. # # Grade A: the arithmetic is mechanical (compare a modelled station to # 0.207L/0.793L) once the tolerance is known, and reuses 6.9's own point # clustering rather than re-deriving it. - id: '6.10' name: 'Lifting-point position vs PCI recommendation (plant rule)' grade: 'A' owner_codes: ['LIFT-POS', 'LIFT-POS-UNREAD'] not_run_reason: null checks: 'check_lift_positions' # ---- DUNNAGE-POINT COUNT vs STRIPPING-POINT COUNT, added 2026-09-02 ----- # # A THIRD MC plant rule, not a published catalogue number: owner, # verbatim, "默认MC panels dunnage point=stripping points。4p stripping # 则4p dunnage。" -- by default an MC panel's dunnage-point count equals # its stripping-point count. And: "stripping point就是face lifters位置" # / "LPA开头的就是face lifter" -- a stripping point is a face # (back-stripping) lifter position, and the LPA-prefixed anchors are the # face lifters. # # A DEFAULT, NOT AN ABSOLUTE RULE -- a mismatch is a deviation from it and # the owner judges feasibility, exactly like 6.9/6.10: WARN, never FAIL. # # Grade A, like 6.9/6.10: the arithmetic is mechanical (count the two # kinds of point, compare) once the plant fact -- which lifters are the # face/stripping ones -- is known. That fact is read structurally, off # `lifters.capacity_lb..usage: back_stripping` (the same tree and # resolver `lifter_capacity()` uses, so this row can never disagree with # 6.1 about which entry a family resolves to), with the bare `LPA` # family-name prefix as a fallback for a family with no usage tag at all. # `lifters.lifting_points.dunnage_matches_stripping` gates the rule -- a # layer that has not set it gets no judgement from this row at all, # exactly like 6.9/6.10's own stand-down. - id: '6.11' name: 'Dunnage-point count vs stripping-point count (plant rule)' grade: 'A' owner_codes: ['DUN-STRIP', 'DUN-STRIP-UNREAD'] not_run_reason: null checks: 'check_dunnage_stripping_match' # ---- LIFTER ACCESSORY (CUP/PATCH) ASSEMBLY MEMBERSHIP, added 2026-09-08, # REVIT issue #26 --------------------------------------------------------- # # 6.12 IS DELIBERATELY SKIPPED -- see the `FLEXURAL_CHECKS_ALREADY_ASKED` # comment in `ticketchecks.py`: a "6.12" built to an earlier draft of the # flexural table would have duplicated 6.10, so this row is numbered 6.13 # rather than reuse a number that already almost meant something else. # # A lifter can nest a small accessory family that is not itself a # recognised lifter -- a cup / void former that forms the crane-hook hole # (QL121 under LIFT_QL589G_8T, QL062 under LIFT_QL548G_4T) or a patch # (DB_PATCH, nested under either LPA pin-anchor family, `parent` given as # a list) -- and none of it is reachable through the SAME family-name # recognition 6.1's lifter capacity uses, because these are not lifters # themselves. `lifters.accessories` in the plant layer names which # families count and what role each plays; a nested child matching one of # those families that never joined the piece's assembly is invisible on # the ticket the same way a stray solid zone is (5.11's own reasoning, # one part class over) -- the layout view is assembly-scoped and cannot # show what the assembly does not contain. # # GRADE A: the gather is mechanical (`GetSubComponentIds()` on every # recognised lifter, generalising 5.11's own reader rather than adding a # second one) and the comparison is a plain membership test once # `lifters.accessories` says which families to look for. What is # per-plant is only that table -- which families are accessories at all # and what each one is called. # # A child NOT in `lifters.accessories` and also not a member is a # separate WARN (`LIFT-ACCESSORY-UNCLASSIFIED`), never folded into the # FAIL: this check has no basis to call an unrecognised nested part a # defect, only to flag it for a person to look at. # # LIVE-VALIDATED 2026-09-08 on the open MC model: WPA648 (assembly # 9141343) FAILs naming 8 DB_PATCH children nested in its LPA pin anchors # (e.g. child 9521037 under anchor 9521036) that never joined the # assembly; WPA655 (9141344) and SMD516 (6113164) are OK. - id: '6.13' name: 'Lifter accessory (cup/patch) is a member of the assembly' grade: 'A' owner_codes: ['LIFT-ACCESSORY-NOT-IN-ASSEMBLY', 'LIFT-ACCESSORY-UNCLASSIFIED'] not_run_reason: null checks: 'check_lifter_accessory_in_assembly' - 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'