Sashité for Developers
  1. Sashité for Developers
  2. Rules
  3. Sanki
  4. Kernel

Kernel — Sanki (sashite.sanki.kernel/1)

This page renders the specification the Sanki reference module was built to — the document a Rule System event (kind 3417) names in its spec tag. The bytes that tag names are the blob 7aeb3f6dfaf623bd71d829dbdf8d6f89c757b9b04fd98010111243089cf84682; this page is a courtesy rendering of the same text. The rules of a session are what its module computes (Rule System — Sanki); this document describes it.

This document specifies the kernel that Sashité’s reference rule system for Sanki implements — the fixed rules that the module’s embedded data (the GGN movement tables and the parameters of Appendix B) parameterise. Under ADR-0034 a rule system is an executable WebAssembly module named by a signed Rule System event (kind 3417), and this document is that module’s specification: the human-readable statement of what it does, pinned by the event’s spec tag, the reference a defect is judged against. It is written so that everything a session’s verdict depends on is either stated here or incorporated here by digest; the verdict itself is what the module computes (Rule System — Sanki), and nothing is left to an arbiter, because under ADR-0033 there is none.

The kernel has two layers. The position kernel (Part I) is the per-ply transition legality → apply → canonicalize: given a position, a ply and the rule-system data, it either rejects the ply or yields the next canonical position. The session kernel (Part II) is the natural-state function: given the public events of a session — its founding, its Plies with their canonical timings, and the invocation that ends it — it yields the verdict. The position kernel is a pure function of a position and a ply; the session kernel is a pure function of events. Neither has any other input.

Identification and standing

Where Value Meaning
describe.kernel of the reference module (Kernel ABI — Sanki §describe) sashite.sanki.kernel/1 the name of this set of rules — documentary: what the module was built to
the spec tag of Sashité’s Rule System event (kind 3417) the SHA-256 of this document as published pins the text beside the module’s bytes — two parties committing to the rule system can read the same words; documentary, never computed from

Incorporated documents. Four documents of the Sashité suite are part of this kernel by incorporation; their digests at the time of publication are listed here, so that the spec digest transitively pins them. They are normative for what they specify; this document restates their rules only as far as needed to name them.

Document Specifies SHA-256 at publication
Time Accounting — Sanki the clock arithmetic (§II.4) 8bc18fc997aecbcb56f4226fa0d14f92de4bb6e5ceab5fc36eafd9a5629fc573
Move Encoding — Sanki the ply content, the slot structure, the selection rule and the cap K (§II.2–II.3) 4694c64fd312a3eae36c99c217e367f58fac03b0e4a54609c95b53da5a4df1ff
Statuses — Sanki the status vocabulary, the implicit conventions and the verdict resolution (§II.5–II.6) 490337601f139c6391fe09e54680da3ac41c3f2bd1fd22312287be680fbc5a99
GGN v1.0.0 the movement-table format the position kernel reads c054ef693a1f6c3b367d94390c512e581730a39ac85c0a8f40bba921cce175b4

The module is the rule; the prose describes it. For a session, the rules are what the module its rule system names computes — every answer, the verdict included (Kernel ABI — Sanki §Normativity). This document says what that module is meant to do; the conformance corpus (§III.1) is the module’s test suite, run through every build before publication. Where the prose admits two readings, the module’s behaviour is the rule and the corpus pins it; where a published module contradicts the prose, the module is defective, and the defect is repaired forward by a new rule system — a new module, a new event — never by editing the module, this document or its corpus under a published rule system, since sessions founded under it name those exact bytes and that exact spec digest (ADR-0034 §8). A revision of this document is a new spec digest, carried by the next Rule System event; a change to a fixed rule is also a new kernel identifier (Appendix A).

No discretion. Nothing in this kernel is “MAY” for the party computing a verdict. The one place a rule system could leave a choice open — a non-deterministic initial position — is excluded: a Sanki rule system MUST prescribe a deterministic initial position for every pairing it defines, reported by its module’s describe.positions (Rule System — Sanki §What describe reports).


Part I — The position kernel

I.1 Inputs and output

The position kernel takes:

It returns either Illegal (with a reason, informational) or the next canonical position together with one bit, irreversible, consumed by the session kernel’s move-limit counter (§I.8).

The pipeline is fixed: resolve (§I.4) → uchifuzume (§I.5) → apply (§I.6) → capture mutation and hand canonicalisation (§I.6) → canonicalize (§I.7). A rejection at any stage leaves the position untouched.

I.2 Data the kernel reads

The GGN tables enumerate, for every QPI the variant uses, every source (a square, or the hand *) and every destination, the possibilities — each a set of LCN pre-conditions (must/deny) on named squares. A possibility is satisfied in a position when every one of its conditions holds. A movement absent from the tables is not possible (GGN §13.2, exhaustiveness).

Condition matching (normative). "empty" is satisfied by an unoccupied square; "enemy" by a square holding a piece whose Side differs from the Side of the QPI under which the possibility is listed (GGN §7.2); a QPI value by a piece whose style letter (cased by its Side), state marker, letter and terminal marker all equal the value’s — exact token equality, the EPIN derivation marker ' (not part of QPI) being ignored. A must object is a conjunction, a deny object a disjunction (GGN §7.3).

The parameters the position kernel reads are listed in Appendix B. Everything else in this Part is a fixed rule.

I.3 Derived relations

A1 — Attack. A square s is attacked by side X in a position if and only if some piece of X has, from its square, a satisfied possibility with destination s whose destination condition is enemy, the possibility being evaluated as though s were occupied by an enemy piece. The relation is derived from the GGN alone; it therefore excludes foot-soldier pushes, the xiongqi General’s step to an empty square and castling (destination empty), and includes the General’s Chariot-range capture. Every rule below that mentions attack, check or safety is phrased in this relation.

In check. A side is in check when the square of a piece of that side bearing the terminal marker ^ is attacked by the other side.

Pseudo-legal set. The pseudo-legal moves of the active side are the satisfied possibilities of its pieces (board and hand). Legal set. The pseudo-legal moves that survive §I.4 (royal safety) and §I.5 (uchifuzume).

I.4 Resolution of a ply (A8, A6, E)

A ply [from, to, actor] is admitted if and only if all of the following hold; otherwise it is rejected with the first failing condition as reason:

  1. Possibility. from is a square holding a piece of the active side, or null with actor naming a piece the active side holds in hand (hand availability, a conservation fact read on the FEEN hands); and some possibility of that piece’s QPI from that source to to is satisfied.
  2. Actor. actor obeys the promotion table (parameter): for a board move that promotes — the piece is a promoting letter of its variant and to lies on its promotion rank, from not already on it — actor MUST name one of the table’s targets when the table lists several, and MUST be null when it lists one (automatic promotion); for every other board move actor MUST be null; for a drop actor names the dropped piece.
  3. Royal safety (A6). The canonical position resulting from the ply (after §I.6–I.7) does not leave the mover in check. This single condition covers pins, moving into check, failing to parry, and castling out of, through or into check — the last through §I.7 A5, which withholds + from a rook whose castling would cross an attacked square.

A8 — Rejection is not a sanction. A rejected ply changes nothing: the position, the hands, the clocks and the turn are as before. The session kernel treats it as a non-event (§II.3). There is no illegalmove termination.

Effect recognition (E). An admitted ply is one of exactly five effects, recognised on the form of the satisfied possibility, never on an annotation:

Effect Recognised when Applied as
E1 move / ordinary capture the default the piece leaves from and occupies to; an occupant of to is captured
E2 castling the possibility’s must names a square holding a rook-class QPI of the mover marked + (the only possibility shape of this form in the whole GGN) E1 for the royal; that rook is relocated to the square of the straight segment from→to adjacent to to
E3 en passant the possibility’s must names a square holding an enemy foot-soldier marked - E1 for the mover; that foot-soldier is captured although it does not stand on to
E4 promotion condition 2 above identifies a promotion E1, then the piece at to is replaced by the target letter (from actor, or the single target) in the mover’s case, without state marker
E5 drop from is null the hand token is placed on to exactly as held (no marker, no capture, no promotion), and removed from the hand

The turn passes to the other side after every effect (a protocol invariant, not a rule of this kernel).

I.5 Drop restrictions (A7)

Two restrictions apply to drops (E5) above the GGN’s own conditions (an empty destination; for a letter the document keeps off a rank, its omission there):

Both lists may be empty.

I.6 Capture mutation and hand canonicalisation

When an effect captures (E1 with an occupant, E3), the captured token goes to the capturer’s hand after the variant’s capture mutation (parameter, keyed by the capturer’s variant):

  1. A2′ — Hand canonicalisation (fixed rule). A token in hand never bears a state marker: + and - are stripped. This is a recomposition of the position, not an action of the move; it is why PMN writes e5~f6 bare.
  2. Demotion (parameter). The letter is replaced per the demote table (T → F in ōgi; empty elsewhere).
  3. Side and foreign pieces (parameter). side: "flip" puts the token in the capturer’s case (ōgi capturing ōgi); side: "keep" leaves it in the victim’s case, an inert token the capturer can never drop (chess and xiongqi capturing anything). foreign says what a piece of another variant becomes: kept as is, or replaced by a letter of the capturer’s variant (ōgi captures chess or xiongqi pieces as F).

A token is droppable by a side if and only if its case is that side’s (§I.2: an inert token has no * entry in the GGN and so no possibility).

I.7 Canonicalisation

After apply, four recompositions are performed on the resulting position, in this order, each a pure function of the board (and, for A5, of the ply’s effect). They are mutually independent — none reads a marker another writes — and together they define position identity: two positions are the same for repetition (§I.8) if and only if their canonical FEEN strings are equal, markers and hands included.

  Recomposition Rule
A2 check marker every piece marked ^ bears - if and only if its square is attacked (A1) by the other side; every other - on a ^ piece is removed
A3 double-step marker (parameter: foot-soldier letter and home rank per side) + is removed from every foot-soldier not on its home rank; + is never added
A4 en passant window (parameter: foot-soldier letter, marching direction) - is removed from every foot-soldier; then - is set on the foot-soldier that has just made a double step if and only if an en passant capture of it is legal for the side now to move — a capturer stands in position and the capture passes A6 — so that the marker is present exactly when the capture is possible. This is the kernel’s only arrow from canonicalisation back into legality (a one-ply look-ahead on a tentative position).
A5 castling rights (no parameter — derived from the GGN) a rook-class piece marked R (bare) stays bare forever. A rook marked +R or -R becomes bare R when the ply moved its royal (every marked rook of that side) or moved that rook, or when any piece arrives on the rook’s square by capture. Otherwise it is marked +R if the royal’s castling possibility that references it (the possibility whose must names its square with value +R) would be satisfied with, in addition, no square of the royal’s transit — the straight segment from→to, source excluded — attacked (A1) and the royal not in check; and -R if not. The transit is read on the geometry of the possibility; the parameters carry no castling vocabulary.

The initial position of a pairing carries no - markers and carries + only on the pieces the rule system’s parameters list (playing-principles §10, condition 4).

I.8 Terminal classification (A9)

After canonicalisation, the resulting position is classified for the side now to move. Facts: has a pseudo-legal move, has a legal move (§I.3), in check, and the four background conditions below. The classification order is fixed:

  1. if there is no legal move: nomove (a draw) when there is not even a pseudo-legal move; else checkmate (the side to move loses) when in check; else stalemate (outcome: parameter session.stalemate, draw for Sanki);
  2. otherwise, in this order: insufficient (a draw) when one of the dead-position predicates the pairing enables holds (parameter pairings.<pair>.dead_positions, names from this fixed catalogue: lone_royals — only the two royals on the board; royal_and_bishop_vs_royal and royal_and_knight_vs_royal — a lone royal against a royal and one minor, either orientation; royals_and_same_colour_bishops — royals and any number of bishops all standing on squares of one colour; royals_only_with_inert_hands — only the two royals on the board and no hand holding a token droppable by its holder); repetition when the canonical position has now occurred session.repetition times (3) in the session, initial position included; movelimit when session.move_limit (100) half-moves have passed without an irreversible move; movecap when the session has reached session.move_cap (600) half-moves;
  3. otherwise the session is ongoing.

Irreversible (parameter session.irreversible): a ply is irreversible when it captures, or when it is a board move of an unpromoted foot-soldier (read on the source piece, so a promotion counts); a drop and a castling are not. The move-limit counter resets on an irreversible ply and counts every other one.

A delivered checkmate takes precedence over any background draw the same ply would produce, by the order above.


Part II — The session kernel

II.1 Inputs and output

The session kernel takes the founding (the Game Session: players, seats, variants, rules, time_control, t₀), the session’s Plies with their canonical timings (Canonical Timing), and an invocation: the pubkey of the player ending the session and a cutoff timing. The protocol layer supplies the invocation — before ADR-0033 the Adjudication Request, under it the Conclusion event itself, whose signer and canonical timing are the invocation. An invocation’s cutoff is at or after t₀: the session is not playable before t₀ (kind 3422 §Canonical session start), a Conclusion timed before it is not a conforming invocation (kind 3425 §Semantic constraints, item 9), and the kernel is not invoked for it. Plies with canonical timing strictly after the cutoff, and Plies without canonical timing, are not inputs.

It returns a verdict: a status of the Statuses — Sanki vocabulary and a result split — 100/0, 50/50 or 0/100, seats being the axis. A conforming invocation always yields a verdict (§II.6).

II.2 t₀, slots and play order

t₀ is the Game Session’s start_at when present, otherwise its canonical timing. A Ply’s slot is (session, signer, step), step being the signer’s own move ordinal; the play order interleaves the two sequences by strict alternation, seat first before seat second within each step (Move Encoding — Sanki §Within-step ordering).

II.3 The canonical chain

Slots are consumed in the play order. A slot’s candidates are its Plies whose canonical timing lies in [t₀, cutoff] (§II.1; a Ply timed before t₀ is invalid and never a candidate). Identical candidates — same content, byte for byte, same draw flag — are one candidate for the cap, represented in each window by the one that window’s scan reaches first; an identical pair straddling T is one candidate per window (Move Encoding — Sanki §Slot candidates and selection). At each slot the selection rule picks the canonical Ply: the boundary T is the maximum canonical timing among the preceding selected Plies (t₀ for the first slot), never rewound by a selected premove; candidates timed strictly before T form the anterior window (premoves), those timed at or after T the informed window (live moves). The canonical Ply is the latest legal anterior candidate among the K most recent by (canonical timing, event id), scanned newest-first; else the earliest legal informed candidate among the K earliest by (canonical timing, event id), scanned oldest-first; else the slot is unfilled and the chain stops there. K is the rule system’s parameter session.candidate_cap (Appendix B; 8 for the reference module), compiled into the module and identical for every consumer of a session. Legality is the position kernel’s (§I.4–I.5); an illegal candidate is skipped in either window and is never a termination. Identical candidates have identical legality, so their collapse never changes which candidate a scan reaches first — it only keeps retries from consuming the cap.

II.4 Clocks

Each selected Ply charges its mover elapsed = max(0, t(ply) − anchor), the anchor being the boundary T of its slot; the charge is spent per the session’s time_control under the rules of Time Accounting — Sanki (fixed bank / Fischer, quota periods, period transitions). A selected premove charges zero. If the mover’s clock flags on the charge, the session terminates timeout against the mover at that Ply (played-Ply timeout); the position is still the one the Ply produces.

II.5 Chain replay and termination

Starting from the pairing’s initial position, each selected Ply is applied by the position kernel; after each, the clocks are charged (§II.4) and then the position is classified (§I.8). The first Ply that terminates — by timeout or by any status of §I.8 — ends the session at that Ply; later slots are irrelevant.

II.6 Post-chain resolution

If the chain replays to an ongoing position — because a slot is unfilled, or because the cutoff cuts the candidates short — the invocation is resolved at the cutoff, in this order:

  1. agreement (50/50): the last selected Ply carries the draw flag and the invoker is its signer’s opponent;
  2. abandonment timeout: the player on move has let their clock, charged from the chain’s anchor — the boundary T the next slot would have (§II.3): the maximum canonical timing among the selected Plies, never rewound by a selected premove, t₀ for an empty chain — to the cutoff, expire — decisive against them;
  3. resignation: otherwise, decisive against the invoker — the residual reading of an invocation with no objective cause.

The order is normative: an invocation by the offeree while a draw offer stands is an acceptance; an invocation while the on-move player’s clock has expired is a win-on-time claim (or a loss on time if the invoker is that player); anything else is a resignation. A conforming invocation therefore always yields a verdict.

Which invocation. When several conforming invocations exist, the one with the smallest canonical timing (then the smallest event id) is the session’s; the verdict is a function of that cutoff. Under ADR-0033 this is the slot rule of the Conclusion event, and a Conclusion whose stated verdict differs from the kernel’s for its own cutoff is invalid.


Part III — Conformance

III.1 The corpus

The conformance corpus is the kernel’s test suite: the vectors every build of the reference module runs through, in two runtimes and with byte-identical answers, before it is published (Kernel ABI — Sanki §Conformance of a build). It is kept with the module’s sources and published as documentation beside this text (Sanki conformance vectors, categories legality, selection, time, scenarios, clock, puzzle); it names nothing by digest and arbitrates between no implementations, there being one. It is composed of:

A vector is never edited under a published rule system: a vector found wrong is a defect of the kernel or of its module (§Identification and standing), repaired by a new rule system whose corpus covers the case.

III.2 The reference module

Sashité’s reference implementation of this kernel is the sashite-sanki-engine crate (position kernel and clocks) with its session layer, the sashite-sanki-session crate (Part II over an abstract event model), compiled through sashite-sanki-kernel-wasm — the ABI crate, which computes nothing of its own — into the reference module: a WebAssembly module under sashite.sanki.kernel-abi/1 (Kernel ABI — Sanki), named by digest by Sashité’s Rule System event (kind 3417; Rule System — Sanki). The verdict of a session is by definition that module’s output on the session’s public events; this document describes what that output is, and a divergence between the two is a defect of the module, repaired forward by a new rule system (ADR-0034 §8) — the price of having no residual prose ambiguity is that a defect of the build is the rule for the sessions that name it, which is why a build is verified before publication as the ABI requires: the corpus and a random-session differential against the native engine, run through the module in a browser’s WebAssembly engine and in wasmi with byte-identical responses, and the extreme session of the ABI’s bounds completing in both.


Appendix A — The atoms and where they live

Atom Fixed rule Parameterised by Reference engine
E the five effects, recognised on the form of the possibility promotion table; capture mutation apply.rs, legality/resolve.rs, capture.rs
A1 attack, derived from the GGN — movement/attack.rs
A2 check marker — canonicalize/check_marker.rs
A2′ no marker in hand — capture.rs (to move under canonicalize)
A3 double-step marker strip foot-soldier letter, home rank canonicalize/double_step.rs
A4 en passant window iff capture legal foot-soldier letter, direction canonicalize/en_passant_flag.rs
A5 castling rights, derived from the GGN — canonicalize/castling_rights.rs
A6 royal safety — legality/self_check.rs
A7 drop restrictions: nifu, uchifuzume the letters concerned by each (may be empty) legality/drops.rs, terminal/uchifuzume.rs
A8 rejection, never a sanction — kernel/step.rs
A9 terminations and their order stalemate outcome; dead-position tables; repetition, move-limit, move-cap thresholds; irreversible class terminal/*
S1 slots, play order, selection, cap K session layer
S2 clock accounting, two timeout flavours time_control (a founding term) clock.rs, session layer
S3 post-chain resolution and its order; which invocation — session layer

Changing anything in either column, or any GGN table, is a new module and therefore a new rule system — one revision rule, without exception (ADR-0034 §1). A change in the “Fixed rule” column is also a new kernel identifier; a change confined to the “Parameterised by” column, or to a table, keeps sashite.sanki.kernel/1 and is a new module under the same kernel.

Appendix B — Parameters the kernel reads

variants.<v>.style, .pieces, .ggn (the variant’s movement table itself), .initial, .foot_soldier, .promotion, .drops (.letters, .nifu, .uchifuzume), .capture; pairings.<pair>.dead_positions; session.stalemate, .repetition, .move_limit, .move_cap, .irreversible, .candidate_cap (K). They are compiled into the reference module and reported, in this shape, by its describe.parameters — documentary: a consumer reads them to know the rules, never to compute them (Kernel ABI — Sanki §describe; Rule System — Sanki §The module).

References