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

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

This page renders the kernel specification whose SHA-256 the Sanki rule-system manifest carries as kernel.digest. The normative bytes are the blob cd7d2eb232ef7240e0275c79458a9c9dfec08d0c2da824ef68738003c48cdbf5; this page is a courtesy rendering of the same text.

This document specifies the kernel that the Sanki rule-system document names — the fixed rules that the document’s data (the GGN movement tables and the parameters of Rule System — Sanki) parameterise. It is the document whose SHA-256 the manifest carries as kernel.digest, and it is written to be the load-bearing half of an executable contract: everything a session’s verdict depends on is either stated here, incorporated here by digest, or carried by the rule-system document; 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 contract

Field of the manifest Value Meaning
kernel.id sashite.sanki.kernel/1 the name of this set of rules; a consumer that does not implement it MUST refuse the manifest
kernel.digest the SHA-256 of this document as published pins the text — two parties agreeing on the identifier agree on these words
kernel.conformance the SHA-256 of the conformance corpus (§Conformance) pins the executable form of the same rules
kernel.reference (optional) the SHA-256 of a reference implementation pins one program whose output is the verdict

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 kernel.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) 171f71cbccf471c5952fef31688a8bf58d6d159bd2abf59f5efeb255e450c65b
Move Encoding — Sanki the ply content, the slot structure, the selection rule and the cap K (§II.2–II.3) ed49e170861d0c4fadc9c9789579479d7250e5ef9653227d5a7fb998ef4bf756
Statuses — Sanki the status vocabulary, the implicit conventions and the verdict resolution (§II.5–II.6) 82eb9648a58fcc511198758fe39d1cdcb00c292401deece80f6394c9247380e7
GGN v1.0.0 the movement-table format the position kernel reads c054ef693a1f6c3b367d94390c512e581730a39ac85c0a8f40bba921cce175b4

The corpus rules over the prose. An implementation that passes the conformance corpus is a conforming implementation of this kernel. Where the prose of this document or of an incorporated document admits two readings and the corpus decides between them, the corpus is right. Where the corpus is silent and two conforming implementations diverge, the kernel is defective: the defect is repaired by publishing a new kernel identifier with a corpus that covers the case — never by editing this document or its corpus under the same identifier, since sessions founded under it have signed these exact digests.

No discretion. Nothing in this kernel is “MAY” for the party computing a verdict. The one place a rule-system document could leave a choice open — a non-deterministic initial position — is excluded: a Sanki rule-system document MUST prescribe a deterministic initial position for every pairing it defines (Rule System — Sanki §Variants).


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 documents 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 documents 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 manifest’s 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 manifest carries no castling vocabulary.

The initial position of a pairing carries no - markers and carries + only on the pieces the rule-system document lists (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. 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. At each slot the selection rule picks the canonical Ply among the slot’s candidates: the boundary T is the maximum canonical timing among the preceding half-moves (t₀ for the first), never rewound by a selected premove; candidates timed before T form the anterior window (premoves), the others the informed window (live moves); the canonical Ply is the latest legal anterior candidate among the K most recent, else the earliest legal informed candidate among the K earliest, else the slot is unfilled and the chain stops there. K = 8 (parameter of the deployment, identical for every consumer of a session — it is part of the rules document’s session object). Legality is the position kernel’s (§I.4–I.5); an illegal candidate is skipped in either window and is never a termination.

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 last anchor (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 executable form of this kernel. It is published as one JSON object merging its categories under their names (legality, selection, time, scenarios, clock, puzzle), serialised with sorted keys and no insignificant whitespace; the SHA-256 of that document is kernel.conformance. It is composed of:

Every vector carries the digests of the rule-system document it was computed under. A vector is never edited: a wrong vector is a kernel defect (§Identification and contract).

III.2 Reference implementation

Sashité’s reference implementation of this kernel is the sashite-sanki-engine crate (position kernel and clocks) with its session layer. When the manifest carries kernel.reference, that digest names a build of it (a WASM module with a fixed ABI: natural_state(events) → verdict), and the verdict of a session is by definition that program’s output. Publishing the reference digest removes even the residual risk of prose ambiguity, at the price that a defect of the build is the rule until a new kernel is published.


Appendix A — The atoms and where they live

Atom Fixed rule Parameterised by Reference module
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 the “Fixed rule” column is a new kernel.id. Changing anything in the “Parameterised by” column, or any GGN table, is a new rule-system document under the same kernel.

Appendix B — Manifest members the kernel reads

variants.<v>.style, .pieces, .ggn, .initial, .foot_soldier, .promotion, .drops (.letters, .nifu, .uchifuzume), .capture; pairings.<pair>.dead_positions; session.stalemate, .repetition, .move_limit, .move_cap, .irreversible, .candidate_cap (K). Any other member is ignored by the kernel but MUST be known to the manifest format (Rule System — Sanki §Structure: unknown members are rejected).

References