% Input:       kernel syntax from the executable registry.
% Output:      validated model records, kernel events, and boundary signatures.
% Owned state: the chain cursor, the address node table, panel and relation stores.
% Invariants:  every record is written before validation; addresses parse or error.
% Next stage:  tenkz-kernel-policy derives the wires a picture's own words imply.
% SPDX-License-Identifier: Apache-2.0
% Copyright the TNLean project; see LICENSE for the full terms.
%
% tenkz-kernel-language.code.tex is the language stage: diagnostics, kernel state, the address grammar,
%   every key tree, record helpers, sugar expansion, the event stream,
%   boundary signatures, and the seven public commands.
%
% Split out of tenkz-kernel.code.tex without changing a line of content;
% the stages load in the order tenkz-kernel.code.tex lists them, and the
% golden event and pixel streams are unchanged by the move.

% Load guard.  It stands before \ExplSyntaxOn deliberately: a guarded exit
% never reaches the trailing \ExplSyntaxOff, so a guard inside expl3 syntax
% would leave the caller's catcodes changed -- ignoring spaces rather than
% erroring.  Guarding first leaves them exactly as they were found, whether
% the caller had expl3 syntax on or off.
\ifcsname tenkzkernel\endcsname \expandafter\endinput \fi

\ExplSyntaxOn


% Load guard: tikz-tensor-networks.sty loads this stage; a document that also inputs the
% file directly (the pre-bridge fixture spelling) must not redefine it.
\cs_if_exist:NT \tenkzkernel { \tex_endinput:D }

% ---------- messages --------------------------------------------------------
% Every diagnostic names the offending record or node and the source address
% text, so a failure is findable without re-reading the picture.

\msg_new:nnn {tenkz}{kernel-address}
  { [TKZ-LANG-ADDRESS]~'#1'~matches~no~address~production~(record~#2). }
\msg_new:nnn {tenkz}{kernel-address-operand}
  { [TKZ-LANG-ADDRESS]~operand~'#1'~of~'#2'~must~be~#3~(node~#4). }
\msg_new:nnn {tenkz}{kernel-unknown-key}
  { [TKZ-LANG-UNKNOWN-KEY]~'#1'~is~not~a~#2~key~(record~#3). }
\msg_new:nnn {tenkz}{kernel-choice}
  { [TKZ-LANG-CHOICE]~#1=#2~is~not~in~the~alphabet~#3~(record~#4). }
\msg_new:nnn {tenkz}{kernel-mark-tint-form}
  { [TKZ-LANG-TINT-FORM]~tint=true~requires~form=enclosure;~form=#1~has~no~
    tinted~interior~(record~#2). }
\msg_new:nnn {tenkz}{kernel-restyle-transform}
  {
    [TKZ-WIRE-RESTYLE-TRANSFORM]~a~restyle~of~'#1'~moves~the~drawn~
    geometry~(a~transform~canvas~class~key):~the~wire-ink~record~cannot~
    follow~it,~so~the~audit~would~read~the~wrong~route.~Restyle~ink~--~
    hue,~width,~dash~--~not~geometry.
  }
\msg_new:nnn {tenkz}{kernel-tombstone}
  { [TKZ-LANG-TOMBSTONE]~#1=#2~retired~from~the~alphabet:~#3~(record~#4). }
\msg_new:nnn {tenkz}{kernel-wire-arity}
  { [TKZ-LANG-WIRE-ARITY]~a~closed~wire~takes~no~positional~ends;~
    every~other~wire~takes~exactly~two~(record~#1). }
\msg_new:nnn {tenkz}{kernel-closed-points}
  { [TKZ-LANG-CLOSED-POINTS]~a~closed~wire~needs~nonempty~via=,~wind=,~
    or~route=\{<side>~of~<selector>\}~to~define~its~cycle~(record~#1). }
\msg_new:nnn {tenkz}{kernel-wind-zero}
  { [TKZ-WIND-ZERO]~wind=\{0,0\}~is~contractible;~
    use~an~ordinary~closed~string~instead~(record~#1). }
\msg_new:nnn {tenkz}{kernel-wind-via}
  { [TKZ-WIND-VIA]~wind=~and~via=~cannot~be~combined~(record~#1). }
\msg_new:nnn {tenkz}{kernel-wind-shape}
  { [TKZ-WIND-SHAPE]~wind='#1'~must~be~an~integer~pair~\{p,q\}~
    (record~#2). }
\msg_new:nnn {tenkz}{kernel-strict-sugar}
  { [TKZ-LANG-STRICT]~'#1'~is~sugar~and~\tnset{strict}~is~active. }
\msg_new:nnn {tenkz}{kernel-declare-class}
  { [TKZ-LANG-DECLARE]~'#1'~is~not~a~declaration~class;~
    expected~atom|skin|species. }
\msg_new:nnn {tenkz}{kernel-prelude-class}
  { [TKZ-LANG-PRELUDE-CLASS]~stock~prelude~class~'#1'~is~not~supported;~
    expected~skin. }
\msg_new:nnn {tenkz}{kernel-signature}
  { [TKZ-EQ-SIGNATURE]~sides~#1~and~#2~expose~unequal~boundaries:~
    (#3)~versus~(#4). }
\msg_new:nnn {tenkz}{kernel-signature-prose}
  { [TKZ-EQ-SIGNATURE]~sides~#1~and~#2~include~prose;~
    checked~relations~require~diagrams~unless~the~relation~is~opted~out. }
\msg_new:nnn {tenkz}{kernel-product-signature}
  { [TKZ-EQ-SIGNATURE]~panels~#1~and~#2~cannot~contract:~
    the~east~cut~(#3)~does~not~continue~as~the~west~cut~(#4). }
\msg_new:nnn {tenkz}{kernel-equation-arity}
  { [TKZ-EQ-ARITY]~an~equation~with~#1~panel(s)~has~#2~relation~glyph(s);~
    every~relation~needs~a~panel~or~product~group~on~each~side,~and~an~
    equation~asserts~at~least~one~relation. }
\msg_new:nnn {tenkz}{kernel-equation-nested}
  { [TKZ-EQ-NESTED]~tenkzeq~cannot~be~nested;~each~equation~is~one~
    audit~scope,~and~the~tngroup~command~nests~picture~content. }
\msg_new:nnn {tenkz}{kernel-cellset}
  { [TKZ-LANG-CELLSET]~#1=#2~is~not~a~cell-set~of~
    (interface,column)~terms~(record~#3). }
\msg_new:nnn {tenkz}{kernel-cell-occupied}
  { [TKZ-LANG-OCCUPANCY]~cell~(#1,#2)~belongs~to~record~#3;~
    record~#4~cannot~claim~it. }
\msg_new:nnn {tenkz}{kernel-sugar-value}
  { [TKZ-LANG-SUGAR]~#1=#2~is~malformed;~expected~#3~(record~#4). }
\msg_new:nnn {tenkz}{kernel-cluster-name}
  { [TKZ-LANG-CLUSTER-NAME]~cluster=#1~requires~name=~
    so~its~sub-atoms~are~addressable~(record~#2). }
% The audit is the equation's, and the form that misplaced it names itself:
% a picture and a group are refused in the same words, each about itself.
\msg_new:nnn {tenkz}{kernel-check-scope}
  { [TKZ-EQ-CHECK-SCOPE]~check=#1~belongs~to~tenkzeq~options,~
    not~a~#2~(record~#2). }
\msg_new:nnn {tenkz}{kernel-frame-word}
  { [TKZ-FRAME-WORD]~frame~word~'#1'~is~not~flat|plane|circle. }
\msg_new:nnn {tenkz}{kernel-circle-open-direction}
  { [TKZ-CIRCLE-OPEN-DIRECTION]~circle~frame~does~not~define~diagonal~open~
    direction~'#3'~at~the~#2~end~of~wire~'#1';~use~n|e|s|w. }
\msg_new:nnn {tenkz}{kernel-open-transverse-frame}
  { [TKZ-OPEN-TRANSVERSE-FRAME]~open~direction~'#3'~at~the~#2~end~of~
    wire~'#1'~follows~the~independent~transverse~physical~axis,~which~
    only~a~plane~frame~declares. }
\msg_new:nnn {tenkz}{kernel-frame-basis-parse}
  { [TKZ-FRAME-BASIS-PARSE]~basis~member~'#1'~must~be~
    '<row~kind>~at~(<integer>,<integer>)'. }
\msg_new:nnn {tenkz}{kernel-frame-basis-kind}
  { [TKZ-FRAME-BASIS-KIND]~basis~member~kind~'#1'~is~not~
    wire|ket|op|bra. }
\msg_new:nnn {tenkz}{kernel-frame-member-range}
  { [TKZ-FRAME-MEMBER-RANGE]~member~#3~of~cell~(#1,#2)~does~not~exist. }
\msg_new:nnn {tenkz}{kernel-frame-basis-grid}
  { [TKZ-FRAME-BASIS-GRID]~bonds=grid~requires~the~origin~singleton~basis;~
    use~bonds=none~and~declare~multi-member~topology~with~wires. }
\msg_new:nnn {tenkz}{kernel-frame-basis-scope}
  { [TKZ-FRAME-BASIS-SCOPE]~basis=~is~picture-scoped~in~this~kernel~stage. }
\msg_new:nnn {tenkz}{kernel-metrics-scope}
  { [TKZ-METRICS-SCOPE]~metrics=~is~picture~policy:~a~group~shares~its~
    picture's~metric~context. }
\msg_new:nnn {tenkz}{kernel-check-twice}
  { [TKZ-EQ-CHECK-TWICE]~relation~#1~is~opted~out~twice;~one~relation~
    carries~one~waiver~and~one~reason. }
\msg_new:nnn {tenkz}{kernel-check-bar}
  { [TKZ-EQ-CHECK-BAR]~the~reason~for~relation~#1~carries~the~record~
    stream's~own~field~separator;~a~reason~is~prose~and~cannot~hold~one. }
\msg_new:nnn {tenkz}{kernel-check-off}
  { [TKZ-EQ-CHECK-OFF]~an~opt-out~reads~off={<relation>:~<reason>};~got~'#1'. }
\msg_new:nnn {tenkz}{kernel-check-word}
  { [TKZ-EQ-CHECK-WORD]~the~audit~does~not~know~'#1';~check=~states~the~
    audit~(signature)~and~its~recorded~opt-outs~
    (off={<relation>:~<reason>}). }
\msg_new:nnn {tenkz}{kernel-check-relation}
  { [TKZ-EQ-CHECK-RELATION]~relation~#1~is~opted~out~of~the~audit,~but~the~
    equation~has~#2~relation(s);~an~opt-out~names~a~relation~the~equation~
    performs. }
\msg_new:nnn {tenkz}{kernel-check-reason}
  { [TKZ-EQ-CHECK-REASON]~relation~#1~is~opted~out~of~the~audit~with~no~
    reason;~the~opt-out~records~the~mathematics~the~diagram~does~not~draw,~
    and~an~opt-out~without~one~is~not~an~opt-out. }
\msg_new:nnn {tenkz}{kernel-metrics-panel}
  { [TKZ-METRICS-PANEL]~metrics=~is~the~equation's~in~an~equation:~its~
    panels~share~one~pitch,~and~a~panel~naming~a~profile~of~its~own~would~
    scale~one~side~of~the~equality~and~not~the~other. }
\msg_new:nnn {tenkz}{kernel-size-scope}
  { [TKZ-SIZE-SCOPE]~size=~is~picture~and~equation~policy:~a~group~shares~
    its~picture's~size~class,~and~an~atom~states~its~own. }
\msg_new:nnn {tenkz}{kernel-frame-basis-circle}
  { [TKZ-FRAME-BASIS-CIRCLE]~basis=~is~cell-level~and~is~defined~on~flat~
    and~plane~frames;~a~circle~frame~has~no~repeating~cell~to~decompose. }
\msg_new:nnn {tenkz}{kernel-frame-basis-policy}
  { [TKZ-FRAME-BASIS-POLICY]~displaced~or~multi-member~bases~require~
    explicit~wires;~picture~policy~'#1'~has~no~member-level~meaning. }
\msg_new:nnn {tenkz}{kernel-physical-axis-host}
  { [TKZ-PHYSICAL-AXIS-HOST]~physical~#1~at~cell~(#2,#3)~has~no~
    live~frame~atom. }
\msg_new:nnnn {tenkz}{kernel-frame-basis-spacing}
  { [TKZ-FRAME-BASIS-SPACING]~basis-member~#1~has~centre~separation~
    #2~pitch;~the~conservative~default-bead~support~requirement~is~#3~
    pitch,~with~signed~margin~#4~pitch. }
  { The~requirement~is~the~two~page-space~bead~supports~on~the~centre~
    bearing~plus~named~daylight.~The~signed~margin~is~raw~separation~
    minus~requirement~and~is~rounded~independently.~The~records~remain~
    distinct~and~no~edge~is~inferred.~Choose~basis~offsets~whose~
    translated~sites~clear~one~another. }
\msg_new:nnn {tenkz}{kernel-frame-member-occupied}
  { [TKZ-FRAME-MEMBER-OCCUPIED]~member~(#1,#2,#3)~is~already~claimed~
    by~record~#4. }
\msg_new:nnn {tenkz}{kernel-picture-positive}
  { [TKZ-PIC-POSITIVE-INTEGER]~#1=#2~must~be~a~positive~integer. }
\msg_new:nnn {tenkz}{kernel-atom-positive}
  { [TKZ-ATOM-POSITIVE-INTEGER]~#1=#2~must~be~a~positive~integer~
    (record~#3). }
\msg_new:nnn {tenkz}{kernel-name-collision}
  { [TKZ-LANG-NAME-COLLISION]~generated~name~'#1'~already~belongs~to~
    record~#2 }
\msg_new:nnn {tenkz}{kernel-name-reserved}
  { [TKZ-LANG-NAME-RESERVED]~name~'#1'~is~reserved~by~the~address~grammar~
    (record~#2). }
\msg_new:nnn {tenkz}{kernel-skin-cluster}
  { [TKZ-SKIN-PAIRING-CLUSTER]~declared~skin~'#1'~carries~pairings,~
    but~cluster~carrier~#2~is~a~group~with~no~glyph~or~own~ports. }
\msg_new:nnn {tenkz}{kernel-port-cluster}
  { [TKZ-PORT-CLUSTER]~cluster~carrier~#1~is~a~group~with~no~glyph~
    or~authored~ports;~attach~wires~to~its~addressable~members. }
\msg_new:nnn {tenkz}{kernel-glyph-face-cluster}
  { [TKZ-GLYPH-FACE-CLUSTER]~cluster~carrier~#1~is~a~group~with~no~
    glyph~face;~query~one~of~its~addressable~members. }
\msg_new:nnn {tenkz}{kernel-glyph-face-none}
  { [TKZ-GLYPH-FACE-NONE]~record~#1~has~skin=none~and~no~declared~
    pairing~support,~so~it~has~no~glyph~face. }
\msg_new:nnn {tenkz}{kernel-leg-plan-vector}
  { [TKZ-LEG-PLAN-VECTOR]~physical-leg~corridor~'#1'~has~a~zero~
    outward~direction. }
\msg_new:nnn {tenkz}{kernel-leg-plan-basis}
  { [TKZ-LEG-PLAN-BASIS]~physical-leg~corridor~'#1'~was~redeclared~
    with~a~different~page-space~basis. }
\msg_new:nnn {tenkz}{kernel-leg-plan-corridor}
  { [TKZ-LEG-PLAN-CORRIDOR]~semantic~leg~'#1'~names~undeclared~
    corridor~'#2'. }
\msg_new:nnn {tenkz}{kernel-leg-plan-face}
  { [TKZ-LEG-PLAN-FACE]~semantic~leg~'#1'~was~first~enrolled~at~#2,~
    then~re-enrolled~at~#3. }
\msg_new:nnn {tenkz}{kernel-leg-plan-reach}
  { [TKZ-LEG-PLAN-REACH]~semantic~leg~'#1'~has~negative~face-relative~
    reach~#2. }
\msg_new:nnn {tenkz}{kernel-skin-pairings-parse}
  { [TKZ-SKIN-PAIRING-PARSE]~pairings~on~declared~skin~'#1'~must~be~
    one~braced~port-pair-list. }
\msg_new:nnn {tenkz}{kernel-cell-port-bearing-alias}
  { [TKZ-CELL-PORT-BEARING-ALIAS]~paired~cell~port~#1~receives~#2~and~#3;~
    one~cell~lane~cannot~represent~both~bearings. }
\msg_new:nnn {tenkz}{kernel-skin-pairing-cross-parse}
  { [TKZ-SKIN-PAIRING-CROSS-PARSE]~pairing~cross~item~'#1'~on~atom~#2~
    must~be~'<pairing-number>:~<crossing-declaration>'. }
\msg_new:nnn {tenkz}{kernel-skin-pairing-cross-index}
  { [TKZ-SKIN-PAIRING-CROSS-INDEX]~pairing~cross~on~atom~#1~names~
    pairing~#2,~but~the~skin~declares~only~#3~pairing(s). }

% ---------- kernel state -----------------------------------------------------

\int_new:N  \g__tenkz_kernel_picture_int      % kernel picture serial (events)
\int_new:N  \g__tenkz_kernel_group_int        % group frame serial
\int_new:N  \l__tenkz_kernel_row_int          % chain cursor
\int_new:N  \l__tenkz_kernel_col_int
\int_new:N  \l__tenkz_kernel_row_advance_int  % maximum implicit span on current row
\int_new:N  \l__tenkz_kernel_node_int         % address node serial
\__tenkz_prop_new_indexed:N \l__tenkz_kernel_node_prop        % "id/field" -> value
\__tenkz_prop_new_indexed:N \l__tenkz_kernel_named_prop       % user name -> record id
% Atoms whose labels need the silhouette / pairing / label render split.
\prop_new:N \l__tenkz_kernel_skin_pairing_host_prop
\prop_new:N \l__tenkz_kernel_skin_pairing_cross_prop
\prop_new:N \l__tenkz_kernel_cell_port_pairing_bearing_prop
\prop_new:N \l__tenkz_kernel_policy_prop      % picture policy for late passes
\prop_new:N \l__tenkz_kernel_eq_policy_prop   % tenkzeq policy inherited by panels
\prop_new:N \l__tenkz_kernel_cell_prop        % "r-c" -> atom id (occupancy)
\prop_new:N \l__tenkz_kernel_populated_cell_prop % "r-c" -> populated atom id
\prop_new:N \l__tenkz_kernel_basis_cell_prop  % "r-c-k" -> basis atom id
\prop_new:N \l__tenkz_kernel_basis_claim_prop % "r-c-k" -> authored atom id
\prop_new:N \l__tenkz_kernel_basis_live_prop  % "k/r/c" -> canonical atom id
\prop_new:N \l__tenkz_kernel_basis_live_count_prop % member -> canonical records
\prop_new:N \l__tenkz_kernel_basis_live_in_window_count_prop
\prop_new:N \l__tenkz_kernel_basis_live_record_prop % "k/i" -> "r/c/id"
\prop_new:N \l__tenkz_kernel_basis_kind_prop  % member -> row kind
\prop_new:N \l__tenkz_kernel_basis_east_prop  % member -> quarter-pitch east
\prop_new:N \l__tenkz_kernel_basis_north_prop % member -> quarter-pitch north
\prop_new:N \l__tenkz_kernel_basis_spacing_east_prop  % member -> local east
\prop_new:N \l__tenkz_kernel_basis_spacing_north_prop % member -> local north
\prop_new:N \l__tenkz_kernel_basis_spacing_plan_prop % sparse keys / dense pairs
\prop_new:N \l__tenkz_kernel_basis_spacing_prop % worst distinct-family margin
\prop_new:N \l__tenkz_kernel_trace_cell_prop  % "interface-col" -> traced segment
\prop_new:N \l__tenkz_kernel_open_cell_prop   % "interface-col" -> opened segment
% A plane's physical axis is not one of its authored numeric ports.  Closure
% topology therefore owns the exact host/direction identity directly.
\prop_new:N \l__tenkz_kernel_physical_axis_consumer_prop % "atom/n|s" -> wire
\prop_new:N \g__tenkz_kernel_setup_prop       % setup inherited by later pictures
\bool_new:N \l__tenkz_kernel_trace_physical_bool
\bool_new:N \l__tenkz_kernel_strict_bool
\bool_new:N \l__tenkz_kernel_ineq_bool        % inside tenkzeq
\bool_new:N \l__tenkz_kernel_claim_collision_bool
\bool_new:N \l__tenkz_kernel_basis_valid_bool
\bool_new:N \l__tenkz_kernel_basis_member_live_bool
\bool_new:N \l__tenkz_kernel_basis_spacing_bool
\bool_new:N \l__tenkz_kernel_basis_spacing_realized_bool
\bool_new:N \l__tenkz_kernel_frame_valid_bool
\bool_new:N \l__tenkz_kernel_group_keys_bool
\int_new:N  \l__tenkz_kernel_basis_count_int
\int_new:N  \l__tenkz_kernel_basis_spacing_a_int
\int_new:N  \l__tenkz_kernel_basis_spacing_b_int
\int_new:N  \l__tenkz_kernel_basis_spacing_dr_int
\int_new:N  \l__tenkz_kernel_basis_spacing_dc_int
\int_new:N  \l__tenkz_kernel_basis_spacing_a_count_int
\int_new:N  \l__tenkz_kernel_basis_spacing_b_count_int
\int_new:N  \l__tenkz_kernel_basis_spacing_a_row_int
\int_new:N  \l__tenkz_kernel_basis_spacing_a_col_int
\int_new:N  \l__tenkz_kernel_basis_spacing_b_row_int
\int_new:N  \l__tenkz_kernel_basis_spacing_b_col_int
\int_new:N  \l__tenkz_kernel_basis_spacing_translation_count_int
\int_new:N  \l__tenkz_kernel_basis_spacing_scan_member_int
\int_new:N  \l__tenkz_kernel_basis_spacing_target_member_int
\int_new:N  \l__tenkz_kernel_basis_spacing_scan_sign_int
\int_new:N  \l__tenkz_kernel_basis_spacing_overlap_int
\int_new:N  \l__tenkz_kernel_basis_spacing_holes_int
\fp_const:Nn \c__tenkz_kernel_basis_step_fp { 1 / 4 }
\int_new:N  \g__tenkz_kernel_eq_scope_int     % owner id for pictures/checks
\clist_new:N \l__tenkz_kernel_check_off_clist % relations opted out of check=
\seq_new:N  \l__tenkz_kernel_check_reason_seq % their written reasons, in order
\int_new:N  \l__tenkz_kernel_check_index_int
\bool_new:N \l__tenkz_kernel_check_first_bool
\tl_new:N   \l__tenkz_kernel_scratch_tl
\tl_new:N   \l__tenkz_kernel_sig_tl
\tl_new:N   \l__tenkz_kernel_size_tl
\tl_new:N   \l__tenkz_kernel_eq_size_tl
\tl_new:N   \l__tenkz_kernel_check_relation_tl
\tl_new:N   \l__tenkz_kernel_check_reason_tl
\tl_new:N   \l__tenkz_kernel_slot_tl
\tl_new:N   \l__tenkz_kernel_child_tl
\tl_new:N   \l__tenkz_kernel_parent_tl
\tl_new:N   \l__tenkz_kernel_claim_owner_tl
\tl_new:N   \l__tenkz_kernel_basis_live_id_tl
\tl_new:N   \l__tenkz_kernel_basis_live_key_tl
\tl_new:N   \l__tenkz_kernel_basis_live_record_tl
\tl_new:N   \l__tenkz_kernel_basis_spacing_a_id_tl
\tl_new:N   \l__tenkz_kernel_basis_spacing_b_id_tl
\tl_new:N   \l__tenkz_kernel_basis_spacing_target_id_tl
\tl_new:N   \l__tenkz_kernel_basis_spacing_dist_tl
\tl_new:N   \l__tenkz_kernel_basis_spacing_floor_tl
\tl_new:N   \l__tenkz_kernel_basis_spacing_margin_tl
\seq_new:N  \l__tenkz_kernel_stack_seq        % operand stack (see shelf notes)
\seq_new:N  \l__tenkz_kernel_basis_spacing_address_seq
\seq_new:N  \g__tenkz_kernel_panel_gseq       % tenkzeq panel signatures
\seq_new:N  \g__tenkz_kernel_panel_kind_gseq  % diagram or prose per panel
\seq_new:N  \g__tenkz_kernel_rel_gseq         % tenkzeq relation glyphs
\seq_new:N  \g__tenkz_kernel_rel_pos_gseq     % panels closed before each relation

% One equation, one measure.  An equation says its panels are one object
% written two ways, so the names inscribed in them are set on a single
% measure and their glyphs come out the same size: an operator and its
% inverse are drawn alike, whatever the two names happen to measure
% (LANGUAGE-1.0 section 7.1).  The equation learns that measure by running
% its own body once with the event stream silent and the ink thrown away,
% reading the widest and deepest inscribed name of each size class, and then
% setting every name of that class on it.  A measuring run is the only way
% the first panel can know what the last one needs, and the measure only
% ever grows a glyph, so no name is ever pushed outside the ink holding it.
\bool_new:N \l__tenkz_kernel_eq_probe_bool    % inside the measuring run
\bool_new:N \l__tenkz_kernel_extent_floor_probe_bool
\prop_new:N \g__tenkz_kernel_eq_extent_prop   % size class -> "wd,ht,dp"
\box_new:N  \l__tenkz_kernel_eq_probe_box
\box_new:N  \l__tenkz_kernel_eq_name_box
\int_new:N  \l__tenkz_kernel_eq_saved_picture_int
\int_new:N  \l__tenkz_kernel_eq_saved_scope_int
\int_new:N  \l__tenkz_kernel_eq_saved_group_int
\seq_new:N  \l__tenkz_kernel_eq_saved_seq
\tl_new:N   \l__tenkz_kernel_eq_extent_key_tl
\tl_new:N   \l__tenkz_kernel_eq_extent_tl
\dim_new:N  \l__tenkz_kernel_eq_extent_wd_dim
\dim_new:N  \l__tenkz_kernel_eq_extent_ht_dim
\dim_new:N  \l__tenkz_kernel_eq_extent_dp_dim

% Key trees fill one staging table; each command copies it onto its record
% and clears it, so one tree serves every use site.
\prop_new:N \l__tenkz_kernel_stage_prop
\prop_new:N \l__tenkz_kernel_sugar_active_prop
\prop_new:N \l__tenkz_kernel_sugar_saved_prop
\tl_new:N \l__tenkz_kernel_sugar_current_tl

\cs_new_protected:Npn \__tenkz_kernel_stage_put:nn #1#2
  { \prop_put:Nnn \l__tenkz_kernel_stage_prop {#1} {#2} }
\cs_new_protected:Npn \__tenkz_kernel_stage_put:ne #1#2
  { \prop_put:Nne \l__tenkz_kernel_stage_prop {#1} {#2} }

% A sugar flag owns only the fields written by its own expansion.  Its first
% true spelling saves the preceding values; a later false restores them.  A
% direct author spelling relinquishes that ownership and therefore survives a
% subsequent false.
\cs_new_protected:Npn \__tenkz_kernel_sugar_save:nn #1#2
  {
    \prop_get:NnN
      \l__tenkz_kernel_stage_prop {#2} \l__tenkz_kernel_scratch_tl
    \quark_if_no_value:NTF \l__tenkz_kernel_scratch_tl
      {
        \prop_put:Nnn
          \l__tenkz_kernel_sugar_saved_prop {#1/#2} { \q_no_value }
      }
      {
        \prop_put:NnV
          \l__tenkz_kernel_sugar_saved_prop {#1/#2}
          \l__tenkz_kernel_scratch_tl
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_sugar_restore:nn #1#2
  {
    \prop_get:NnN
      \l__tenkz_kernel_sugar_saved_prop {#1/#2}
      \l__tenkz_kernel_scratch_tl
    \quark_if_no_value:NTF \l__tenkz_kernel_scratch_tl
      { \prop_remove:Nn \l__tenkz_kernel_stage_prop {#2} }
      {
        \prop_put:NnV
          \l__tenkz_kernel_stage_prop {#2} \l__tenkz_kernel_scratch_tl
      }
    \prop_remove:Nn \l__tenkz_kernel_sugar_saved_prop {#1/#2}
  }
\cs_new_protected:Npn \__tenkz_kernel_sugar_abandon:nn #1#2
  {
    \tl_if_eq:NnF \l__tenkz_kernel_sugar_current_tl {#1}
      {
        \prop_remove:Nn \l__tenkz_kernel_sugar_active_prop {#1}
        \clist_map_inline:nn {#2}
          {
            \prop_remove:Nn
              \l__tenkz_kernel_sugar_saved_prop {#1/##1}
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_sugar_clear:
  {
    \prop_clear:N \l__tenkz_kernel_sugar_active_prop
    \prop_clear:N \l__tenkz_kernel_sugar_saved_prop
    \tl_clear:N \l__tenkz_kernel_sugar_current_tl
  }

% The contract writes cell addresses unbraced (`at=(1,2)` and
% `at=(1,2,3)`).  A keyval comma splitter would tear them, so every option
% list passes through this door, which braces each address before l3keys
% reads it.  Members go first: the pair pattern must not consume their prefix.
\tl_new:N \l__tenkz_kernel_opts_tl
\seq_new:N \l__tenkz_kernel_diagnostic_context_seq
\cs_new_protected:Npn \__tenkz_kernel_keys:nn #1#2
  {
    \tl_set:Nn \l__tenkz_kernel_opts_tl {#2}
    % A panel option list standing in an equation body was swept by the
    % relation rewrite, so its key separators arrive wearing the relation
    % marker; give each one back the plain equals the keyval and value
    % parsers read.
    \bool_if:NT \l__tenkz_kernel_ineq_bool
      { \__tenkz_kernel_revert_rels:N \l__tenkz_kernel_opts_tl }
    \regex_replace_all:nnN
      { \( \s* (\d+) \s* , \s* (\d+) \s* , \s* (\d+) \s* \) }
      { \cB\{ \( \1 , \2 , \3 \) \cE\} }
      \l__tenkz_kernel_opts_tl
    \regex_replace_all:nnN
      { \( \s* (\d+) \s* , \s* (\d+) \s* \) }
      { \cB\{ \( \1 , \2 \) \cE\} }
      \l__tenkz_kernel_opts_tl
    \keys_set:nV {#1} \l__tenkz_kernel_opts_tl
  }
% Picture, equation, setup, and declaration options are read before they own
% a model record.  Such a parse pushes the scope it can truthfully name;
% body and group parses leave the stack empty and name their minted record.
% A recoverable diagnostic returns through \keys_set:nV, so the pop also
% restores an enclosing context after a refusal.
\cs_new:Npn \__tenkz_kernel_diagnostic_context:
  {
    \seq_if_empty:NTF \l__tenkz_kernel_diagnostic_context_seq
      { \tl_use:N \l__tenkz_model_last_tl }
      { \seq_item:Nn \l__tenkz_kernel_diagnostic_context_seq {1} }
  }
\cs_new_protected:Npn \__tenkz_kernel_context_keys:nnn #1#2#3
  {
    \seq_push:Nn \l__tenkz_kernel_diagnostic_context_seq {#1}
    \__tenkz_kernel_keys:nn {#2} {#3}
    \seq_pop:NN \l__tenkz_kernel_diagnostic_context_seq
      \l__tenkz_kernel_scratch_tl
  }

% The package event writer, reachable whatever the catcode of @ is at load.
\cs_new_protected:Npn \__tenkz_kernel_event:n #1
  { \use:c { tenkz@event } {#1} }
\cs_new_eq:Nc \__tenkz_kernel_event_writer:n {tenkz@event}
\cs_new_protected:Npn \__tenkz_kernel_event_suppress:n #1 { }

% ---------- the address grammar ---------------------------------------------
% One parser, nine productions.  Each handler quotes its row of the grammar
% table, and the anchored patterns are checkable against that table by eye.
% Operands of the composite productions are themselves addresses; an operand
% that contains the word `and` is braced, and one brace pair is stripped
% before recursion.  Recursion discipline: every value that must survive a
% recursive call is pushed on the operand stack first -- no scratch register
% lives across recursion.
%
% Parsing produces an address NODE: a stable id whose fields hold the
% production and its resolved operands, plus the author's raw spelling --
% events and errors speak the author's text, never an internal id alone.

\cs_new_protected:Npn \__tenkz_kernel_node_new:nN #1#2
  {
    \int_incr:N \l__tenkz_kernel_node_int
    \tl_set:Ne #2 { addr - \int_use:N \l__tenkz_kernel_node_int }
    \prop_put:Nen \l__tenkz_kernel_node_prop { #2 / text } {#1}
  }
\cs_new_protected:Npn \__tenkz_kernel_node_put:Nnn #1#2#3
  { \prop_put:Nen \l__tenkz_kernel_node_prop { #1 / #2 } {#3} }
\cs_generate_variant:Nn \__tenkz_kernel_node_put:Nnn { NnV, Nne }
\cs_new_protected:Npn \__tenkz_kernel_node_port_init:N #1
  {
    \__tenkz_kernel_node_put:Nnn #1 {kind} {port}
    \__tenkz_kernel_node_put:Nnn #1 {port-type} {virtual}
  }
\cs_new:Npn \__tenkz_kernel_node_item:Nn #1#2
  { \prop_item:Ne \l__tenkz_kernel_node_prop { #1 / #2 } }
% the same reading when the node id arrives as text rather than in a variable
\cs_new:Npn \__tenkz_kernel_node_item:nn #1#2
  { \prop_item:Ne \l__tenkz_kernel_node_prop { #1 / #2 } }

% \__tenkz_kernel_addr:nN {address text} \node-id-out
\cs_new_protected:Npn \__tenkz_kernel_addr:nN #1#2
  {
    \tl_set:Nn \l__tenkz_kernel_scratch_tl {#1}
    \tl_trim_spaces:N \l__tenkz_kernel_scratch_tl
    \exp_args:NV \__tenkz_kernel_addr_try:nN \l__tenkz_kernel_scratch_tl #2
  }
\cs_generate_variant:Nn \__tenkz_kernel_addr:nN { eN, VN }

\seq_new:N \l__tenkz_kernel_match_seq
\cs_new_protected:Npn \__tenkz_kernel_addr_try:nN #1#2
  {
    % production 1b: (r,c,k) -- member k of one frame cell
    \regex_extract_once:nnNTF
      { \A \( \s* (\d+) \s* , \s* (\d+) \s* , \s* (\d+) \s* \) \Z } {#1}
      \l__tenkz_kernel_match_seq
      { \__tenkz_kernel_addr_member:N #2 }
      {
    % production 1: (r,c) -- cell of the current frame
    \regex_extract_once:nnNTF
      { \A \( \s* (\d+) \s* , \s* (\d+) \s* \) \Z } {#1}
      \l__tenkz_kernel_match_seq
      { \__tenkz_kernel_addr_cell:N #2 }
      {
    % production 10: a .. b -- a selector range.  Current selectors resolve
    % cell corners; wire places and frame stations are not yet included.  It is
    % tried before every production whose tail would swallow the joiner and
    % is spelt with a range, not a hyphen: a cluster member is named `a-2-2`.
    \regex_extract_once:nnNTF
      { \A (.+?) \s* \.\. \s* (.+) \Z } {#1}
      \l__tenkz_kernel_match_seq
      { \__tenkz_kernel_addr_two:nnN {#1} {range} #2 }
      {
    % production 4: n <dir> of a -- n pitch steps from a
    \regex_extract_once:nnNTF
      { \A ([0-9.]+) \s+ (nw|ne|sw|se|n|e|s|w) \s+ of \s+ (.+) \Z } {#1}
      \l__tenkz_kernel_match_seq
      { \__tenkz_kernel_addr_rel:nN {#1} #2 }
      {
    % production 5: midway a and b -- midpoint
    \regex_extract_once:nnNTF
      { \A midway \s+ (.+) \s+ and \s+ (.+) \Z } {#1}
      \l__tenkz_kernel_match_seq
      { \__tenkz_kernel_addr_two:nnN {#1} {mid} #2 }
      {
    % production 7: crossing of a and b -- the declared intersection
    \regex_extract_once:nnNTF
      { \A crossing \s+ of \s+ (.+) \s+ and \s+ (.+) \Z } {#1}
      \l__tenkz_kernel_match_seq
      { \__tenkz_kernel_addr_two:nnN {#1} {crossing} #2 }
      {
    % production 8: leg <face> of <cell> -- the policy-generated leg wire
    \regex_extract_once:nnNTF
      { \A leg \s+ (n|e|s|w) \s+ of \s+ (.+) \Z } {#1}
      \l__tenkz_kernel_match_seq
      { \__tenkz_kernel_addr_leg:nN {#1} #2 }
      {
    % production 6: on w t -- fraction t along wire w
    \regex_extract_once:nnNTF
      { \A on \s+ (\S+) \s+ ([0-9.]+) \Z } {#1}
      \l__tenkz_kernel_match_seq
      { \__tenkz_kernel_addr_onwire:nN {#1} #2 }
      {
    % production 9: <compass> outside -- a point on the picture margin
    \regex_extract_once:nnNTF
      { \A (nw|ne|sw|se|n|e|s|w) \s+ outside \Z } {#1}
      \l__tenkz_kernel_match_seq
      { \__tenkz_kernel_addr_outside:nN {#1} #2 }
      {
    % production 3: a.θ@k -- the port at angle θ on a, slot k; @1 when the
    % face has one.  A face is an angle in the record's own axes, and the
    % four compass words name the four right angles of that circle.
    \regex_extract_once:nnNTF
      { \A ([A-Za-z][A-Za-z0-9\-]*) \.
           ( [-+]? (?: [0-9]+ (?: \. [0-9]* )? | \. [0-9]+ ) | n|e|s|w )
           (?: @ (\d+) )? \Z } {#1}
      \l__tenkz_kernel_match_seq
      { \__tenkz_kernel_addr_port:nN {#1} #2 }
      {
    % Generated skin pairings have an author-facing crossing name.  `self'
    % denotes the wire whose crossing field is being validated; the indexed
    % spelling resolves through an author-named host.
    \regex_extract_once:nnNTF
      { \A pairing \s+ ([1-9]\d*) \s+ of \s+
        ([A-Za-z][A-Za-z0-9\-]*) \Z } {#1}
      \l__tenkz_kernel_match_seq
      { \__tenkz_kernel_addr_pairing:N #2 }
      {
    \str_if_eq:nnTF {#1} {self}
      { \__tenkz_kernel_addr_self:N #2 }
      {
    % production 2: a -- named record
    \regex_match:nnTF { \A [A-Za-z][A-Za-z0-9\-]* \Z } {#1}
      { \__tenkz_kernel_addr_record:nN {#1} #2 }
      {
        \msg_error:nnee {tenkz}{kernel-address} {#1}
          { \tl_use:N \l__tenkz_model_last_tl }
        \__tenkz_kernel_node_new:nN {#1} #2
      }
      } } } } } } } } } } } }
  }

% capture groups sit at seq items 2..n; item 1 is the full match.

\cs_new_protected:Npn \__tenkz_kernel_addr_cell:N #1
  {
    \__tenkz_kernel_node_new:nN
      {
        ( \int_eval:n { \seq_item:Nn \l__tenkz_kernel_match_seq {2} } ,
          \int_eval:n { \seq_item:Nn \l__tenkz_kernel_match_seq {3} } )
      } #1
    \__tenkz_kernel_node_put:Nne #1 {kind} {cell}
    \__tenkz_kernel_node_put:Nne #1 {row}
      { \int_eval:n { \seq_item:Nn \l__tenkz_kernel_match_seq {2} } }
    \__tenkz_kernel_node_put:Nne #1 {col}
      { \int_eval:n { \seq_item:Nn \l__tenkz_kernel_match_seq {3} } }
  }

\cs_new_protected:Npn \__tenkz_kernel_addr_member:N #1
  {
    \__tenkz_kernel_node_new:nN
      {
        ( \int_eval:n { \seq_item:Nn \l__tenkz_kernel_match_seq {2} } ,
          \int_eval:n { \seq_item:Nn \l__tenkz_kernel_match_seq {3} } ,
          \int_eval:n { \seq_item:Nn \l__tenkz_kernel_match_seq {4} } )
      } #1
    \__tenkz_kernel_node_put:Nne #1 {kind} {member}
    \__tenkz_kernel_node_put:Nne #1 {row}
      { \int_eval:n { \seq_item:Nn \l__tenkz_kernel_match_seq {2} } }
    \__tenkz_kernel_node_put:Nne #1 {col}
      { \int_eval:n { \seq_item:Nn \l__tenkz_kernel_match_seq {3} } }
    \__tenkz_kernel_node_put:Nne #1 {member}
      { \int_eval:n { \seq_item:Nn \l__tenkz_kernel_match_seq {4} } }
  }

% Relative placement: the compass word converts to a page direction once,
% here at the parse boundary; the base operand resolves recursively.
\cs_new_protected:Npn \__tenkz_kernel_addr_rel:nN #1#2
  {
    \__tenkz_kernel_node_new:nN {#1} #2
    \__tenkz_kernel_node_put:Nne #2 {kind} {rel}
    \__tenkz_kernel_node_put:Nne #2 {steps}
      { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
    \__tenkz_kernel_node_put:Nne #2 {dir}
      { \seq_item:Nn \l__tenkz_kernel_match_seq {3} }
    \seq_push:NV \l__tenkz_kernel_stack_seq #2
    \tl_set:Ne \l__tenkz_kernel_scratch_tl
      { \seq_item:Nn \l__tenkz_kernel_match_seq {4} }
    \__tenkz_kernel_addr_operand:VN \l__tenkz_kernel_scratch_tl
      \l__tenkz_kernel_scratch_tl
    \tl_set:NV \l__tenkz_kernel_child_tl \l__tenkz_kernel_scratch_tl
    \seq_pop:NN \l__tenkz_kernel_stack_seq #2
    \__tenkz_kernel_node_put:NnV #2 {base} \l__tenkz_kernel_child_tl
  }

% midway and crossing share one two-operand shape; the kind field tells the
% resolver which mathematics applies.
%
% The out variable may alias a caller scratch register -- an atom's place
% arrives in the same register the general-purpose temporaries use -- so the
% pending operand is held in a register of this production's own.  Holding it
% in a shared temporary made the last pop, which restores the node id,
% overwrite the first operand with the node itself, and both two-operand
% productions then resolved their first operand as their own place.
\tl_new:N \l__tenkz_kernel_operand_tl
\cs_new_protected:Npn \__tenkz_kernel_addr_two:nnN #1#2#3
  {
    \__tenkz_kernel_node_new:nN {#1} #3
    \__tenkz_kernel_node_put:Nnn #3 {kind} {#2}
    % both operand texts and the node id go to the stack before recursion
    \seq_push:NV \l__tenkz_kernel_stack_seq #3
    \seq_push:Ne \l__tenkz_kernel_stack_seq
      { \seq_item:Nn \l__tenkz_kernel_match_seq {3} }
    \tl_set:Ne \l__tenkz_kernel_scratch_tl
      { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
    \__tenkz_kernel_addr_operand:VN \l__tenkz_kernel_scratch_tl
      \l__tenkz_kernel_scratch_tl
    % first child resolved; swap it against the pending second operand
    \seq_pop:NN \l__tenkz_kernel_stack_seq \l__tenkz_kernel_operand_tl
    \seq_push:NV \l__tenkz_kernel_stack_seq \l__tenkz_kernel_scratch_tl
    \__tenkz_kernel_addr_operand:VN \l__tenkz_kernel_operand_tl
      \l__tenkz_kernel_scratch_tl
    \tl_set:NV \l__tenkz_kernel_child_tl \l__tenkz_kernel_scratch_tl
    \seq_pop:NN \l__tenkz_kernel_stack_seq
      \l__tenkz_kernel_operand_tl                       % first child id
    \seq_pop:NN \l__tenkz_kernel_stack_seq #3           % the node id
    \__tenkz_kernel_node_put:NnV #3 {first} \l__tenkz_kernel_operand_tl
    \__tenkz_kernel_node_put:NnV #3 {second} \l__tenkz_kernel_child_tl
  }

% leg <face> of <cell>: the operand must be a cell -- only cells carry
% policy legs.
\cs_new_protected:Npn \__tenkz_kernel_addr_leg:nN #1#2
  {
    \__tenkz_kernel_node_new:nN {#1} #2
    \__tenkz_kernel_node_put:Nne #2 {kind} {leg}
    \__tenkz_kernel_node_put:Nne #2 {face}
      { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
    \seq_push:NV \l__tenkz_kernel_stack_seq #2
    \tl_set:Ne \l__tenkz_kernel_scratch_tl
      { \seq_item:Nn \l__tenkz_kernel_match_seq {3} }
    \__tenkz_kernel_addr_operand:VN \l__tenkz_kernel_scratch_tl
      \l__tenkz_kernel_scratch_tl
    \tl_set:NV \l__tenkz_kernel_child_tl \l__tenkz_kernel_scratch_tl
    \seq_pop:NN \l__tenkz_kernel_stack_seq \l__tenkz_kernel_parent_tl
    \prop_get:NeN \l__tenkz_kernel_node_prop
      { \l__tenkz_kernel_child_tl / kind } \l_tmpa_tl
    \str_if_eq:VnTF \l_tmpa_tl {cell}
      {
        \__tenkz_kernel_node_put:NnV
          \l__tenkz_kernel_parent_tl {cell} \l__tenkz_kernel_child_tl
      }
      {
        \msg_error:nneeee {tenkz}{kernel-address-operand}
          { \__tenkz_kernel_node_item:Nn \l__tenkz_kernel_child_tl {text} }
          {#1} { a~cell }
          { \tl_use:N \l__tenkz_kernel_parent_tl }
      }
    \tl_set:NV #2 \l__tenkz_kernel_parent_tl
  }

% A compass word is sugar for a right angle, converted here at the parse
% boundary; downstream a face is a number and nothing asks which spelling
% produced it.
\cs_new:Npn \__tenkz_kernel_face_angle:n #1
  {
    \str_case:enF {#1}
      { {n}{90} {e}{0} {s}{270} {w}{180} }
      { \fp_eval:n { #1 } }
  }

\cs_new_protected:Npn \__tenkz_kernel_addr_port:nN #1#2
  {
    \__tenkz_kernel_node_new:nN {#1} #2
    \__tenkz_kernel_node_port_init:N #2
    \__tenkz_kernel_node_put:Nne #2 {record}
      { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
    \__tenkz_kernel_node_put:Nne #2 {face}
      {
        \__tenkz_kernel_face_angle:n
          { \seq_item:Nn \l__tenkz_kernel_match_seq {3} }
      }
    % the slot suffix is optional when the face carries one slot; the out
    % variable may alias a caller scratch register, so nothing else writes it
    \int_compare:nNnTF { \seq_count:N \l__tenkz_kernel_match_seq } > { 3 }
      {
        \tl_set:Ne \l__tenkz_kernel_slot_tl
          { \seq_item:Nn \l__tenkz_kernel_match_seq {4} }
        \tl_if_blank:VTF \l__tenkz_kernel_slot_tl
          { \__tenkz_kernel_node_put:Nnn #2 {slot} {1} }
          { \__tenkz_kernel_node_put:NnV #2 {slot} \l__tenkz_kernel_slot_tl }
      }
      { \__tenkz_kernel_node_put:Nnn #2 {slot} {1} }
  }

\cs_new_protected:Npn \__tenkz_kernel_addr_record:nN #1#2
  {
    \__tenkz_kernel_node_new:nN {#1} #2
    \__tenkz_kernel_node_put:Nne #2 {kind} {record}
    \__tenkz_kernel_node_put:Nnn #2 {name} {#1}
  }

\cs_new_protected:Npn \__tenkz_kernel_addr_pairing:N #1
  {
    \prop_get:NeN \l__tenkz_kernel_named_prop
      { \seq_item:Nn \l__tenkz_kernel_match_seq {3} }
      \l__tenkz_kernel_scratch_tl
    \exp_args:Ne \__tenkz_kernel_addr_record:nN
      {
        skin-\l__tenkz_kernel_scratch_tl-
        \seq_item:Nn \l__tenkz_kernel_match_seq {2}
      }
      #1
  }

\cs_new_protected:Npn \__tenkz_kernel_addr_self:N #1
  {
    \__tenkz_model_get:nnN
      { \l__tenkz_model_last_tl } {name} \l__tenkz_kernel_scratch_tl
    \exp_args:NV \__tenkz_kernel_addr_record:nN
      \l__tenkz_kernel_scratch_tl #1
  }
\cs_new_protected:Npn \__tenkz_kernel_addr_onwire:nN #1#2
  {
    \__tenkz_kernel_node_new:nN {#1} #2
    \__tenkz_kernel_node_put:Nne #2 {kind} {onwire}
    \__tenkz_kernel_node_put:Nne #2 {wire}
      { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
    \__tenkz_kernel_node_put:Nne #2 {t}
      { \seq_item:Nn \l__tenkz_kernel_match_seq {3} }
  }
\cs_new_protected:Npn \__tenkz_kernel_addr_outside:nN #1#2
  {
    \__tenkz_kernel_node_new:nN {#1} #2
    \__tenkz_kernel_node_put:Nne #2 {kind} {outside}
    \__tenkz_kernel_node_put:Nne #2 {side}
      { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
  }

\cs_new_protected:Npn \__tenkz_kernel_strip_outer_groups:N #1
  {
    \tl_if_single:NT #1
      {
        \exp_args:NV \tl_if_head_is_group:nT #1
          {
            \tl_set:Ne #1 { \exp_args:NV \tl_head:n #1 }
            \__tenkz_kernel_strip_outer_groups:N #1
          }
      }
  }

% the braced-operand rule, then recursion
\cs_new_protected:Npn \__tenkz_kernel_addr_operand:nN #1#2
  {
    \tl_set:Nn #2 {#1}
    \tl_trim_spaces:N #2
    \__tenkz_kernel_strip_outer_groups:N #2
    \exp_args:NV \__tenkz_kernel_addr:nN #2 #2
  }
\cs_generate_variant:Nn \__tenkz_kernel_addr_operand:nN { VN }

% ---------- shared key machinery ---------------------------------------------
% One function installs a choice key whose rejection quotes the alphabet;
% each closed alphabet of the contract appears below exactly once.

\tl_new:N \l__tenkz_kernel_list_tl
\tl_new:N \l__tenkz_kernel_list_item_tl
\clist_new:N \l__tenkz_kernel_address_clist
\clist_new:N \l__tenkz_kernel_frame_clist
\tl_new:N \l__tenkz_kernel_frame_tl

% One normalization owns the semantic membership of every address list.
% TeX clists discard empty entries, so callers asking whether a list carries
% a point must inspect this result rather than its raw token spelling.
\cs_new_protected:Npn \__tenkz_kernel_address_list_set:n #1
  {
    \tl_set:Nn \l__tenkz_kernel_list_tl {#1}
    \tl_trim_spaces:N \l__tenkz_kernel_list_tl
    \__tenkz_kernel_strip_outer_groups:N \l__tenkz_kernel_list_tl
    \regex_replace_all:nnN
      { \( \s* (\d+) \s* , \s* (\d+) \s* , \s* (\d+) \s* \) }
      { \cB\{ \( \1 , \2 , \3 \) \cE\} }
      \l__tenkz_kernel_list_tl
    \regex_replace_all:nnN
      { \( \s* (\d+) \s* , \s* (\d+) \s* \) }
      { \cB\{ \( \1 , \2 \) \cE\} }
      \l__tenkz_kernel_list_tl
    \exp_args:NNV \clist_set:Nn
      \l__tenkz_kernel_address_clist \l__tenkz_kernel_list_tl
  }

% Address-valued fields retain the author's spelling in their record, but
% every member is parsed now.  Picture option parsing has already grouped
% cell coordinates before these key handlers run.
\cs_new_protected:Npn \__tenkz_kernel_validate_address_list:n #1
  {
    \__tenkz_kernel_address_list_set:n {#1}
    \clist_map_inline:Nn \l__tenkz_kernel_address_clist
      {
        \__tenkz_kernel_addr_operand:nN
          {##1} \l__tenkz_kernel_list_item_tl
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_validate_crossing_list:n #1
  {
    \tl_set:Nn \l__tenkz_kernel_list_tl {#1}
    \tl_trim_spaces:N \l__tenkz_kernel_list_tl
    \regex_replace_all:nnN
      { \( \s* (\d+) \s* , \s* (\d+) \s* , \s* (\d+) \s* \) }
      { \cB\{ \( \1 , \2 , \3 \) \cE\} }
      \l__tenkz_kernel_list_tl
    \regex_replace_all:nnN
      { \( \s* (\d+) \s* , \s* (\d+) \s* \) }
      { \cB\{ \( \1 , \2 \) \cE\} }
      \l__tenkz_kernel_list_tl
    \exp_args:NNV \clist_set:Nn
      \l__tenkz_kernel_address_clist \l__tenkz_kernel_list_tl
    \clist_map_inline:Nn \l__tenkz_kernel_address_clist
      {
        \regex_extract_once:nnNTF
          { \A \s* (?: over | under ) \s+ at \s+ (.+) \s* \Z } {##1}
          \l__tenkz_kernel_match_seq
          {
            \tl_set:Ne \l__tenkz_kernel_list_item_tl
              { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
            \__tenkz_kernel_addr_operand:VN
              \l__tenkz_kernel_list_item_tl \l__tenkz_kernel_list_item_tl
          }
          {
            \__tenkz_kernel_addr:nN
              {##1} \l__tenkz_kernel_list_item_tl
          }
      }
  }

% route= carries two families.  Three words name a path shape.  The hull form
% `<side> of <selector>' names a curve instead: the wire travels at one
% standing offset from the offset hull of the selection, on the named side,
% entering and leaving where that side's arc ends.  The side is a compass
% word or `all'; `around' is not among them, because the whole picture is a
% selection like any other and one idea keeps one spelling.
\prg_new_protected_conditional:Npnn
  \__tenkz_kernel_route_hull_parse:nNN #1#2#3 {T,TF}
  {
    \tl_clear:N #2
    \tl_clear:N #3
    \regex_extract_once:nnNTF
      { \A \s* (all|n|e|s|w) \s+ of \s+ (.+?) \s* \Z }
      {#1} \l__tenkz_kernel_match_seq
      {
        \tl_set:Ne #2 { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
        \tl_set:Ne #3 { \seq_item:Nn \l__tenkz_kernel_match_seq {3} }
        \prg_return_true:
      }
      { \prg_return_false: }
  }
\cs_new_protected:Npn \__tenkz_kernel_route:n #1
  {
    \tl_set:Nn \l__tenkz_kernel_list_item_tl {#1}
    \tl_trim_spaces:N \l__tenkz_kernel_list_item_tl
    \exp_args:NV \__tenkz_kernel_route_hull_parse:nNNTF
      \l__tenkz_kernel_list_item_tl \l_tmpa_tl \l_tmpb_tl
      { \__tenkz_kernel_route_keep: }
      {
        \str_case:VnF \l__tenkz_kernel_list_item_tl
          {
            {straight} { \__tenkz_kernel_route_keep: }
            {orth}     { \__tenkz_kernel_route_keep: }
            {arc}      { \__tenkz_kernel_route_keep: }
          }
          {
            \msg_error:nneeee {tenkz}{kernel-choice}
              {route} {#1} {straight|orth|arc|<side>~of~<selector>}
              { \tl_use:N \l__tenkz_model_last_tl }
          }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_validate_closed_points:
  {
    \bool_set_false:N \l_tmpa_bool
    % A wind derives its points after language validation; its own parser
    % retains ownership of malformed and contractible wind values.
    \prop_if_in:NnT \l__tenkz_kernel_stage_prop {wind}
      { \bool_set_true:N \l_tmpa_bool }
    \prop_get:NnN \l__tenkz_kernel_stage_prop {via}
      \l__tenkz_kernel_scratch_tl
    \quark_if_no_value:NF \l__tenkz_kernel_scratch_tl
      {
        % Commit expands semantic fields before address validation and route
        % resolution.  Inspect that same value so an expandable empty item
        % cannot masquerade as a waypoint here.
        \tl_set:Ne \l__tenkz_kernel_scratch_tl
          { \tl_use:N \l__tenkz_kernel_scratch_tl }
        \exp_args:NV \__tenkz_kernel_address_list_set:n
          \l__tenkz_kernel_scratch_tl
        \clist_if_empty:NF \l__tenkz_kernel_address_clist
          { \bool_set_true:N \l_tmpa_bool }
      }
    \prop_get:NnN \l__tenkz_kernel_stage_prop {route}
      \l__tenkz_kernel_scratch_tl
    \quark_if_no_value:NF \l__tenkz_kernel_scratch_tl
      {
        \exp_args:NV \__tenkz_kernel_route_hull_parse:nNNT
          \l__tenkz_kernel_scratch_tl \l_tmpa_tl \l_tmpb_tl
          { \bool_set_true:N \l_tmpa_bool }
      }
    \bool_if:NF \l_tmpa_bool
      {
        \msg_error:nne {tenkz}{kernel-closed-points}
          { \tl_use:N \l__tenkz_model_last_tl }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_route_keep:
  {
    \__tenkz_kernel_stage_put:ne {route}
      { \tl_use:N \l__tenkz_kernel_list_item_tl }
  }

% crossing= orders the crossing set a hull route derives: one order for
% every crossing the route makes, stated once instead of once per crossing.
% A free string derives no crossing set, so its crossings answer to cross=
% alone.  `alternate' flips along the order the string meets them, and
% `alternate=under' anchors the first.
\cs_new_protected:Npn \__tenkz_kernel_crossing:n #1
  {
    \tl_set:Nn \l__tenkz_kernel_list_item_tl {#1}
    \tl_trim_spaces:N \l__tenkz_kernel_list_item_tl
    \str_case:VnF \l__tenkz_kernel_list_item_tl
      {
        {over}            { \__tenkz_kernel_crossing_keep: }
        {under}           { \__tenkz_kernel_crossing_keep: }
        {alternate}       { \__tenkz_kernel_crossing_keep: }
        {alternate=over}  { \__tenkz_kernel_crossing_keep: }
        {alternate=under} { \__tenkz_kernel_crossing_keep: }
      }
      {
        \msg_error:nneeee {tenkz}{kernel-choice}
          {crossing} {#1} {over|under|alternate|alternate=under}
          { \tl_use:N \l__tenkz_model_last_tl }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_crossing_keep:
  {
    \__tenkz_kernel_stage_put:ne {crossing}
      { \tl_use:N \l__tenkz_kernel_list_item_tl }
  }

\cs_new_protected:Npn \__tenkz_kernel_choice:nnnn #1#2#3#4
  {
    \keys_define:nn {#1}
      {
        #2 .choices:nn = {#3}
          { \__tenkz_kernel_stage_put:ne {#4} { \l_keys_choice_tl } },
        #2 / unknown .code:n =
          {
            \msg_error:nneeee {tenkz}{kernel-choice} {#2} {##1} {#3}
              { \__tenkz_kernel_diagnostic_context: }
          }
      }
  }
% A word struck from a closed alphabet stays installed as a tombstone: the
% parser still knows the spelling, refuses it, and states the migration.  The
% alphabet error alone cannot do that: it reports the words that remain, which
% reads as a typo when the word was in fact retired.  A tombstone is a
% branch of a key that is already installed, so it opens no parser path of its
% own and the census does not move.
%
% Each branch below answers a row of the registry's tombstone ledger
% (LANGUAGE-1.0 section 10), and the language check holds the two to one list
% in both directions, migration text included: a refusal the ledger does not
% record fails, and a row the parser does not refuse fails.  The registry is
% where a reader and the source lint find the spelling; this is where a
% compile answers for it.
\cs_new_protected:Npn \__tenkz_kernel_tombstone:nnnn #1#2#3#4
  {
    \keys_define:nn {#1}
      {
        #2 / #3 .code:n =
          {
            \msg_error:nneeee {tenkz}{kernel-tombstone} {#2} {#3} {#4}
              { \tl_use:N \l__tenkz_model_last_tl }
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_value:nnn #1#2#3
  {
    \keys_define:nn {#1}
      { #2 .code:n = { \__tenkz_kernel_stage_put:nn {#3} {##1} } }
  }
% A label bearing's registry default is the word auto -- the automatic
% chooser -- so the word must be spellable.  Spelling the default is saying
% nothing: auto stages no bearing and retracts one an earlier spelling
% staged, the last-wins doctrine the alphabet already follows.  The stage
% takes the trimmed word the comparison read, since a braced key value may
% arrive spaced and the compass reader answers words, not spaced words.
\cs_new_protected:Npn \__tenkz_kernel_label_pos:nn #1#2
  {
    \keys_define:nn {#1}
      {
        #2 .code:n =
          {
            \tl_set:Nn \l__tenkz_kernel_list_item_tl {##1}
            \tl_trim_spaces:N \l__tenkz_kernel_list_item_tl
            \str_if_eq:VnTF \l__tenkz_kernel_list_item_tl {auto}
              { \prop_remove:Nn \l__tenkz_kernel_stage_prop {label-pos} }
              {
                \__tenkz_kernel_stage_put:ne {label-pos}
                  { \tl_use:N \l__tenkz_kernel_list_item_tl }
              }
          }
      }
  }
% A side takes one word of the policy alphabet, and the cup word takes the
% matrix that stands on its bend.  The matrix is a second slot on the same
% side, filled and emptied together with the word it belongs to, so a later
% word on that side retracts the bead an earlier cup asked for -- the
% last-wins doctrine the alphabet already follows.
\cs_new_protected:Npn \__tenkz_kernel_side:nn #1#2
  {
    \keys_define:nn {#1}
      { #2 .code:n = { \__tenkz_kernel_side_policy:nn {#2} {##1} } }
  }
\cs_new_protected:Npn \__tenkz_kernel_side_policy:nn #1#2
  {
    \prop_remove:Nn \l__tenkz_kernel_stage_prop {cup-#1}
    \tl_set:Nn \l__tenkz_kernel_list_item_tl {#2}
    \tl_trim_spaces:N \l__tenkz_kernel_list_item_tl
    \str_case:VnF \l__tenkz_kernel_list_item_tl
      {
        {open}  { \__tenkz_kernel_stage_put:nn {#1} {open}  }
        {none}  { \__tenkz_kernel_stage_put:nn {#1} {none}  }
        {trace} { \__tenkz_kernel_stage_put:nn {#1} {trace} }
        {cup}   { \__tenkz_kernel_stage_put:nn {#1} {cup}   }
      }
      {
        \regex_extract_once:nVNTF
          { \A cup=(.*) \Z }
          \l__tenkz_kernel_list_item_tl \l__tenkz_kernel_match_seq
          {
            \__tenkz_kernel_stage_put:nn {#1} {cup}
            \seq_get_right:NN \l__tenkz_kernel_match_seq
              \l__tenkz_kernel_scratch_tl
            \prop_put:NnV \l__tenkz_kernel_stage_prop {cup-#1}
              \l__tenkz_kernel_scratch_tl
          }
          {
            \msg_error:nneeee {tenkz}{kernel-choice} {#1} {#2}
              { open|none|trace|cup|cup={m} }
              { \__tenkz_kernel_diagnostic_context: }
          }
      }
  }
% One positive-integer parser serves every scope.  Its last three arguments
% name the diagnostic, its \msg_error variant, and any trailing diagnostic
% arguments, so the accepted alphabet is independent of the message arity.
% Extension-gate: #6967 records this helper-signature identity change; the
% installed parser leaves and public language census are unchanged.
\cs_new_protected:Npn \__tenkz_kernel_positive_integer:nnnnnn #1#2#3#4#5#6
  {
    \keys_define:nn {#1}
      {
        #2 .code:n =
          {
            \regex_match:nnTF { \A \s* [1-9]\d* \s* \Z } {##1}
              { \__tenkz_kernel_stage_put:nn {#3} {##1} }
              {
                #5 {tenkz} {#4} {#2} {##1} #6
              }
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_align:n #1
  {
    \tl_set:Nn \l__tenkz_kernel_list_item_tl {#1}
    \tl_trim_spaces:N \l__tenkz_kernel_list_item_tl
    \str_if_eq:VnTF \l__tenkz_kernel_list_item_tl {midline}
      { \__tenkz_kernel_stage_put:nn {align} {midline} }
      {
        \exp_args:NnV \regex_match:nnTF { \A [1-9]\d* \Z }
          \l__tenkz_kernel_list_item_tl
          {
            \__tenkz_kernel_stage_put:ne {align}
              { \tl_use:N \l__tenkz_kernel_list_item_tl }
          }
          {
            \msg_error:nneeee {tenkz}{kernel-choice}
              {align} {#1} {midline|<row~number>}
              { \__tenkz_kernel_diagnostic_context: }
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_flag:nnn #1#2#3
  {
    \keys_define:nn {#1}
      {
        #2 .code:n =
          {
            \str_case:nnF {##1}
              {
                {true}  { \__tenkz_kernel_stage_put:nn {#3} {true} }
                {false} { \prop_remove:Nn \l__tenkz_kernel_stage_prop {#3} }
              }
              {
                \msg_error:nneeee {tenkz}{kernel-choice}
                  {#2} {##1} {true|false}
                  { \tl_use:N \l__tenkz_model_last_tl }
              }
          },
        #2 .default:n = true
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_unknown:nn #1#2
  {
    \keys_define:nn {#1}
      {
        unknown .code:n =
          {
            \msg_error:nneee {tenkz}{kernel-unknown-key}
              { \l_keys_key_str } {#2}
              { \__tenkz_kernel_diagnostic_context: }
          }
      }
  }

% A frame value is one word followed by its own subkeys.  Keeping `basis`
% inside this value preserves the one picture-level frame leaf while giving
% every consumer the same parsed member table.
\cs_new_protected:Npn \__tenkz_kernel_basis_clear:
  {
    \int_zero:N \l__tenkz_kernel_basis_count_int
    \prop_clear:N \l__tenkz_kernel_basis_kind_prop
    \prop_clear:N \l__tenkz_kernel_basis_east_prop
    \prop_clear:N \l__tenkz_kernel_basis_north_prop
  }
\cs_new_protected:Npn \__tenkz_kernel_frame_basis_parse:n #1
  {
    \tl_set:Nn \l__tenkz_kernel_frame_tl {#1}
    \tl_trim_spaces:N \l__tenkz_kernel_frame_tl
    \__tenkz_kernel_strip_outer_groups:N \l__tenkz_kernel_frame_tl
    \regex_replace_all:nnN
      {
        \( \s* ([-+]?\d+) \s* , \s* ([-+]?\d+) \s* \)
      }
      { ( \1 ; \2 ) }
      \l__tenkz_kernel_frame_tl
    \exp_args:NNV \clist_set:Nn
      \l__tenkz_kernel_frame_clist \l__tenkz_kernel_frame_tl
    \__tenkz_kernel_basis_clear:
    \bool_set_true:N \l__tenkz_kernel_basis_valid_bool
    \clist_map_inline:Nn \l__tenkz_kernel_frame_clist
      {
        \regex_extract_once:nnNTF
          {
            \A \s* ([A-Za-z]+) \s+ at \s*
            (?: \cB\{ )? \s*
            \( \s* ([-+]?\d+) \s* ; \s* ([-+]?\d+) \s* \)
            \s* (?: \cE\} )? \s* \Z
          }
          {##1}
          \l__tenkz_kernel_match_seq
          {
            \tl_set:Ne \l__tenkz_kernel_frame_tl
              { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
            \str_case:VnF \l__tenkz_kernel_frame_tl
              {
                {wire}{}
                {ket}{}
                {op}{}
                {bra}{}
              }
              {
                \bool_set_false:N \l__tenkz_kernel_basis_valid_bool
                \msg_error:nne {tenkz}{kernel-frame-basis-kind}
                  { \tl_use:N \l__tenkz_kernel_frame_tl }
              }
            \bool_if:NT \l__tenkz_kernel_basis_valid_bool
              {
                \int_incr:N \l__tenkz_kernel_basis_count_int
                \prop_put:Nee \l__tenkz_kernel_basis_kind_prop
                  { \int_use:N \l__tenkz_kernel_basis_count_int }
                  { \tl_use:N \l__tenkz_kernel_frame_tl }
                \prop_put:Nee \l__tenkz_kernel_basis_east_prop
                  { \int_use:N \l__tenkz_kernel_basis_count_int }
                  { \seq_item:Nn \l__tenkz_kernel_match_seq {3} }
                \prop_put:Nee \l__tenkz_kernel_basis_north_prop
                  { \int_use:N \l__tenkz_kernel_basis_count_int }
                  { \seq_item:Nn \l__tenkz_kernel_match_seq {4} }
              }
          }
          {
            \bool_set_false:N \l__tenkz_kernel_basis_valid_bool
            \msg_error:nnn {tenkz}{kernel-frame-basis-parse} {##1}
          }
      }
    \bool_if:NT \l__tenkz_kernel_basis_valid_bool
      {
        \int_compare:nNnT { \l__tenkz_kernel_basis_count_int } = {0}
          {
            \bool_set_false:N \l__tenkz_kernel_basis_valid_bool
            \msg_error:nnn {tenkz}{kernel-frame-basis-parse} {#1}
          }
      }
    \bool_if:NTF \l__tenkz_kernel_basis_valid_bool
      { \__tenkz_kernel_stage_put:nn {basis} {#1} }
      {
        \__tenkz_kernel_basis_clear:
        \prop_remove:Nn \l__tenkz_kernel_stage_prop {basis}
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_frame_basis:n #1
  {
    \tl_if_in:nnTF {#1} {;}
      {
        \__tenkz_kernel_basis_clear:
        \prop_remove:Nn \l__tenkz_kernel_stage_prop {basis}
        \msg_error:nnn {tenkz}{kernel-frame-basis-parse} {#1}
      }
      { \__tenkz_kernel_frame_basis_parse:n {#1} }
  }

\keys_define:nn {tenkz-kernel-frame}
  {
    basis .code:n =
      {
        \bool_if:NTF \l__tenkz_kernel_group_keys_bool
          { \msg_error:nn {tenkz}{kernel-frame-basis-scope} }
          { \__tenkz_kernel_frame_basis:n {#1} }
      },
    unknown .code:n =
      {
        \msg_error:nneee {tenkz}{kernel-unknown-key}
          { \l_keys_key_str } {frame} {picture}
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_frame:n #1
  {
    % A later frame= replaces the whole earlier frame, including its basis.
    % Group-local frame parsing must not mutate the enclosing picture table.
    \bool_if:NF \l__tenkz_kernel_group_keys_bool
      {
        \__tenkz_kernel_basis_clear:
        \prop_remove:Nn \l__tenkz_kernel_stage_prop {basis}
        \prop_remove:Nn \l__tenkz_kernel_stage_prop {frame}
      }
    \clist_set:Nn \l__tenkz_kernel_frame_clist {#1}
    \clist_pop:NN \l__tenkz_kernel_frame_clist \l__tenkz_kernel_frame_tl
    \tl_trim_spaces:N \l__tenkz_kernel_frame_tl
    \bool_set_true:N \l__tenkz_kernel_frame_valid_bool
    \str_case:VnF \l__tenkz_kernel_frame_tl
      {
        {flat}{}
        {plane}{}
        {circle}{}
      }
      {
        \bool_set_false:N \l__tenkz_kernel_frame_valid_bool
        \msg_error:nne {tenkz}{kernel-frame-word}
          { \tl_use:N \l__tenkz_kernel_frame_tl }
      }
    \bool_if:NT \l__tenkz_kernel_frame_valid_bool
      {
        \__tenkz_kernel_stage_put:ne
          {frame} { \tl_use:N \l__tenkz_kernel_frame_tl }
        \clist_if_empty:NF \l__tenkz_kernel_frame_clist
          {
            \str_if_eq:VnTF \l__tenkz_kernel_frame_tl {circle}
              { \msg_error:nn {tenkz}{kernel-frame-basis-circle} }
              {
                \exp_args:NnV \keys_set:nn
                  {tenkz-kernel-frame} \l__tenkz_kernel_frame_clist
              }
          }
      }
  }

% Setup and equation policy merge as raw picture fields after the panel's own
% key parser has run.  Rebuild the derived member table from that final policy
% so every panel sees the same effective basis.
\cs_new_protected:Npn \__tenkz_kernel_basis_rehydrate:
  {
    \__tenkz_kernel_basis_clear:
    \prop_get:NnN
      \l__tenkz_kernel_stage_prop {basis} \l__tenkz_kernel_scratch_tl
    \quark_if_no_value:NF \l__tenkz_kernel_scratch_tl
      {
        \prop_get:NnN
          \l__tenkz_kernel_stage_prop {frame} \l__tenkz_kernel_frame_tl
        \str_if_eq:VnTF \l__tenkz_kernel_frame_tl {circle}
          {
            \msg_error:nn {tenkz}{kernel-frame-basis-circle}
            \prop_remove:Nn \l__tenkz_kernel_stage_prop {basis}
          }
          {
            \exp_args:NV \__tenkz_kernel_frame_basis:n
              \l__tenkz_kernel_scratch_tl
          }
      }
  }

% A frame and its nested basis are one policy value.  When an outer policy
% replaces the frame with a bare word, no basis from the inner policy survives.
\cs_new_protected:Npn \__tenkz_kernel_policy_merge:NN #1#2
  {
    \prop_if_in:NnT #1 {frame} { \prop_remove:Nn #2 {basis} }
    \prop_map_inline:Nn #1 { \prop_put:Nnn #2 {##1} {##2} }
  }

% ---------- picture keys -------------------------------------------------------
% "Picture options declare topology and policy; they create no hidden atoms."

\keys_define:nn { tenkz-kernel-picture }
  {
    rows .code:n =
      {
        \__tenkz_kernel_sugar_abandon:nn {sandwich} {rows}
        \__tenkz_kernel_stage_put:nn {rows} {#1}
      }
  }
% cols= carries the row's stated type: an illegal value refuses with the
% picture-scope positive-integer diagnostic instead of a bare TeX
% arithmetic error (#6198 sweep).
\__tenkz_kernel_positive_integer:nnnnnn
  { tenkz-kernel-picture } { cols } { cols }
  { kernel-picture-positive } { \msg_error:nnee } { }
\keys_define:nn {tenkz-kernel-picture}
  {
    frame .code:n =
      {
        \__tenkz_kernel_sugar_abandon:nn {planes} {frame,basis}
        \__tenkz_kernel_frame:n {#1}
      }
  }
\__tenkz_kernel_side:nn { tenkz-kernel-picture } { west }
\__tenkz_kernel_side:nn { tenkz-kernel-picture } { east }
\__tenkz_kernel_side:nn { tenkz-kernel-picture } { north }
\__tenkz_kernel_side:nn { tenkz-kernel-picture } { south }
\__tenkz_kernel_value:nnn   { tenkz-kernel-picture } { trace } { trace }
\__tenkz_kernel_value:nnn   { tenkz-kernel-picture } { open } { open }
\__tenkz_kernel_choice:nnnn { tenkz-kernel-picture } { bonds }
  { grid, none } { bonds }
% align= is the midline word or a row number: anything else refused here
% instead of deep in an fp read (#6198 sweep).
\keys_define:nn { tenkz-kernel-picture }
  {
    align .code:n = { \__tenkz_kernel_align:n {#1} }
  }
\__tenkz_kernel_choice:nnnn { tenkz-kernel-picture } { size }
  { s, m, l } { size }
% metrics=: the named metric profile of the picture's pitch.  Math style
% owns the density profile; only the author knows the page, and a picture
% whose word outruns the text column names the compact profile here
% instead of spelling a raw length in its body (Extension-gate: #5620).
\__tenkz_kernel_choice:nnnn { tenkz-kernel-picture } { metrics }
  { compact } { metrics }
\__tenkz_kernel_value:nnn   { tenkz-kernel-picture } { check } { check }
\__tenkz_kernel_unknown:nn  { tenkz-kernel-picture } { picture }

% A flag-shaped sugar key carries last-wins flag semantics (#6198 sweep):
% true or the bare spelling expands, a later false retracts that expansion,
% and any other value refuses instead of silently behaving as true.
\cs_new_protected:Npn \__tenkz_kernel_sugar_flag:nnnn #1#2#3#4
  {
    \str_case:nnF {#2}
      {
        {true}
          {
            \__tenkz_kernel_sugar:n {#1}
            \prop_if_in:NnF \l__tenkz_kernel_sugar_active_prop {#1}
              {
                \clist_map_inline:nn {#3}
                  { \__tenkz_kernel_sugar_save:nn {#1} {##1} }
                \prop_put:Nnn
                  \l__tenkz_kernel_sugar_active_prop {#1} {true}
              }
            \tl_set:Nn \l__tenkz_kernel_sugar_current_tl {#1}
            #4
            \tl_clear:N \l__tenkz_kernel_sugar_current_tl
          }
        {false}
          {
            \__tenkz_kernel_sugar:n {#1}
            \prop_if_in:NnT \l__tenkz_kernel_sugar_active_prop {#1}
              {
                \clist_map_inline:nn {#3}
                  { \__tenkz_kernel_sugar_restore:nn {#1} {##1} }
                \prop_remove:Nn \l__tenkz_kernel_sugar_active_prop {#1}
              }
          }
      }
      {
        \msg_error:nneeee {tenkz}{kernel-choice}
          {#1} {#2} {true|false} { \__tenkz_kernel_diagnostic_context: }
      }
  }
% picture-scope sugar: each expansion is the sugar ledger's row, verbatim.
\keys_define:nn { tenkz-kernel-picture }
  {
    sandwich .code:n =
      {
        \__tenkz_kernel_sugar_flag:nnnn {sandwich} {#1} {rows}
          {
            \keys_set:nn {tenkz-kernel-picture} {rows={ket,op,bra}}
          }
      },
    sandwich .default:n = true ,
    boundary .code:n = { \__tenkz_kernel_desugar_boundary:n {#1} },
    lattice .code:n  = { \__tenkz_kernel_desugar_lattice:n {#1} },
    ring .code:n     = { \__tenkz_kernel_desugar_ring:n {#1} },
    surface .code:n  = { \__tenkz_kernel_desugar_surface:n {#1} },
    planes .code:n =
      {
        \__tenkz_kernel_sugar_flag:nnnn {planes} {#1} {frame,basis}
          {
            \keys_set:nn {tenkz-kernel-picture}
              {frame={plane,basis={ket~at~(0,0),bra~at~(2,2)}}}
          }
      },
    planes .default:n = true ,
    physical .code:n = { \__tenkz_kernel_desugar_physical:n {#1} }
  }

% ---------- atom keys ------------------------------------------------------------

\__tenkz_kernel_value:nnn   { tenkz-kernel-atom } { skin } { skin }
\__tenkz_kernel_positive_integer:nnnnnn
  { tenkz-kernel-atom } { wide } { wide }
  { kernel-atom-positive } { \msg_error:nneee }
  { { \tl_use:N \l__tenkz_model_last_tl } }
\__tenkz_kernel_positive_integer:nnnnnn
  { tenkz-kernel-atom } { wires } { wires }
  { kernel-atom-positive } { \msg_error:nneee }
  { { \tl_use:N \l__tenkz_model_last_tl } }
\__tenkz_kernel_value:nnn   { tenkz-kernel-atom } { at } { at }
\__tenkz_kernel_value:nnn   { tenkz-kernel-atom } { name } { name }
\__tenkz_kernel_value:nnn   { tenkz-kernel-atom } { ports } { ports }
\__tenkz_kernel_value:nnn   { tenkz-kernel-atom } { species } { species }
\__tenkz_kernel_value:nnn
  { tenkz-kernel-atom } { pairing~cross } { pairing-cross }
\__tenkz_kernel_choice:nnnn { tenkz-kernel-atom } { size }
  { s, m, l } { size }
\__tenkz_kernel_label_pos:nn { tenkz-kernel-atom } { label~pos }
\__tenkz_kernel_choice:nnnn { tenkz-kernel-atom } { void }
  { open, sealed } { void }
% A site may answer the picture's physical policy for itself: the policy
% states what a row's sites expose, and an atom that carries no physical
% index says so once instead of listing the ports it does carry.
\__tenkz_kernel_choice:nnnn { tenkz-kernel-atom } { physical }
  { up, down, updown, none } { physical }
\__tenkz_kernel_flag:nnn    { tenkz-kernel-atom } { conjugate } { conjugate }
\__tenkz_kernel_unknown:nn  { tenkz-kernel-atom } { atom }
\keys_define:nn { tenkz-kernel-atom }
  {
    cluster .code:n = { \__tenkz_kernel_desugar_cluster:n {#1} },
    role .code:n =
      {
        \__tenkz_kernel_sugar:n {role}
        \keys_set:nn {tenkz-kernel-atom} {species={#1}}
      }
  }

% ---------- wire keys ------------------------------------------------------------

\__tenkz_kernel_choice:nnnn { tenkz-kernel-wire } { kind }
  { index, string } { kind }
\__tenkz_kernel_value:nnn   { tenkz-kernel-wire } { via } { via }
\keys_define:nn { tenkz-kernel-wire }
  {
    route .code:n    = { \__tenkz_kernel_route:n {#1} } ,
    crossing .code:n = { \__tenkz_kernel_crossing:n {#1} }
  }
\__tenkz_kernel_value:nnn   { tenkz-kernel-wire } { species } { species }
\__tenkz_kernel_value:nnn   { tenkz-kernel-wire } { wind } { wind }
\__tenkz_kernel_value:nnn   { tenkz-kernel-wire } { cross } { cross }
\__tenkz_kernel_choice:nnnn { tenkz-kernel-wire } { dir }
  { to, from, none } { dir }
\__tenkz_kernel_choice:nnnn { tenkz-kernel-wire } { stroke }
  { solid, dashed, dotted } { stroke }
\__tenkz_kernel_value:nnn   { tenkz-kernel-wire } { name } { name }
\__tenkz_kernel_flag:nnn    { tenkz-kernel-wire } { closed } { closed }
\__tenkz_kernel_unknown:nn  { tenkz-kernel-wire } { wire }

% ---------- mark keys ------------------------------------------------------------

\__tenkz_kernel_choice:nnnn { tenkz-kernel-mark } { form }
  { bracket, enclosure, label, prose }
  { form }
% The four spellings the alphabet lost, each answering a row of the registry's
% tombstone ledger in that row's own words.  A dead word is kept knowable so
% the parser can name its successor, rather than answering with the alphabet
% and leaving a reader to guess whether the word was mistyped or removed.
\__tenkz_kernel_tombstone:nnnn { tenkz-kernel-mark } { form } { brace-above }
  { use~form=bracket~with~label~pos=90;~a~bracket~speaks~from~the~side~its~
    label~sits~on }
\__tenkz_kernel_tombstone:nnnn { tenkz-kernel-mark } { form } { brace-below }
  { use~form=bracket,~which~speaks~from~the~south~when~it~names~no~side }
\__tenkz_kernel_tombstone:nnnn { tenkz-kernel-mark } { form } { cut }
  { no~successor:~the~sources'~cut~is~the~contour~of~what~it~separates,~
    so~use~form=enclosure~over~that~selection }
\__tenkz_kernel_tombstone:nnnn { tenkz-kernel-mark } { form } { band }
  { use~form=enclosure~with~tint,~which~lays~ink~over~the~paper~its~contour~
    encloses }
\__tenkz_kernel_value:nnn   { tenkz-kernel-mark } { species } { species }
\__tenkz_kernel_label_pos:nn { tenkz-kernel-mark } { label~pos }
\__tenkz_kernel_value:nnn   { tenkz-kernel-mark } { name } { name }
\__tenkz_kernel_flag:nnn    { tenkz-kernel-mark } { tint } { tint }
\__tenkz_kernel_unknown:nn  { tenkz-kernel-mark } { mark }

% ---------- setup keys -----------------------------------------------------------
% pitch acts at the style stage; the kernel records policy only.

\keys_define:nn { tenkz-kernel-setup }
  {
    pitch .code:n  = { \pgfqkeys{/tenkz}{pitch=#1} },
    strict .code:n =
      {
        \str_case:nnF {#1}
          {
            {true}  { \bool_set_true:N \l__tenkz_kernel_strict_bool }
            {false} { \bool_set_false:N \l__tenkz_kernel_strict_bool }
          }
          {
            \msg_error:nneeee {tenkz}{kernel-choice}
              {strict} {#1} {true|false} {setup}
          }
      },
    strict .default:n = true
  }
\__tenkz_kernel_unknown:nn { tenkz-kernel-setup } { setup }

% ---------- declaration keys -------------------------------------------------------

\__tenkz_kernel_value:nnn  { tenkz-kernel-declare } { hue } { hue }
\__tenkz_kernel_value:nnn  { tenkz-kernel-declare } { base } { base }
\__tenkz_kernel_value:nnn  { tenkz-kernel-declare } { pairings } { pairings }
\__tenkz_kernel_unknown:nn { tenkz-kernel-declare } { declaration }
\__tenkz_kernel_value:nnn
  { tenkz-kernel-declare-atom } { skin } { skin }
\__tenkz_kernel_value:nnn
  { tenkz-kernel-declare-atom } { ports } { ports }
\__tenkz_kernel_unknown:nn
  { tenkz-kernel-declare-atom } { atom~declaration }

% ---------- record helpers ---------------------------------------------------------
% Copy every staged field onto the newest record, register a user name when
% one was staged, and clear the stage for the next declaration.

\cs_new_protected:Npn \__tenkz_kernel_commit:
  {
    \prop_map_inline:Nn \l__tenkz_kernel_stage_prop
      {
        \str_if_eq:nnTF {##1} {ports}
          {
            \__tenkz_model_complete_raw:nnn
              { \l__tenkz_model_last_tl } {##1} {##2}
          }
          {
            \__tenkz_model_complete:nnn
              { \l__tenkz_model_last_tl } {##1} {##2}
          }
      }
    \prop_get:NnN \l__tenkz_kernel_stage_prop {name} \l__tenkz_kernel_scratch_tl
    \quark_if_no_value:NF \l__tenkz_kernel_scratch_tl
      {
        \str_if_eq:VnTF \l__tenkz_kernel_scratch_tl {self}
          {
            \msg_error:nnee {tenkz}{kernel-name-reserved}
              {self} { \tl_use:N \l__tenkz_model_last_tl }
          }
          {
            \prop_put:NeV \l__tenkz_kernel_named_prop
              { \tl_use:N \l__tenkz_kernel_scratch_tl }
              \l__tenkz_model_last_tl
          }
      }
    \prop_clear:N \l__tenkz_kernel_stage_prop
  }

% Sugar guard: under \tnset{strict} a sugar spelling is a coded error.
% The guard fires before its command mints a record, so the diagnostic
% carries no record clause.
\cs_new_protected:Npn \__tenkz_kernel_sugar:n #1
  {
    \bool_if:NT \l__tenkz_kernel_strict_bool
      { \msg_error:nnn {tenkz}{kernel-strict-sugar} {#1} }
  }

% ---------- picture-scope sugar expansions -------------------------------------------

\cs_new_protected:Npn \__tenkz_kernel_desugar_boundary:n #1
  {
    \__tenkz_kernel_sugar:n {boundary}
    \str_case:nnF {#1}
      {
        {open}     { \keys_set:nn {tenkz-kernel-picture} {west=open, east=open} }
        {none}     { \keys_set:nn {tenkz-kernel-picture} {west=none, east=none} }
        {periodic} { \keys_set:nn {tenkz-kernel-picture} {west=trace, east=trace} }
      }
      {
        \msg_error:nneeee {tenkz}{kernel-choice} {boundary} {#1}
          { open,~none,~periodic } { picture }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_desugar_lattice:n #1
  {
    \__tenkz_kernel_sugar:n {lattice}
    \regex_extract_once:nnNTF
      { \A \s* ([1-9]\d*) \s* x \s* ([1-9]\d*) \s* \Z } {#1}
      \l__tenkz_kernel_match_seq
      {
        \tl_clear:N \l_tmpa_tl
        \int_step_inline:nn { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
          {
            \tl_if_empty:NF \l_tmpa_tl { \tl_put_right:Nn \l_tmpa_tl {,} }
            \tl_put_right:Nn \l_tmpa_tl {wire}
          }
        \keys_set:ne {tenkz-kernel-picture}
          {
            rows = { \tl_use:N \l_tmpa_tl } ,
            cols = { \seq_item:Nn \l__tenkz_kernel_match_seq {3} }
          }
      }
      {
        \msg_error:nneeee {tenkz}{kernel-sugar-value}
          {lattice} {#1} {positive~rows x positive~columns} {picture}
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_desugar_ring:n #1
  {
    \__tenkz_kernel_sugar:n {ring}
    \regex_match:nnTF { \A \s* [1-9]\d* \s* \Z } {#1}
      {
        \keys_set:nn {tenkz-kernel-picture}
          { rows = {wire}, frame = circle, west = trace, east = trace }
        \keys_set:ne {tenkz-kernel-picture} { cols = {#1} }
      }
      {
        \msg_error:nneeee {tenkz}{kernel-sugar-value}
          {ring} {#1} {a~positive~column~count} {picture}
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_desugar_surface:n #1
  {
    \__tenkz_kernel_sugar:n {surface}
    \str_if_eq:nnTF {#1} {torus}
      {
        \keys_set:nn {tenkz-kernel-picture}
          { west = trace, east = trace, north = trace, south = trace }
      }
      {
        \msg_error:nneeee {tenkz}{kernel-sugar-value}
          {surface} {#1} {torus} {picture}
      }
  }
\tl_new:N \l__tenkz_kernel_atom_phys_tl

\cs_new_protected:Npn \__tenkz_kernel_desugar_physical:n #1
  {
    \__tenkz_kernel_sugar:n {physical}
    \str_case:nnF {#1}
      {
        {up}     { \__tenkz_kernel_stage_put:nn {physical} {up} }
        {down}   { \__tenkz_kernel_stage_put:nn {physical} {down} }
        {updown} { \__tenkz_kernel_stage_put:nn {physical} {updown} }
        {none}   { \__tenkz_kernel_stage_put:nn {physical} {none} }
      }
      {
        \msg_error:nneeee {tenkz}{kernel-choice} {physical} {#1}
          { up,~down,~updown,~none } { picture }
      }
  }

% cluster={RxC}: a group of R x C dot atoms on a quarter-pitch sub-frame,
% each addressable as <name>-<r>-<c>.  The expansion runs at commit time,
% when the carrying atom's name is known.
\cs_new_protected:Npn \__tenkz_kernel_desugar_cluster:n #1
  {
    \__tenkz_kernel_sugar:n {cluster}
    \regex_match:nnTF
      { \A \s* [1-9]\d* \s* x \s* [1-9]\d* \s* \Z } {#1}
      { \__tenkz_kernel_stage_put:nn {cluster} {#1} }
      {
        \msg_error:nneeee {tenkz}{kernel-sugar-value}
          {cluster} {#1} {positive~rows x positive~columns} {atom}
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_cluster_expand:nn #1#2
  {
    \regex_extract_once:nnNTF
      { \A \s* ([1-9]\d*) \s* x \s* ([1-9]\d*) \s* \Z } {#2}
      \l__tenkz_kernel_match_seq
      {
        \prop_get:NnN \l__tenkz_kernel_named_prop {#1} \l_tmpa_tl
        \__tenkz_model_get:nnN
          { \tl_use:N \l_tmpa_tl } {species} \l_tmpb_tl
        \tl_clear:N \l__tenkz_kernel_scratch_tl
        \quark_if_no_value:NF \l_tmpb_tl
          {
            \tl_set:Ne \l__tenkz_kernel_scratch_tl
              { , species = \tl_use:N \l_tmpb_tl }
          }
        \int_gincr:N \g__tenkz_kernel_group_int
        \__tenkz_model_record_frame:e
          {
            map = subframe , scale = quarter ,
            group = g \int_use:N \g__tenkz_kernel_group_int
          }
        \int_step_inline:nn { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
          {
            \int_step_inline:nn { \seq_item:Nn \l__tenkz_kernel_match_seq {3} }
              {
                \__tenkz_model_record_atom:e
                  {
                    kind = tn , skin = dot , name = #1 - ##1 - ####1 ,
                    cluster-of = #1 ,
                    group = g \int_use:N \g__tenkz_kernel_group_int
                    \tl_use:N \l__tenkz_kernel_scratch_tl
                  }
                \prop_put:Nee \l__tenkz_kernel_named_prop
                  { #1 - ##1 - ####1 } { \tl_use:N \l__tenkz_model_last_tl }
              }
          }
      }
      {
        \msg_error:nneeee {tenkz}{kernel-sugar-value}
          {cluster} {#2} {positive~rows x positive~columns} {#1}
      }
  }

% ---------- events -------------------------------------------------------------------
% The kernel serializes its validated records: one picture line, then one
% line per record in declaration order with the fields sorted, so the stream
% is stable under implementation change.

\seq_new:N \l__tenkz_kernel_emit_seq
\cs_new_protected:Npn \__tenkz_kernel_emit:
  {
    % The shared renderer owns measured label and glyph events.  Give it the
    % kernel picture identity without overloading the dialect picture counter.
    \use:c {tenkz@auditpicturetrue}
    \cs_set:cpe {tenkz@auditpictureid}
      { k \int_use:N \g__tenkz_kernel_picture_int }
    % A declared metric profile is part of the picture record: the stream
    % states it, since pitch-normalized coordinates cannot show it.  The
    % size class is stated on the same terms and for the same reason, and
    % like the profile it is stated only where it departs from the base --
    % the class the stream's own coordinates were calibrated on.
    \__tenkz_model_picture_get:nN {metrics} \l__tenkz_kernel_scratch_tl
    \__tenkz_model_picture_get:nN {size} \l__tenkz_kernel_size_tl
    \__tenkz_kernel_event:n
      {
        picture|id=k \int_use:N \g__tenkz_kernel_picture_int |lang=kernel
        \quark_if_no_value:NF \l__tenkz_kernel_scratch_tl
          { |metrics=\tl_use:N \l__tenkz_kernel_scratch_tl }
        \quark_if_no_value:NF \l__tenkz_kernel_size_tl
          {
            \str_if_eq:VnF \l__tenkz_kernel_size_tl {m}
              { |size=\tl_use:N \l__tenkz_kernel_size_tl }
          }
        \bool_if:NT \l__tenkz_kernel_ineq_bool
          { |scope=\int_use:N \g__tenkz_kernel_eq_scope_int }
      }
    \__tenkz_kernel_emit_index:
    \clist_map_inline:nn { atom, wire, mark, frame }
      {
        \__tenkz_model_map_ids:nn {##1}
          { \__tenkz_kernel_emit_record:n {####1} }
      }
    \__tenkz_kernel_signature:N \l__tenkz_kernel_sig_tl
    \__tenkz_kernel_event:n
      {
        kernel-boundary
        |signature=\tl_use:N \l__tenkz_kernel_sig_tl
      }
  }
% The record store is one flat table keyed "id/field", while the stream is
% written one record at a time.  Asking the whole table for each record's
% fields reads every field of the picture once per record.  One pass files
% each field under the record that owns it, and a record reads only its own.
\__tenkz_prop_new_indexed:N \l__tenkz_kernel_emit_field_prop
\tl_new:N \l__tenkz_kernel_emit_field_tl
\cs_new_protected:Npn \__tenkz_kernel_emit_index:
  {
    \prop_clear:N \l__tenkz_kernel_emit_field_prop
    \prop_map_inline:Nn \l__tenkz_model_record_prop
      { \__tenkz_kernel_emit_index_entry:wn ##1 \q_stop {##2} }
  }
\cs_new_protected:Npn \__tenkz_kernel_emit_index_entry:wn #1 / #2 \q_stop #3
  {
    \prop_get:NnNF \l__tenkz_kernel_emit_field_prop {#1}
      \l__tenkz_kernel_emit_field_tl
      { \tl_clear:N \l__tenkz_kernel_emit_field_tl }
    \tl_put_right:Ne \l__tenkz_kernel_emit_field_tl
      { { \tl_to_str:n {#2} = \tl_to_str:n {#3} } }
    \prop_put:NnV \l__tenkz_kernel_emit_field_prop {#1}
      \l__tenkz_kernel_emit_field_tl
  }
\cs_new_protected:Npn \__tenkz_kernel_emit_record:n #1
  {
    \seq_clear:N \l__tenkz_kernel_emit_seq
    \prop_get:NnNT \l__tenkz_kernel_emit_field_prop {#1}
      \l__tenkz_kernel_emit_field_tl
      {
        \tl_map_inline:Nn \l__tenkz_kernel_emit_field_tl
          { \seq_put_right:Nn \l__tenkz_kernel_emit_seq {##1} }
      }
    \seq_sort:Nn \l__tenkz_kernel_emit_seq
      {
        \str_compare:eNeTF {##1} > {##2}
          { \sort_return_swapped: } { \sort_return_same: }
      }
    \__tenkz_kernel_event:n
      {
        \prop_item:Nn \l__tenkz_model_class_prop {#1} |id=#1
        \seq_map_function:NN \l__tenkz_kernel_emit_seq
          \__tenkz_kernel_emit_field:n
      }
  }
\cs_new:Npn \__tenkz_kernel_emit_field:n #1 { |#1 }

% ---------- boundary signatures --------------------------------------------------------
% The signature of a panel is the sorted multiset of its exposed indices:
% one entry per open wire end and one per outward physical port.  tenkzeq
% compares adjacent panels and errors on mismatch.  A directed open end,
% virtual or physical, carries its orientation: the entry says whether the
% index leaves the panel (to) or enters it (from), so a space and its dual
% cannot pass for one another and two sides of a relation must agree on
% orientation and not merely on count.  An internal directed contraction
% exposes nothing and leaves no entry.  The orientation follows the face and
% follows the face; a port and a policy leg declare no direction, so only a
% wire end carries one.

\tl_new:N \l__tenkz_kernel_sig_dir_tl
\tl_new:N \l__tenkz_kernel_sig_dir_src_tl

% The orientation of a directed index at the end being described, normalized
% to the cut: the barb ends at this end (to, the index leaves the panel) or
% at the other end (from, the index enters it).  #1 is the wire record, #2
% the end (`from' or `to'); an undirected wire yields the empty suffix.
\cs_new_protected:Npn \__tenkz_kernel_signature_dir:nnN #1#2#3
  {
    \tl_clear:N #3
    \__tenkz_model_get:nnN {#1} {dir} \l__tenkz_kernel_sig_dir_src_tl
    \quark_if_no_value:NF \l__tenkz_kernel_sig_dir_src_tl
      {
        \str_if_eq:VnF \l__tenkz_kernel_sig_dir_src_tl {none}
          {
            \tl_set:Ne #3
              {
                :
                \str_if_eq:VnTF \l__tenkz_kernel_sig_dir_src_tl {#2}
                  {to} {from}
              }
          }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_bearing_normalize:nN #1#2
  {
    \tl_set:Ne #2
      { \fp_eval:n { round( #1 - 360 * floor( (#1) / 360 ), 6 ) } }
    % Rounding can carry 359.9999999 to a full turn.  Canonicalize once more
    % so face identities and panel signatures share the same half-open turn.
    \tl_set:Ne #2
      { \fp_eval:n { #2 - 360 * floor( (#2) / 360 ) } }
  }

% Normalize a bearing and name it when it is exactly cardinal.  Boundary
% signatures and pre-render label occupancy share this one interpretation of
% a full turn and of the four compass sides.
\cs_new_protected:Npn \__tenkz_kernel_bearing_cardinal:nN #1#2
  {
    \__tenkz_kernel_bearing_normalize:nN {#1} #2
    \str_case:Vn #2
      { {0}{\tl_set:Nn #2 {e}} {90}{\tl_set:Nn #2 {n}}
        {180}{\tl_set:Nn #2 {w}} {270}{\tl_set:Nn #2 {s}} }
  }

\cs_new_protected:Npn \__tenkz_kernel_signature_port_open:nN #1#2
  {
    \__tenkz_model_get:nnN {#1} {port-face} \l__tenkz_kernel_port_face_tl
    \__tenkz_model_get:nnN {#1} {host}
      \l__tenkz_kernel_port_record_tl
    \exp_args:NVV \__tenkz_kernel_r_page_face:nnN
      \l__tenkz_kernel_port_record_tl
      \l__tenkz_kernel_port_face_tl
      \l__tenkz_kernel_r_leg_turn_tl
    \exp_args:NV \__tenkz_kernel_bearing_cardinal:nN
      \l__tenkz_kernel_r_leg_turn_tl \l__tenkz_kernel_port_face_tl
    \__tenkz_model_get:nnN {#1} {port-type} \l__tenkz_kernel_port_type_tl
    \tl_set:Ne #2
      {
        \str_if_eq:VnTF \l__tenkz_kernel_port_type_tl {physical}
          {phys}{open}
        : \tl_use:N \l__tenkz_kernel_port_face_tl
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_signature_physical_axis:nn #1#2
  {
    \__tenkz_kernel_policy_leg_map:nnn {#1} {#2}
      {
        \__tenkz_kernel_policy_slot_bearing:nnnN {#1} {#2} {##1}
          \l__tenkz_kernel_r_leg_turn_tl
        \exp_args:NV \__tenkz_kernel_bearing_cardinal:nN
          \l__tenkz_kernel_r_leg_turn_tl
          \l__tenkz_kernel_port_face_tl
        \seq_put_right:Ne \l__tenkz_kernel_emit_seq
          { phys: \tl_use:N \l__tenkz_kernel_port_face_tl }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_signature:N #1
  {
    \seq_clear:N \l__tenkz_kernel_emit_seq
    \__tenkz_model_map_ids:nn {wire}
      {
        \__tenkz_model_get:nnN {##1} {origin} \l__tenkz_kernel_scratch_tl
        \str_if_eq:VnT \l__tenkz_kernel_scratch_tl {port-open}
          {
            \__tenkz_kernel_signature_port_open:nN {##1} \l_tmpa_tl
            \seq_put_right:NV \l__tenkz_kernel_emit_seq \l_tmpa_tl
          }
        \clist_map_inline:nn { from, to }
          {
            \__tenkz_model_get:nnN {##1} {####1-open}
              \l__tenkz_kernel_scratch_tl
            \quark_if_no_value:NF \l__tenkz_kernel_scratch_tl
              {
                \str_if_eq:VnF \l__tenkz_kernel_scratch_tl {port}
                  {
                    % An end with no direction is still an index the frame
                    % cuts, and the side it is cut on is the end of the arc
                    % its route travels.
                    \str_if_eq:VnT \l__tenkz_kernel_scratch_tl {route}
                      {
                        \__tenkz_kernel_route_of:nNN {##1}
                          \l__tenkz_kernel_ro_side_tl
                          \l__tenkz_kernel_ro_sel_tl
                        \tl_set:Ne \l__tenkz_kernel_scratch_tl
                          {
                            \__tenkz_kernel_route_end:ee
                              { \tl_use:N \l__tenkz_kernel_ro_side_tl }
                              {####1}
                          }
                      }
                    % A transverse open end is cut on the page side its
                    % signed transverse vector exits, so panels compare it
                    % against the ordinary physical legs of that side.
                    \str_case:VnTF \l__tenkz_kernel_scratch_tl
                      {
                        {up}
                          {
                            \tl_set:Nn
                              \l__tenkz_kernel_port_face_tl {n}
                          }
                        {down}
                          {
                            \tl_set:Nn
                              \l__tenkz_kernel_port_face_tl {s}
                          }
                      }
                      {
                        \exp_args:NnV
                          \__tenkz_geom_frame_transverse:nnNN {kpic}
                          \l__tenkz_kernel_port_face_tl
                          \l__tenkz_kernel_r_physical_x_tl
                          \l__tenkz_kernel_r_physical_y_tl
                        \exp_args:Ne
                          \__tenkz_kernel_bearing_cardinal:nN
                          {
                            \fp_eval:n
                              {
                                atand(
                                  \l__tenkz_kernel_r_physical_y_tl ,
                                  \l__tenkz_kernel_r_physical_x_tl )
                              }
                          }
                          \l__tenkz_kernel_scratch_tl
                      }
                      { }
                    \__tenkz_model_get:nnN
                      {##1} {####1-open-type}
                      \l__tenkz_kernel_port_type_tl
                    % The orientation of a directed index is part of the
                    % exposed entry whether the index is virtual or physical:
                    % a directed virtual leg states the bond space against
                    % its dual exactly as a directed physical leg does.
                    % The orientation follows the face.
                    \__tenkz_kernel_signature_dir:nnN {##1} {####1}
                      \l__tenkz_kernel_sig_dir_tl
                    \seq_put_right:Ne \l__tenkz_kernel_emit_seq
                      {
                        \str_if_eq:VnTF
                          \l__tenkz_kernel_port_type_tl {physical}
                          {phys}{open}
                        : \tl_use:N \l__tenkz_kernel_scratch_tl
                        \tl_use:N \l__tenkz_kernel_sig_dir_tl
                      }
                  }
              }
            \__tenkz_model_get:nnN {##1} {####1} \l__tenkz_kernel_scratch_tl
            \quark_if_no_value:NF \l__tenkz_kernel_scratch_tl
              {
                \prop_get:NeN \l__tenkz_kernel_node_prop
                  { \l__tenkz_kernel_scratch_tl / kind } \l_tmpa_tl
                \quark_if_no_value:NF \l_tmpa_tl
                  {
                    \str_if_eq:VnT \l_tmpa_tl {outside}
                      {
                        \__tenkz_kernel_signature_dir:nnN {##1} {####1}
                          \l__tenkz_kernel_sig_dir_tl
                        \seq_put_right:Ne \l__tenkz_kernel_emit_seq
                          {
                            edge: \prop_item:Ne \l__tenkz_kernel_node_prop
                              { \l__tenkz_kernel_scratch_tl / side }
                            \tl_use:N \l__tenkz_kernel_sig_dir_tl
                          }
                      }
                  }
              }
          }
      }
    \__tenkz_model_map_ids:nn {atom}
      {
        \__tenkz_kernel_policy_leg_renders:nT {##1}
          {
            \__tenkz_kernel_policy_faces:nn {##1}
              { \__tenkz_kernel_signature_physical_axis:nn {##1} {####1} }
          }
      }
    \seq_sort:Nn \l__tenkz_kernel_emit_seq
      {
        \str_compare:eNeTF {##1} > {##2}
          { \sort_return_swapped: } { \sort_return_same: }
      }
    \tl_set:Ne #1 { \seq_use:Nn \l__tenkz_kernel_emit_seq { ,~ } }
  }

% ---------- the label math shift ---------------------------------------------------------

% Every positional label is mathematics and the kernel opens the math shift
% around it.  An author who spells the shift as well -- `\tn{$\bar A$}`, the
% form most reach for -- would double it, and a doubled shift only fails on
% content that cares, such as an accent, so the trap fires late and blames the
% label rather than the doubling.  Peel the author's outer pair here.  Both
% spellings then reach the label as the same tokens and leave the same record,
% and the peel is where the labelled cup already found it (issue 5562): a
% value that keeps its delimiters needs a box, and a box holds text style
% inside a script-style label.
\tl_new:N \l__tenkz_kernel_unmath_tl
\tl_new:N \l__tenkz_kernel_label_tl
\cs_new_protected:Npn \__tenkz_kernel_unmath:w $#1$ \q_stop
  { \tl_set:Nn \l__tenkz_kernel_unmath_tl {#1} }
\cs_new_protected:Npn \__tenkz_kernel_unmath:N #1
  {
    % A value may arrive wrapped in the braces that protected a comma while
    % its key list was read; the delimiters to peel sit inside them.
    \__tenkz_kernel_strip_outer_groups:N #1
    \exp_args:NV \tl_if_head_eq_catcode:nNT #1 $
      {
        \exp_last_unbraced:NV \__tenkz_kernel_unmath:w #1 \q_stop
        % A label that holds the comma separating record fields keeps the
        % grouping that protects it; every other value returns bare, so a
        % label still compares equal to the one a policy recorded.
        \tl_if_in:VnTF \l__tenkz_kernel_unmath_tl { , }
          {
            \tl_set:Ne #1
              { \exp_not:n { { \exp_not:V \l__tenkz_kernel_unmath_tl } } }
          }
          { \tl_set_eq:NN #1 \l__tenkz_kernel_unmath_tl }
      }
  }

% ---------- the seven commands -----------------------------------------------------------

% \tn[keys]{label}: an atom at the next chain cell or at=.
\cs_new_protected:Npn \__tenkz_kernel_tn:nn #1#2
  {
    \__tenkz_model_record_atom:n { kind = tn }
    \prop_clear:N \l__tenkz_kernel_stage_prop
    \__tenkz_kernel_keys:nn { tenkz-kernel-atom } {#1}
    % A label is opaque text: \hphantom, \vphantom, and math accents like
    % \bar embed their own conditional/box primitives, so a full expansion
    % of the label content orphans an \iffalse from its \fi (issue 5481).
    % The raw completion stores it unexpanded, as the model already does
    % for the equally opaque `ports` field in \__tenkz_kernel_commit:.
    \tl_if_blank:nF {#2}
      {
        \tl_set:Nn \l__tenkz_kernel_label_tl {#2}
        \__tenkz_kernel_unmath:N \l__tenkz_kernel_label_tl
        \__tenkz_model_complete_raw:nnV { \l__tenkz_model_last_tl } {label}
          \l__tenkz_kernel_label_tl
      }
    \prop_get:NnN \l__tenkz_kernel_stage_prop {at} \l__tenkz_kernel_scratch_tl
    \quark_if_no_value:NTF \l__tenkz_kernel_scratch_tl
      {
        \__tenkz_model_complete:nnn { \l__tenkz_model_last_tl } {addr}
          {
            ( \int_use:N \l__tenkz_kernel_row_int ,
              \int_use:N \l__tenkz_kernel_col_int )
          }
        \__tenkz_kernel_claim_span:nnN
          { \l__tenkz_kernel_row_int }
          { \l__tenkz_kernel_col_int }
          \l__tenkz_model_last_tl
        \int_add:Nn \l__tenkz_kernel_col_int
          { \l__tenkz_kernel_claim_cols_int - 1 }
        \int_set:Nn \l__tenkz_kernel_row_advance_int
          {
            \int_max:nn
              { \l__tenkz_kernel_row_advance_int }
              { \l__tenkz_kernel_claim_rows_int }
          }
      }
      {
        \prop_remove:Nn \l__tenkz_kernel_stage_prop {at}
        % the record id survives the address parse on the operand stack
        \seq_push:Ne \l__tenkz_kernel_stack_seq
          { \tl_use:N \l__tenkz_model_last_tl }
        \__tenkz_kernel_addr:VN \l__tenkz_kernel_scratch_tl \l_tmpa_tl
        \seq_pop:NN \l__tenkz_kernel_stack_seq \l__tenkz_kernel_scratch_tl
        \__tenkz_model_complete:nnn { \l__tenkz_kernel_scratch_tl } {node}
          { \tl_use:N \l_tmpa_tl }
        \__tenkz_kernel_claim_cell:NN
          \l_tmpa_tl \l__tenkz_kernel_scratch_tl
        \tl_set:NV \l__tenkz_model_last_tl \l__tenkz_kernel_scratch_tl
      }
    % a staged cluster expands once the carrying atom is committed
    \prop_get:NnN \l__tenkz_kernel_stage_prop {cluster} \l_tmpb_tl
    \prop_get:NnN \l__tenkz_kernel_stage_prop {name} \l_tmpa_tl
    \__tenkz_kernel_commit:
    \quark_if_no_value:NF \l_tmpb_tl
      {
        \quark_if_no_value:NTF \l_tmpa_tl
          {
            \msg_error:nnee {tenkz}{kernel-cluster-name}
              { \tl_use:N \l_tmpb_tl }
              { \tl_use:N \l__tenkz_model_last_tl }
          }
          {
            \exp_args:NVV \__tenkz_kernel_cluster_expand:nn
              \l_tmpa_tl \l_tmpb_tl
          }
      }
  }

% A direct cell placement owns every cell in its declared rectangular span.
% Other address productions are geometric placements and therefore do not
% participate in frame population.
\int_new:N \l__tenkz_kernel_claim_row_int
\int_new:N \l__tenkz_kernel_claim_col_int
\int_new:N \l__tenkz_kernel_claim_rows_int
\int_new:N \l__tenkz_kernel_claim_cols_int
\cs_new_protected:Npn \__tenkz_kernel_any_cell_claim:nnN #1#2#3
  {
    \prop_get:NnN \l__tenkz_kernel_cell_prop {#1-#2} #3
    \quark_if_no_value:NT #3
      {
        \int_step_inline:nn { \l__tenkz_kernel_basis_count_int }
          {
            \quark_if_no_value:NT #3
              {
                \prop_get:NnN
                  \l__tenkz_kernel_basis_claim_prop {#1-#2-##1} #3
              }
          }
      }
  }
\cs_generate_variant:Nn \__tenkz_kernel_any_cell_claim:nnN { eeN }
\cs_new_protected:Npn \__tenkz_kernel_claim_span:nnN #1#2#3
  {
    \int_set:Nn \l__tenkz_kernel_claim_row_int {#1}
    \int_set:Nn \l__tenkz_kernel_claim_col_int {#2}
    \int_set:Nn \l__tenkz_kernel_claim_rows_int {1}
    \int_set:Nn \l__tenkz_kernel_claim_cols_int {1}
    \prop_get:NnN \l__tenkz_kernel_stage_prop {wires} \l_tmpa_tl
    \quark_if_no_value:NF \l_tmpa_tl
      { \int_set:Nn \l__tenkz_kernel_claim_rows_int {\l_tmpa_tl} }
    \prop_get:NnN \l__tenkz_kernel_stage_prop {wide} \l_tmpa_tl
    \quark_if_no_value:NF \l_tmpa_tl
      { \int_set:Nn \l__tenkz_kernel_claim_cols_int {\l_tmpa_tl} }
    \bool_set_false:N \l__tenkz_kernel_claim_collision_bool
    \int_step_inline:nn { \l__tenkz_kernel_claim_rows_int }
      {
        \int_step_inline:nn { \l__tenkz_kernel_claim_cols_int }
          {
            \__tenkz_kernel_any_cell_claim:eeN
              {
                \int_eval:n
                  { \l__tenkz_kernel_claim_row_int + ##1 - 1 }
              }
              {
                \int_eval:n
                  { \l__tenkz_kernel_claim_col_int + ####1 - 1 }
              }
              \l__tenkz_kernel_claim_owner_tl
            \quark_if_no_value:NF \l__tenkz_kernel_claim_owner_tl
              {
                \bool_set_true:N \l__tenkz_kernel_claim_collision_bool
                \msg_error:nneeee {tenkz}{kernel-cell-occupied}
                  {
                    \int_eval:n
                      { \l__tenkz_kernel_claim_row_int + ##1 - 1 }
                  }
                  {
                    \int_eval:n
                      { \l__tenkz_kernel_claim_col_int + ####1 - 1 }
                  }
                  { \tl_use:N \l__tenkz_kernel_claim_owner_tl }
                  { \tl_use:N #3 }
              }
          }
      }
    \bool_if:NF \l__tenkz_kernel_claim_collision_bool
      {
        \int_step_inline:nn { \l__tenkz_kernel_claim_rows_int }
          {
            \int_step_inline:nn { \l__tenkz_kernel_claim_cols_int }
              {
                \prop_put:Nee \l__tenkz_kernel_cell_prop
                  {
                    \int_eval:n
                      { \l__tenkz_kernel_claim_row_int + ##1 - 1 }
                    -
                    \int_eval:n
                      { \l__tenkz_kernel_claim_col_int + ####1 - 1 }
                  }
                  { \tl_use:N #3 }
              }
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_claim_cell:NN #1#2
  {
    \str_case:en
      { \__tenkz_kernel_node_item:Nn #1 {kind} }
      {
        {cell}
          {
            \__tenkz_kernel_claim_span:nnN
              { \__tenkz_kernel_node_item:Nn #1 {row} }
              { \__tenkz_kernel_node_item:Nn #1 {col} }
              #2
          }
        {member}
          { \__tenkz_kernel_claim_member:NN #1#2 }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_complete_member:Nnn #1#2#3
  {
    \__tenkz_model_normalize_field:nnnn
      { \tl_use:N #1 } {node} {addr}
      {
        ( \int_use:N \l__tenkz_kernel_claim_row_int,
          \int_use:N \l__tenkz_kernel_claim_col_int,
          \int_use:N \l__tenkz_kernel_pop_int )
      }
    \__tenkz_model_complete:nnn { \tl_use:N #1 } {cell-row}
      { \int_use:N \l__tenkz_kernel_claim_row_int }
    \__tenkz_model_complete:nnn { \tl_use:N #1 } {cell-col}
      { \int_use:N \l__tenkz_kernel_claim_col_int }
    \__tenkz_model_complete:nnn { \tl_use:N #1 } {member}
      { \int_use:N \l__tenkz_kernel_pop_int }
    \__tenkz_model_complete:nnn { \tl_use:N #1 } {basis-east} {#2}
    \__tenkz_model_complete:nnn { \tl_use:N #1 } {basis-north} {#3}
  }

\cs_new_protected:Npn \__tenkz_kernel_claim_member_span:N #1
  {
    \int_set:Nn \l__tenkz_kernel_claim_rows_int {1}
    \int_set:Nn \l__tenkz_kernel_claim_cols_int {1}
    \prop_get:NnN \l__tenkz_kernel_stage_prop {wires} \l_tmpa_tl
    \quark_if_no_value:NF \l_tmpa_tl
      { \int_set:Nn \l__tenkz_kernel_claim_rows_int {\l_tmpa_tl} }
    \prop_get:NnN \l__tenkz_kernel_stage_prop {wide} \l_tmpa_tl
    \quark_if_no_value:NF \l_tmpa_tl
      { \int_set:Nn \l__tenkz_kernel_claim_cols_int {\l_tmpa_tl} }
    \bool_set_false:N \l__tenkz_kernel_claim_collision_bool
    \int_step_inline:nn { \l__tenkz_kernel_claim_rows_int }
      {
        \int_step_inline:nn { \l__tenkz_kernel_claim_cols_int }
          {
            \prop_get:NeN \l__tenkz_kernel_cell_prop
              {
                \int_eval:n { \l__tenkz_kernel_claim_row_int + ##1 - 1 }
                -
                \int_eval:n { \l__tenkz_kernel_claim_col_int + ####1 - 1 }
              }
              \l__tenkz_kernel_claim_owner_tl
            \quark_if_no_value:NT \l__tenkz_kernel_claim_owner_tl
              {
                \prop_get:NeN \l__tenkz_kernel_basis_claim_prop
                  {
                    \int_eval:n { \l__tenkz_kernel_claim_row_int + ##1 - 1 }
                    -
                    \int_eval:n { \l__tenkz_kernel_claim_col_int + ####1 - 1 }
                    -\int_use:N \l__tenkz_kernel_pop_int
                  }
                  \l__tenkz_kernel_claim_owner_tl
              }
            \quark_if_no_value:NF \l__tenkz_kernel_claim_owner_tl
              {
                \bool_set_true:N \l__tenkz_kernel_claim_collision_bool
                \msg_error:nneeee {tenkz}{kernel-frame-member-occupied}
                  { \int_eval:n { \l__tenkz_kernel_claim_row_int + ##1 - 1 } }
                  { \int_eval:n { \l__tenkz_kernel_claim_col_int + ####1 - 1 } }
                  { \int_use:N \l__tenkz_kernel_pop_int }
                  { \l__tenkz_kernel_claim_owner_tl }
              }
          }
      }
    \bool_if:NF \l__tenkz_kernel_claim_collision_bool
      {
        \int_step_inline:nn { \l__tenkz_kernel_claim_rows_int }
          {
            \int_step_inline:nn { \l__tenkz_kernel_claim_cols_int }
              {
                \prop_put:Nee \l__tenkz_kernel_basis_claim_prop
                  {
                    \int_eval:n { \l__tenkz_kernel_claim_row_int + ##1 - 1 }
                    -
                    \int_eval:n { \l__tenkz_kernel_claim_col_int + ####1 - 1 }
                    -\int_use:N \l__tenkz_kernel_pop_int
                  }
                  { \tl_use:N #1 }
              }
          }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_claim_member:NN #1#2
  {
    \int_set:Nn \l__tenkz_kernel_claim_row_int
      { \__tenkz_kernel_node_item:Nn #1 {row} }
    \int_set:Nn \l__tenkz_kernel_claim_col_int
      { \__tenkz_kernel_node_item:Nn #1 {col} }
    \int_set:Nn \l__tenkz_kernel_pop_int
      { \__tenkz_kernel_node_item:Nn #1 {member} }
    \__tenkz_kernel_basis_cell_aligned:TF
      {
        \int_compare:nNnTF { \l__tenkz_kernel_pop_int } = {1}
          {
            \__tenkz_kernel_claim_span:nnN
              { \l__tenkz_kernel_claim_row_int }
              { \l__tenkz_kernel_claim_col_int }
              #2
            \bool_if:NF \l__tenkz_kernel_claim_collision_bool
              { \__tenkz_kernel_complete_member:Nnn #2 {0}{0} }
          }
          {
            \msg_error:nnnnn {tenkz}{kernel-frame-member-range}
              { \int_use:N \l__tenkz_kernel_claim_row_int }
              { \int_use:N \l__tenkz_kernel_claim_col_int }
              { \int_use:N \l__tenkz_kernel_pop_int }
          }
      }
      {
        \bool_lazy_or:nnTF
          { \int_compare_p:nNn { \l__tenkz_kernel_pop_int } < {1} }
          {
            \int_compare_p:nNn
              { \l__tenkz_kernel_pop_int }
              > { \l__tenkz_kernel_basis_count_int }
          }
          {
            \msg_error:nnnnn {tenkz}{kernel-frame-member-range}
              { \int_use:N \l__tenkz_kernel_claim_row_int }
              { \int_use:N \l__tenkz_kernel_claim_col_int }
              { \int_use:N \l__tenkz_kernel_pop_int }
          }
          {
            \__tenkz_kernel_claim_member_span:N #2
            \bool_if:NF \l__tenkz_kernel_claim_collision_bool
              {
                \prop_get:NeN \l__tenkz_kernel_basis_east_prop
                  { \int_use:N \l__tenkz_kernel_pop_int }
                  \l__tenkz_kernel_claim_owner_tl
                \prop_get:NeN \l__tenkz_kernel_basis_north_prop
                  { \int_use:N \l__tenkz_kernel_pop_int }
                  \l__tenkz_kernel_frame_tl
                \__tenkz_kernel_complete_member:Nnn #2
                  { \l__tenkz_kernel_claim_owner_tl }
                  { \l__tenkz_kernel_frame_tl }
              }
          }
      }
  }

% \tnwire[keys]{end}{end}: a wire end is an address, `open <dir>`, or a bare
% `open`; a closed wire takes no positional ends.  A bare open end names no
% direction and takes its place from the route: it lies where that side's arc
% ends, one clearance past the outermost wire the route crosses.  Without a
% route there is nothing to take a place from, and the end is refused.
\cs_new_protected:Npn \__tenkz_kernel_wire_end:nn #1#2
  {
    \str_if_eq:nnTF {#2} {open}
      {
        \__tenkz_model_complete:nnn { \l__tenkz_model_last_tl } { #1-open }
          {route}
      }
      {
    \regex_extract_once:nnNTF { \A open \s+ (nw|ne|sw|se|n|e|s|w) \Z } {#2}
      \l__tenkz_kernel_match_seq
      {
        \__tenkz_model_complete:nnn { \l__tenkz_model_last_tl } { #1-open }
          { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
      }
      {
    % `open up|down` is the transverse sibling of an in-plane compass open:
    % the end is an open leg on the frame's independent transverse physical
    % axis, so the wire is a physical leg by construction.  Validation
    % rejects the words on frames that declare no transverse axis.
    \regex_extract_once:nnNTF { \A open \s+ (up|down) \Z } {#2}
      \l__tenkz_kernel_match_seq
      {
        \__tenkz_model_complete:nnn { \l__tenkz_model_last_tl } { #1-open }
          { \seq_item:Nn \l__tenkz_kernel_match_seq {2} }
        \__tenkz_model_complete:nnn { \l__tenkz_model_last_tl }
          { #1-open-type } {physical}
        \__tenkz_kernel_port_wire_physical:n { \l__tenkz_model_last_tl }
      }
      {
        \seq_push:Ne \l__tenkz_kernel_stack_seq
          { \tl_use:N \l__tenkz_model_last_tl }
        \__tenkz_kernel_addr:nN {#2} \l_tmpa_tl
        \seq_pop:NN \l__tenkz_kernel_stack_seq \l__tenkz_kernel_scratch_tl
        \tl_set:NV \l__tenkz_model_last_tl \l__tenkz_kernel_scratch_tl
        \__tenkz_model_complete:nnn { \l__tenkz_model_last_tl } {#1}
          { \tl_use:N \l_tmpa_tl }
      }
      }
      }
  }
\cs_generate_variant:Nn \__tenkz_kernel_wire_end:nn { ne }

% Resolve a mark's target as a selection and store the canonical membership.
% The staged target is the caller's; a mark that resolves to nothing keeps
% none, and the selector grammar has already said why.
\cs_new_protected:Npn \__tenkz_kernel_mark_select:n #1
  {
    \__tenkz_kernel_select:VN \l__tenkz_kernel_list_tl
      \l__tenkz_kernel_sel_seq
    \bool_if:NT \l__tenkz_kernel_sel_valid_bool
      {
        \tl_set:Ne \l__tenkz_kernel_sel_tl
          { \seq_use:Nn \l__tenkz_kernel_sel_seq {,} }
        \exp_args:NnnnV \__tenkz_model_normalize_field:nnnn
          {#1} {target} {members} \l__tenkz_kernel_sel_tl
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_validate_addresses:
  {
    \__tenkz_model_map_ids:nn {wire}
      {
        \tl_set:Nn \l__tenkz_model_last_tl {##1}
        \clist_map_inline:nn {via}
          {
            \__tenkz_model_get:nnN {##1} {####1}
              \l__tenkz_kernel_list_tl
            \quark_if_no_value:NF \l__tenkz_kernel_list_tl
              {
                \exp_args:NV \__tenkz_kernel_validate_address_list:n
                  \l__tenkz_kernel_list_tl
              }
          }
        \__tenkz_model_get:nnN {##1} {cross}
          \l__tenkz_kernel_list_tl
        \quark_if_no_value:NF \l__tenkz_kernel_list_tl
          {
            \exp_args:NV \__tenkz_kernel_validate_crossing_list:n
              \l__tenkz_kernel_list_tl
          }
      }
    \__tenkz_model_map_ids:nn {mark}
      {
        \tl_set:Nn \l__tenkz_model_last_tl {##1}
        \__tenkz_model_get:nnN {##1} {form} \l__tenkz_kernel_list_item_tl
        \__tenkz_model_get:nnN {##1} {target} \l__tenkz_kernel_list_tl
        \quark_if_no_value:NF \l__tenkz_kernel_list_tl
          {
            \str_case:Vn \l__tenkz_kernel_list_item_tl
              {
                % Both hull forms speak about a SET of records, so both read
                % their target through the selector grammar -- which is what
                % lets a cell range corner a bracket as it corners a contour.
                {enclosure} { \__tenkz_kernel_mark_select:n {##1} }
                {bracket}   { \__tenkz_kernel_mark_select:n {##1} }
                {label}
                  {
                    \__tenkz_kernel_addr:VN \l__tenkz_kernel_list_tl
                      \l__tenkz_kernel_list_item_tl
                    \exp_args:NnnnV \__tenkz_model_normalize_field:nnnn
                      {##1} {target} {node} \l__tenkz_kernel_list_item_tl
                  }
              }
          }
      }
  }

% An open end names a place, and a frame either has that place or has not:
% a circle frame has no corners for the diagonal words, and only a plane
% frame declares the independent transverse axis that `up' and `down'
% follow.  Those are one rule -- a class of open words is admitted by a set
% of frames -- so the table states it once and one walk over the open ends
% applies every row.  A word some frame cannot honour is added here and
% nowhere else; the frames that can honour it are the row's second field.
\cs_new_protected:Npn \__tenkz_kernel_open_word_rules:N #1
  {
    #1 { nw|ne|sw|se } {flat,plane} { kernel-circle-open-direction }
    #1 { up|down }     {plane}      { kernel-open-transverse-frame }
  }

\tl_new:N \l__tenkz_kernel_open_word_tl
\tl_new:N \l__tenkz_kernel_open_wire_tl
\tl_new:N \l__tenkz_kernel_open_end_tl
\tl_new:N \l__tenkz_kernel_open_frame_tl

% One row of the table against the open word the walk has in hand: the word
% class #1 is admitted on the frames #2, and a frame outside that set raises
% #3 naming the wire, its end, and the word the author wrote.
\cs_new_protected:Npn \__tenkz_kernel_validate_open_word:nnn #1#2#3
  {
    \clist_if_in:nVF {#2} \l__tenkz_kernel_open_frame_tl
      {
        \exp_args:NnV \regex_match:nnT
          { \A ( #1 ) \Z }
          \l__tenkz_kernel_open_word_tl
          {
            \msg_error:nneee {tenkz}{#3}
              { \tl_use:N \l__tenkz_kernel_open_wire_tl }
              { \tl_use:N \l__tenkz_kernel_open_end_tl }
              { \tl_use:N \l__tenkz_kernel_open_word_tl }
          }
      }
  }

\cs_new_protected:Npn \__tenkz_kernel_validate_open_words:
  {
    \tl_set:Ne \l__tenkz_kernel_open_frame_tl
      { \__tenkz_kernel_r_frame_word: }
    \__tenkz_model_map_ids:nn {wire}
      {
        \tl_set:Nn \l__tenkz_kernel_open_wire_tl {##1}
        \clist_map_inline:nn {from, to}
          {
            \__tenkz_model_get:nnN {##1} {####1-open}
              \l__tenkz_kernel_open_word_tl
            \quark_if_no_value:NF \l__tenkz_kernel_open_word_tl
              {
                \tl_set:Nn \l__tenkz_kernel_open_end_tl {####1}
                \__tenkz_kernel_open_word_rules:N
                  \__tenkz_kernel_validate_open_word:nnn
              }
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_tnwire:nnnn #1#2#3#4
  {
    \__tenkz_model_record_wire:n { kind = index }
    \prop_clear:N \l__tenkz_kernel_stage_prop
    \__tenkz_kernel_keys:nn { tenkz-kernel-wire } {#1}
    \prop_if_in:NnTF \l__tenkz_kernel_stage_prop {closed}
      {
        \int_compare:nNnTF {#4} = {0}
          { \__tenkz_kernel_validate_closed_points: }
          {
            \msg_error:nne {tenkz}{kernel-wire-arity}
              { \tl_use:N \l__tenkz_model_last_tl }
          }
      }
      {
        \int_compare:nNnTF {#4} = {2}
          {
            \bool_lazy_or:nnTF
              { \tl_if_blank_p:n {#2} }
              { \tl_if_blank_p:n {#3} }
              {
                \msg_error:nne {tenkz}{kernel-wire-arity}
                  { \tl_use:N \l__tenkz_model_last_tl }
              }
              {
                \__tenkz_kernel_wire_end:nn {from} {#2}
                \__tenkz_kernel_wire_end:nn {to} {#3}
              }
          }
          {
            \msg_error:nne {tenkz}{kernel-wire-arity}
              { \tl_use:N \l__tenkz_model_last_tl }
          }
      }
    \__tenkz_kernel_commit:
    \ignorespaces
  }
\cs_new_protected:Npn \__tenkz_kernel_tnwire:nnn #1#2#3
  { \__tenkz_kernel_tnwire:nnnn {#1} {#2} {#3} {2} }

% \tnmark[keys]{target}{label}: the target -- a cell set, an address set,
% or a wire place -- is stored verbatim.  A mark owns no topology; validate
% its form-specific ink before the target address is completed.
\cs_new_protected:Npn \__tenkz_kernel_mark_validate_tint:
  {
    \prop_if_in:NnT \l__tenkz_kernel_stage_prop {tint}
      {
        \tl_set:Nn \l__tenkz_kernel_scratch_tl {label}
        \prop_get:NnNT \l__tenkz_kernel_stage_prop {form}
          \l__tenkz_kernel_scratch_tl { }
        \str_if_eq:VnF \l__tenkz_kernel_scratch_tl {enclosure}
          {
            \msg_error:nnee {tenkz}{kernel-mark-tint-form}
              { \tl_use:N \l__tenkz_kernel_scratch_tl }
              { \tl_use:N \l__tenkz_model_last_tl }
          }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_tnmark:nnn #1#2#3
  {
    \__tenkz_model_record_mark:n { form = label }
    \prop_clear:N \l__tenkz_kernel_stage_prop
    \__tenkz_kernel_keys:nn { tenkz-kernel-mark } {#1}
    \__tenkz_kernel_mark_validate_tint:
    \__tenkz_model_complete:nnn { \l__tenkz_model_last_tl } {target} {#2}
    % Opaque text, per the \tn label above (issue 5481): raw completion.
    % A mark's label is mathematics like every other, so it peels its shift
    % like every other.  A prose panel is the one exception: it carries a
    % sentence rather than a matrix, and the mark renderer never typesets it.
    \tl_if_blank:nF {#3}
      {
        \tl_set:Nn \l__tenkz_kernel_label_tl {#3}
        \prop_get:NnN \l__tenkz_kernel_stage_prop {form}
          \l__tenkz_kernel_scratch_tl
        \str_if_eq:VnF \l__tenkz_kernel_scratch_tl {prose}
          { \__tenkz_kernel_unmath:N \l__tenkz_kernel_label_tl }
        \__tenkz_model_complete_raw:nnV { \l__tenkz_model_last_tl } {label}
          \l__tenkz_kernel_label_tl
      }
    \__tenkz_kernel_commit:
    \ignorespaces
  }

% \tngroup[keys]{body}: a scoped sub-frame; names inside stay addressable
% outside, because the name table is picture-local, not group-local.
\cs_new_protected:Npn \__tenkz_kernel_tngroup:nn #1#2
  {
    \int_gincr:N \g__tenkz_kernel_group_int
    \__tenkz_model_record_frame:e
      { map = group , group = g \int_use:N \g__tenkz_kernel_group_int }
    \prop_clear:N \l__tenkz_kernel_stage_prop
    \bool_set_true:N \l__tenkz_kernel_group_keys_bool
    \__tenkz_kernel_keys:nn { tenkz-kernel-picture } {#1}
    \bool_set_false:N \l__tenkz_kernel_group_keys_bool
    % The metric context is picture policy: a sub-frame shares its
    % picture's pitch and its size class, so a group-scope profile or class
    % is refused, not applied.  The audit belongs to the equation, which is
    % further out still.  A key that would be silently inert here is a key
    % the author has misplaced, and the kernel says so.
    \prop_if_in:NnT \l__tenkz_kernel_stage_prop {metrics}
      {
        \msg_error:nn {tenkz}{kernel-metrics-scope}
        \prop_remove:Nn \l__tenkz_kernel_stage_prop {metrics}
      }
    \prop_if_in:NnT \l__tenkz_kernel_stage_prop {size}
      {
        \msg_error:nn {tenkz}{kernel-size-scope}
        \prop_remove:Nn \l__tenkz_kernel_stage_prop {size}
      }
    \prop_get:NnN \l__tenkz_kernel_stage_prop {check} \l__tenkz_kernel_scratch_tl
    \quark_if_no_value:NF \l__tenkz_kernel_scratch_tl
      {
        \msg_error:nnee {tenkz}{kernel-check-scope}
          { \tl_use:N \l__tenkz_kernel_scratch_tl } {group}
        \prop_remove:Nn \l__tenkz_kernel_stage_prop {check}
      }
    \__tenkz_kernel_commit:
    % the group body arrives as one grabbed argument, so its column tabs
    % are the document's alignment character; give them the column step
    \tl_set:Nn \l__tenkz_kernel_group_body_tl {#2}
    \__tenkz_kernel_rewrite_tabs:N \l__tenkz_kernel_group_body_tl
    \ignorespaces
    \tl_use:N \l__tenkz_kernel_group_body_tl
    \ignorespaces
  }

% \tnset{keys}: document or group policy.
\cs_new_protected:Npn \__tenkz_kernel_tnset:n #1
  {
    \prop_clear:N \l__tenkz_kernel_stage_prop
    \__tenkz_kernel_context_keys:nnn
      {setup} { tenkz-kernel-setup } {#1}
    \__tenkz_kernel_policy_merge:NN
      \l__tenkz_kernel_stage_prop \g__tenkz_kernel_setup_prop
    \prop_clear:N \l__tenkz_kernel_stage_prop
    \ignorespaces
  }

% \tndeclare{class}{name}{keys}: the one extension door.  A declared atom
% mints a one-label command; skins and species are descriptor tables the
% style stage consumes.
\prop_new:N \g__tenkz_kernel_species_prop
\prop_new:N \g__tenkz_kernel_species_hue_prop
\prop_new:N \g__tenkz_kernel_skin_prop
\cs_new_protected:Npn \__tenkz_kernel_tndeclare:nnn #1#2#3
  {
    \str_case:nnF {#1}
      {
        {atom}    { \__tenkz_kernel_declare_atom:nn {#2} {#3} }
        {skin}    { \__tenkz_kernel_declare_table:Nnn
                      \g__tenkz_kernel_skin_prop {#2} {#3} }
        {species} { \__tenkz_kernel_declare_species:nn {#2} {#3} }
      }
      { \msg_error:nnn {tenkz}{kernel-declare-class} {#1} }
    \ignorespaces
  }
% The prelude species and their fixed hues, one authority for the hue
% resolver and the declaration door: the four semantic roles and the five
% region words the retired slot= key left to species=.  A hue-less prelude
% redeclaration keeps its fixed hue; an explicit hue remains authoritative.
% Only hue-less names outside this table ride the cycle.
\clist_const:Nn \c__tenkz_kernel_region_words_clist
  { selected , secondary , complement , collar , neutral }
\int_new:N \g__tenkz_kernel_species_cycle_int
\cs_new_protected:Npn \__tenkz_kernel_prelude_hue:nN #1#2
  {
    \str_case:nnF {#1}
      {
        {operator}   { \tl_set:Nn #2 {tenkzOperator} }
        {marked}     { \tl_set:Nn #2 {tenkzMarked} }
        {extra}      { \tl_set:Nn #2 {tenkzExtra} }
        {passive}    { \tl_set:Nn #2 {tenkzPassive} }
        {selected}   { \tl_set:Nn #2 {tenkzAction} }
        {secondary}  { \tl_set:Nn #2 {tenkzMarked} }
        {complement} { \tl_set:Nn #2 {tenkzPassive} }
        {collar}     { \tl_set:Nn #2 {tenkzExtra} }
        {neutral}    { \tl_set:Nn #2 {tenkzInk} }
      }
      { \tl_clear:N #2 }
  }
% Read the one optional hue field from a species descriptor.  Declaration,
% rendering, and the mark compatibility rule all use this parser, so they
% cannot disagree about whether a document supplied an explicit hue.
\cs_new_protected:Npn \__tenkz_kernel_species_descriptor_hue:nN #1#2
  {
    \tl_clear:N #2
    \regex_extract_once:nnNT
      {
        (?: \A | , ) \s* hue \s* = \s*
        (?: \{ \s* )? (?: source: )?
        ([^,{}\s]+) (?: \s* \} )?
      }
      {#1}
      \l__tenkz_kernel_r_hue_match_seq
      {
        \tl_set:Ne #2
          { \seq_item:Nn \l__tenkz_kernel_r_hue_match_seq {2} }
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_declare_species:nn #1#2
  {
    \__tenkz_kernel_declare_table:Nnn
      \g__tenkz_kernel_species_prop {#1} {#2}
    % One door, both stores: the same declaration mints the shared
    % `species <name> ...' style family (tenkz-core) that \tntree draws
    % from, so a declared species is usable on every record class.  A
    % declared hue is authoritative there exactly as it is for kernel
    % records (\__tenkz_kernel_r_species_hue:nN); only non-prelude hue-less
    % declarations take the house cycle.
    \__tenkz_kernel_species_descriptor_hue:nN {#2}
      \l__tenkz_kernel_scratch_tl
    \tl_if_empty:NTF \l__tenkz_kernel_scratch_tl
      {
        \__tenkz_kernel_prelude_hue:nN {#1}
          \l__tenkz_kernel_scratch_tl
        \tl_if_empty:NTF \l__tenkz_kernel_scratch_tl
          {
            \tenkz_species_one:n {#1}
            % Hue-less declarations receive a document-scope cycle slot now,
            % in declaration order.  Reordering first uses in later pictures
            % therefore cannot change the identity's ink.
            \prop_if_in:NnF \g__tenkz_kernel_species_hue_prop {#1}
              {
                \int_set:Nn \l_tmpa_int
                  {
                    \int_mod:nn
                      { \g__tenkz_kernel_species_cycle_int }
                      { \clist_count:N \c__tenkz_speccycle_clist }
                    + 1
                  }
                \tl_set:Nx \l_tmpa_tl
                  {
                    \clist_item:Nn \c__tenkz_speccycle_clist
                      { \l_tmpa_int }
                  }
                \int_gincr:N \g__tenkz_kernel_species_cycle_int
                \prop_gput:NnV
                  \g__tenkz_kernel_species_hue_prop {#1} \l_tmpa_tl
              }
          }
          {
            % Prelude declarations use their fixed hue in the shared tree
            % styles as well as in kernel records.  They do not consume a
            % house-cycle slot.
            \exp_args:NnV \tenkz_species_one_hued:nn {#1}
              \l__tenkz_kernel_scratch_tl
            \prop_gput:NnV
              \g__tenkz_kernel_species_hue_prop {#1}
              \l__tenkz_kernel_scratch_tl
          }
      }
      {
        \exp_args:NnV \tenkz_species_one_hued:nn {#1}
          \l__tenkz_kernel_scratch_tl
      }
  }
\cs_new_protected:Npn \__tenkz_kernel_declare_table:Nnn #1#2#3
  {
    % the declaration keys validate the descriptor; the table keeps it raw
    \prop_clear:N \l__tenkz_kernel_stage_prop
    \__tenkz_kernel_context_keys:nnn
      {declaration} { tenkz-kernel-declare } {#3}
    \prop_clear:N \l__tenkz_kernel_stage_prop
    \prop_gput:Nnn #1 {#2} {#3}
  }
\cs_new_protected:Npn \__tenkz_kernel_declare_atom:nn #1#2
  {
    \prop_clear:N \l__tenkz_kernel_stage_prop
    \__tenkz_kernel_context_keys:nnn
      {declaration} { tenkz-kernel-declare-atom } {#2}
    \prop_clear:N \l__tenkz_kernel_stage_prop
    \tl_set:Ne \l__tenkz_kernel_scratch_tl { \tl_to_str:n {#1} }
    \str_if_eq:eeT
      { \exp_args:NV \str_head:n \l__tenkz_kernel_scratch_tl }
      { \c_backslash_str }
      {
        \tl_set:Ne \l__tenkz_kernel_scratch_tl
          {
            \exp_args:NV \str_range:nnn
              \l__tenkz_kernel_scratch_tl {2} {-1}
          }
      }
    \tl_trim_spaces:N \l__tenkz_kernel_scratch_tl
    \exp_args:NV \__tenkz_kernel_declare_atom_aux:nn
      \l__tenkz_kernel_scratch_tl {#2}
  }
\cs_new_protected:Npn \__tenkz_kernel_declare_atom_aux:nn #1#2
  {
    \exp_args:Nc \NewDocumentCommand {#1} { O{} m }
      { \__tenkz_kernel_tn:nn { #2 , ##1 } {##2} \ignorespaces }
  }

% Stock skins live in the executable registry.  Package-load installation is
% deliberately skin-only: it may populate the shared descriptor table, but it
% must not mint atom commands or assign document species.
\cs_new_protected:Npn \__tenkz_kernel_install_prelude:nnn #1#2#3
  {
    \str_if_eq:nnTF {#1} {skin}
      { \__tenkz_kernel_declare_table:Nnn
          \g__tenkz_kernel_skin_prop {#2} {#3} }
      { \msg_error:nnn {tenkz}{kernel-prelude-class} {#1} }
  }
\seq_map_inline:Nn \g__tenkz_language_preludes_seq
  { \__tenkz_kernel_install_prelude:nnn #1 }


\ExplSyntaxOff
\endinput
