Conformance Process
FRAMING. Construction + Conformance = definitional pair (specification + matching test specification). Design-time for producer and verifier authors. At runtime a verifier applies those same rules while measuring: record tokens, including mismatch. That is not a reject gate that skips measurement, and it is not admission. Admission policy values are not a conformance target. This specification defines no tip-versus-last-beacon submit deadline.
Responsibility. Test-specification half of the Construction + Conformance definitional pair: yes/no rules, permanent negatives, and the one pack. Also the process for claiming an implementation matches that pack. Used by a producer or verifier implementer who wants that claim.
Source. Satoshi Hike 2.3 (tag spec-2.3.0). Section numbers are the specification section numbers:
- §16 Conformance
- §16.1 Profile conformance-small
- §16.2 Implementation requirements
- §17 Clarifications
- §19 Fixed issues — do not reintroduce
16. Conformance
Informative. Summary. Conformance is per layer. Matching the one pack byte for byte, and reproducing every permanent negative as a non-match, are part of conformance. They are not the whole of it. Section 16.2 states the rest, including Rules I1–I6. The admission service is not a conformance target. Section 16.1 defines the conformance-small profile, and 16.2 the SDK requirements (Rules I1–I6).
Normative.
Conformance is stated separately for three layers. A layer does not take on another layer’s job.
- A conforming producer follows section 13 and emits the deterministic encoding of section 7.3 for every run that reaches
I_17. It records evidence. It does not emit a verify record. It does not sayaccept,refuse,revoke, orheld. A producer that pauses a leg for a post, a solve, an ask, a header or peer read, a log write, or a wall clock does not follow section 13 (Rule A6). A producer that withholds the object because of a clock-read failure or another transitory network condition does not follow section 13. Stage 0 has nopathkey in that encoding. - A conforming verifier follows section 14 and outputs the verify record: measurements as integers and the section 4 tokens. The profile name and, under
conformance-small,SAMPLE_BYTES,SLOT_BYTES,TERRAIN_BYTES, andSTEPS_PER_LEGare verifier inputs. They appear on the section 14 verify record as measurement provenance. The profile is not absent from that record. The hike object gains no new field. It does not judge those measurements. It does not sayaccept,refuse,revoke, orheld. It does not post a waypoint and it does not ask Bari for a path. - An admission service is not a conformance target. Section 15 is policy for a relying party. Lever values (section 15.4) are policy and are not set by this file. They are not vectors. A service may run section 14 itself and may ignore a submitter-supplied verify record. Doing so does not make the service conform or fail to conform.
Sections 8–14 do not use pass, fail, reject, deny, or admit for the object or for a measurement. Those outcomes are not verifier outputs. The decisions accept, refuse, revoke, and held, and the states in section 15.2, are section 15 only.
Vectors. Conformance means Part A, the Hashcash rule of section 13.1, and the Bari paid rule of section 13.6. A producer or a verifier conforms when its outputs equal the published vectors of the one pack, spec/conformance/pack/, byte for byte, and when it reproduces every permanent negative as a non-match. The pack publishes the seed, the NL4 base words, the terrain digests, the §11.1 fold, the conformance-small leg, checkpoint, and waypoint bytes, one production-size leg, waypoint_equals_seed, the integer rate and anchor comparisons, the thorough-round index draw, the Hashcash challenge and digest for a stated origin and fee header, the Bari paid rule for current, before, after, and outside fee headers (including a wrong origin, and a display-order mismatch: a current, a before, and an after case each built with Bari’s tip hash reversed, each answered 402 under section 9), with the “after” and “wrong nBits” cases published separately for the live plane (mainnet target, including a retarget boundary) and the test plane (fixed synth nBits, section 9), each vector labelled with its plane, the unknown-kind record, and the sign-in wire (sign-in.json, sign-in-origins.json). It does not publish a hike-object encoding, a section 14 record, object_hex, or draw_bytes_hex.
Permanent negatives. The pack carries, and every SDK must reject as non-matching, at least the thirteen negatives below. Each negative is computed at the vector’s profile integers, for the vector’s public key and seed beacon, with the section 10.4 terrain and the section 11.1 step except where its entry says otherwise. A leg negative is the first leg of stage 0, from its Rule A2.2 start (the seed), and is published as legs_le_hex of that leg. A terrain negative is published as the SHA-256 of the whole terrain array. Each entry is a construction this file forbids; it is defined here only so that the pack can pin it as a non-match. The pack generator compares each negative with the honest value of the same vector and stops with an error when they are equal; QA then chooses other fixture inputs. A negative is never removed while the construction it guards stands.
slot_summary_fold,xor_accumulator_fold,xor_w_fold,s_plus_w_fold: the four folds that section 11.1 names. Leg negatives.prefold_hoist: the fold that computes once per slotpre(i) = (sum over w of mix64(word(w) XOR w)) mod 2^64and, on every step that reads sloti, usesS_folded = (pre(i) + S) mod 2^64in place of the section 11.1 accumulator, then the section 11.1 seal. Leg negative. It is the per-slot summary that section 11.1 forbids:word(w) XOR wdoes not depend onS.final_stage_hoist: the fold whoseS_foldedisx XOR (x >> 31), wherexis the XOR, over every word of the slot, ofmix64applied toword(w) XOR Swithout its last line (the valueyjust beforereturn y XOR (y >> 31)in section 10.1). Leg negative. It is the hoist that section 11.1 describes as informative. Its value equals an XOR accumulator ofmix64(word(w) XOR S).partial_slot_read: the section 11.1 fold over word 0 only of the slot each step reads. Leg negative.partial_slot_read_2047: the section 11.1 fold over words0 … WORDS_PER_SLOT − 2of the slot each step reads, leaving out the last word. Under section 6 that is 2047 of the 2048 words. Leg negative.one_lane_flip: the section 10.4 fill with the one-lane map of section 10.4 in place of the NL4 base word. Parents and the slot mix are unchanged. Terrain negative.all4_kernel: the section 10.4 fill with the linear map of section 10.4 in place of the NL4 base word. Parents and the slot mix are unchanged. Terrain negative.f3_shared_terrain: the first leg of stage 0 of the vector’s public key, from that key’s own start, run on the terrain of a second public key that the vector states, with the same seed beacon. Leg negative. Thef3in the name is not row F3 of section 19. This negative guards row F4, shared terrain.cached_leg: the first leg of stage 0 in which a step that reads a slot already read earlier in the same leg reuses theS_foldedof that earlier read instead of folding the slot with the currentS. Leg negative. The vector’s leg reads at least one slot twice.squeezed_seed_terrain: the section 10.4 fill with each of the four lanes replaced by the one 64-bit valueP[0] XOR P[1] XOR P[2] XOR P[3], the squeeze that section 10.2 forbids. Terrain negative.
The vectors are language-agnostic. The sign-in wire vectors are not a section 14 record; matching them is not Part A conformance. A vector compares bytes, integers, and tokens. It does not grade a hike object as pass or fail. This pack does not publish section 15 states. Those states are not a conformance target. A section 14 record is defined by section 14. An unknown kind is the Rule A5 line: kind and kind_text only. A current object’s record carries every key section 14 requires, including shape_item, headers, paths, anchors, bookends, N, and S. The compact line that named only profile, shape, checkpoints, waypoints, leg_rate, six_leg, and anchors is not a conformance output. A conforming verifier outputs this section 14 record.
Precedence. This file governs, together with the pack. Where a published vector and a sentence of this file conflict, the conflict is a defect in this specification. It blocks release until a specification change settles which side is right. An implementer reports the conflict and does not pick a side. Where any SDK disagrees with this file or the pack, this file and the pack win and the SDK has a bug, whatever that SDK’s role as a reference implementation (section 16.2). No SDK output settles a conflict, and an SDK behaviour that skips work never becomes conforming because an SDK does it. The conformance runner may mark a vector known-defect until the specification change settles it.
spec/conformance/pack/ is the one pack. No other pack exists, and no file or sentence of this specification names another. Its files and the bytes they pin are listed in manifest.json and README.md in that directory. kat-small.json and test-3leg.json pin terrain digests and per-stage leg, checkpoint, and waypoint bytes at the profile integers in each file. legs_le_hex is the hexadecimal spelling of the little-endian wire bytes le64(leg_result). It is not the %016llx spelling of the integer. They do not pin object_hex, a section 14 record, bookends, or a six-leg draw. The pack is pinned by the SHA-256 of its manifest, recorded in sdk/SPEC_PIN beside the tag of this file (Rule I5). Renaming, adding, or removing a vector changes the pin. No git tag is cut, and no deployment cuts over to KIND v2.2-pcoc-object, until every SDK passes this pack and the production-size vectors. This conformance process document and its siblings under spec/ carry the Satoshi Hike 2.3 rules (tag spec-2.3.0). This file does not create that tag.
When two implementations follow this file at the production constants in section 6, they agree on the terrain bytes, each leg result, each checkpoint, the seed, and each waypoint W_1 … W_17. Given the same path-body bytes and the same headers, they agree on the deterministic encoding of the object.
Section 10 defines the seed, the seed preimage, the NL4 word, and every terrain byte. There is no fill pin. The checkpoint preimage and the waypoint preimage are defined in sections 11 and 12. The leg is defined in section 11. Path bytes on stages 1–17 are the checked body of the successful fetch in section 13.1, or the empty path bytes section 13.3 stores on path_skip or on not fetched by design. A verifier does not fetch those bytes again. Section 14.3 records skip, present, plant match or mismatch or not read or n/a, and d_g. Each verification draws its own six indices, as section 14.6 states, and records each recomputed leg result against the stored leg result.
16.1 Profile conformance-small
The profile in force is the section 6 constants, unless the verifier is running conformance-small. That profile may differ only in SAMPLE_BYTES, SLOT_BYTES, TERRAIN_BYTES, and STEPS_PER_LEG. The profile name and those four integers are verifier inputs. The hike object gains no new field. The verify record must state the profile, and must state SAMPLE_BYTES, SLOT_BYTES, TERRAIN_BYTES, and STEPS_PER_LEG when the profile is conformance-small. A live object still carries the section 6 size fields. When the profile name is conformance-small, all four integers are required inputs. The verifier does not substitute the section 6 values for an omitted integer. It does not change KIND. A verify CLI that passes --profile conformance-small without all four integers is a usage error, exit code 2. This file assigns no default integers to that name. The record states the four integers it was given.
The profile may set only these section 6 parameters:
| Parameter | Rule ID | Why it may shrink |
|---|---|---|
SAMPLE_BYTES |
P6.5 | It is the terrain-sample limb of every checkpoint preimage. |
SLOT_BYTES |
P6.1 | It is the slot the step reads and the slot-sample limb of every waypoint preimage. |
TERRAIN_BYTES |
P6.3 | It is the terrain fill. |
STEPS_PER_LEG |
P6.7 | It is the number of steps in one leg. |
WORDS_PER_SLOT, N_SLOTS, and OFFSET_MODULUS are not free integers under the profile. They stay the section 6 formulas (P6.23, P6.24, P6.25) applied to the profile values. SLOT_BYTES is at least 8, a multiple of 8, and at most the section 6 value. STEPS_PER_LEG is at least 1 and at most the section 6 value. SAMPLE_BYTES is at least 1 and at most the section 6 value. TERRAIN_BYTES is at most the section 6 value, an exact multiple of SLOT_BYTES, and at least SAMPLE_BYTES, so OFFSET_MODULUS stays at least 1. The step folds every word of the slot by §11.1: word(w) XOR S, then wrapping addition of mix64, for w = 0 … WORDS_PER_SLOT − 1. There is no w term. Equal words do not cancel under addition. The checkpoint preimage stays 32 + 32 + SAMPLE_BYTES + 8 bytes. The waypoint preimage stays 32 + SLOT_BYTES + 32 + 32 bytes. Hash order does not change.
The profile does not set STAGES, HASH_BYTES, LEG_RESULT_BYTES, PUBLIC_KEY_BYTES, BEACON_BYTES, SEAL_DOMAIN, KIND, FILL, or the mix constants P6.17–P6.22. It does not set a domain label on the seed. It does not set the Hashcash difficulty (P13.5), the ask window (P13.2), or the six-leg draw count (A4.4.1). A thread pool is not required (A4.4.4). Eighteen stages stay eighteen. Waypoint indices, W_17, and the cut-off stay where section 13 puts them.
Legs. legs_per_stage is vector fixture metadata, not a section 6 constant. It is a list of 18 integers, one per stage, each at least 1. Producer KATs use it: the rig’s tip schedule follows that list, and the vector’s object stores those counts as leg_count. Production produce does not read legs_per_stage. Principle 9 still holds: there is no maximum leg count on a live object. A vector that exercises Rule A4.4 needs at least 6 legs in the hike, because fewer than 6 leaves six_leg not defined. The Rule A4.4 draw count stays 6. The thorough-round draw vector is the section 15.5 draw and draws D indices (Thorough rounds, below).
Thorough rounds. The thorough-round index draw vector states its own thorough_rounds, as vector fixture metadata like legs_per_stage. That value is not a policy value, it is not read from section 15.4, and it sets no policy. The vector draws D = 6 · thorough_rounds indices under section 15.5 from its own byte source (Draw bytes, below), read from the first byte. A vector that publishes both a six-leg draw and a thorough draw, or draws for more than one leg total, reads each draw from the first byte of its byte source. No draw continues where another stopped.
Anchor comparison. The integer anchor vector gives a stored stage height H and a candidate inclusion height. Its answer is anchored when the inclusion height is H plus one of ANCHOR_WINDOW_OFFSETS, and not anchored otherwise, as if that header were an interrupt stored on the object and its plant comparison were match. It does not test those two conditions. Rule A4.5 still requires both on a real object. The integer anchor vector is not the anchor-window vector of sections 9 and 14.4, which is a full object.
Draw bytes. Injected draw bytes are rig-only, for a deterministic draw vector: the Rule A4.4 six-leg draw, or the section 15.5 thorough-round draw. A published draw vector names its byte source as a SHA-256 chain from a stated ASCII label: b_0 = SHA-256(label) and b_(n+1) = SHA-256(b_n), concatenated in that order. The pack publishes the label, not the bytes. The rig expands the label into that chain and supplies the bytes in order as the section 14.6 byte source. The verifier consumes them in that order and does not hash them again. They are never a production input. Production verify reads the operating-system CSPRNG. The Rule A4.4 draw count stays 6; the thorough-round draw vector draws D indices (Thorough rounds, above). Fewer than six legs leaves the draw unconsumed and six_leg not defined.
Wire. The hike object gains no new field. Its size fields stay slot_bytes, n_slots, sample_bytes, and steps_per_leg. Under conformance-small those fields hold the verifier’s four integers, and n_slots is the formula. shape_item includes constant when a field differs from the profile in force (A4.1.2). A live producer emits section 6 only.
Rig checks. Whether the six recomputes run together is Rule A4.4.4. A rig does not add a required pool. A rig may also check the producer rules in this file: no leg is paused (Rule A6), the object closes at I_17 with no collection window (section 13.1), exactly one resend after a transport failure (P13.13), and no stop on a failed tip read or a stalled header source (P9.12). Rig-only inputs, not specification parameters, are now_unix_s, nonce_store_start_unix_s (a store restart or flush, and each worker’s process start), the issuing nonce, phase timing, the local mirror contents, a Bari mock, a header-source mock and its clock, injected draw bytes, and the block-height (tip) clock.
The same profile integers and the same inputs produce the same bytes. The profile does not relax an INVARIANT. QA chooses the integers in the published vectors. This section does not fix a second set of production numbers.
16.2 Implementation requirements
Normative.
Rule I1. SDKs are the reference implementations
The per-language SDKs under sdk/ (Rust, Go, Java, C, and any SDK added under these rules) are the reference implementations. There is no other reference tree. Bari, the admission service, and the apps use an SDK; they do not carry their own copy of the construction or of the Hashcash rule. An SDK is a reference implementation only while it passes the whole pack. This file and the pack win over every SDK (section 16, Precedence).
Rule I2. Interfaces
Each SDK exposes at least: the seed and terrain fill (section 10); the leg and the step (section 11); the checkpoint (section 11.4); the waypoint (section 12.2); the Hashcash challenge, digest, and solve (section 13.1); the Bari paid-rule check (Rule BR1); the object encode and decode (section 7.3); the section 14 verify record, including Rule A5; the section 14.6 draw and the section 15.5 thorough draw, each from a supplied byte source; the Rule B2 integer comparison; and the integer anchor comparison (section 16.1, Anchor comparison). Each public function names the section it implements. Its behaviour is that section. An SDK’s own prose does not add a rule.
Rule I3. Producer model
An SDK producer runs the walk on its own thread or threads and runs every post, solve, ask, header-source read, Bari exchange, and log write as asynchronous work (Rule A6). The walk reads the tip through a call that returns at once. No SDK producer sleeps, waits on I/O, takes a lock that I/O holds, or syncs a file on the walk path. A failure of asynchronous work becomes an alert record or a logged miss; it never propagates into the walk. Only a producer failure or explicit cancellation ends a run before I_17. The header source detects a stalled or lagging peer and recovers on its own, asynchronously; a stall never pauses a leg.
Rule I4. Tests
Each SDK runs, on every change: the whole pack; every permanent negative as a non-match; the production-size leg vector; a producer test that injects a stalled header source, a failed tip read, a Bari that never answers, a Bari that answers 503, and a log that cannot be written, and checks that legs keep running, that exactly one resend is made after a transport failure, and that the object closes at I_17; a header-source test, on a mock clock, with one silent peer among HEADER_PEERS_MIN or more, which checks that the silent peer is replaced once HEADER_PEER_SILENCE_SECONDS have passed after another peer announced a header and not before, that a gap in which no peer announces a header replaces no peer, that a fall below HEADER_PEERS_MIN writes a header_peers_low alert and reconnects asynchronously, and that legs keep running and the object closes at I_17 throughout; and a test that a paid waypoint on each of the current, before, and after headers is accepted by its Bari paid-rule check, and that any other POST is answered 402, including a fee header that matches Bari’s tip only when one side is reversed to display order (section 9, P9.17); and, for an SDK that implements the section 15.1 Sign-in door, a nonce-store test on a mock clock in three cases: (a) a submission consumes its nonce, the single-process door restarts while that challenge’s window is still open and drops every row, and the same submission is replayed; (b) the shared nonce-row store is flushed with no process restart, while the window is open, and the same submission is replayed; (c) two door workers with different process starts share one store and receive the same challenge. The test checks that each replay is client malformed, consumes nothing, and runs no policy; that a challenge issued at or after nonce_store_start_unix_s + challenge_clock_skew_seconds is accepted and one issued earlier is malformed; and that both workers in (c) use the one store start and give the same answer for the same challenge.
Rule I5. Versioning and pin
An SDK version is MAJOR.MINOR.PATCH, with MAJOR.MINOR equal to this file’s version, 2.3. A PATCH release does not change any output byte. Each SDK reads sdk/SPEC_PIN, which names the tag of this file and the SHA-256 of the pack manifest, and its test fails when the pack it runs does not match that pin. SDK tags are sdk-<language>-v2.3.<patch>. This file’s tag is spec-2.3.0. When the SDKs move to 2.3, sdk/SPEC_PIN names spec-2.3.0. Until then the SDKs stay at 2.2, which produces and verifies the same bytes. An admission service whose pin is spec-2.3.0 reads a policy file whose spec is "2.2" or "2.3" (section 15.4). Bari and the admission service depend on the Rust SDK at such a tag or by path in the same tree; they do not pin any other tag.
Rule I6. Cost
No SDK implements a cache, table, summary, partial read, or any other shortcut in the terrain, the leg, the step, or the fold (section 11.1). Every step reads and folds the full slot of SLOT_BYTES. Where the host provides SHA-256 instructions, the SDK uses them (section 11.1). An SDK change that raises the measured leg rate at unchanged hardware is a suspected break. It is reviewed against the permanent negatives before release. A reviewed SIMD kernel that loads all SLOT_BYTES bytes of the selected slot and mixes every word may be released. Processing several words of one slot at once (SIMD, or compiler or JIT vectorisation) is the section 11.1 loop and is not an efficiency or a speed-up in the sense of section 11.1, provided that on every step all SLOT_BYTES bytes of the selected slot (16 KiB under the section 6 constants) are loaded, each of the WORDS_PER_SLOT words is mixed with the current S by mix64 with MIX_ADD, and the sum is the section 11.1 wrapping sum. Such a kernel’s rate gain is reported under this rule with equality, mutation and load evidence, and after that review it may be released. Skipping, caching, summarising or reusing any word or value across steps remains a break.
17. Clarifications
Informative.
The items below restate parts of the construction in the sections named here. This section does not add a second construction.
17.1 Waypoint path fetch
Section 13.1 posts W_1 … W_17. It does not post the seed. Asks are for W_1 … W_16 under section 13.1. W_17 is posted and is not asked. No ask is made after the close. Asks are asynchronous, and the object closes at I_17 with the checked paths that have arrived. Test and live use that one rule. On stages 1–17 the field is path, a CBOR byte string, in the key order of section 7.3. Stage 0 has no path key. Those bytes are not an input to section 11.4 or section 12.2. A POST that does not succeed, and an ask that is not a checked 200, are a miss. Produce stores empty bytes and writes path_skip <g> for W_1 … W_16, and continues. It does not withhold the object. W_17 writes not fetched by design 17. A skip is a measurement (Rule A4.5). Rule B4 judges it: anchored_total ≥ min_anchored_paths, wherever those waypoints sit. A present path is measured in section 14.3, including CanonicalPathBody validate_for and the chain bind. Verify does not GET the path again.
17.2 Portable-index height fetch during verify
Verify reads the portable index. For each stored header, including last_interrupt, it GETs {index-url}/index/{height} and records index_comparison and T under Rule A4.2. That header GET is separate from a plant GET of the same URL. A body with no usable header is header not read and can still be plant match. T is header.time, Bitcoin nTime, Unix seconds, only when index_comparison is match. Otherwise T is not read. header.time is the portable index’s record of nTime for the block whose hash is header.hash. index_comparison uses hash and height only. It does not use network. An unsuccessful header read is not read. A hash or height that differs, when the read returned them, is mismatch, and T is not read. That record is part of Rule A4.2. The admission service applies section 15 to the record. Verify does not GET an HTTP tip. Verify does not Observe.
A document whose network is synthetic can still record match on the plant comparison. A header that matches that document is not a mismatch for that network. A match against a synthetic document is not a mainnet fact.
17.3 Path and chain bind
Section 14.3 records plant match, mismatch, or not read for each present path among W_1 … W_16. The inclusion-height GET is made only when that height is in the window and that header is a stored interrupt. Otherwise plant is not read and the index is not read for that path. That not-issued not read is not a missing GET under Rule A4.11. not read also means an issued read that does not return a document. A document that was read and has no plant for that block, or whose plants do not match, or whose network is neither main nor synthetic, is mismatch. A document with no header member can still be plant match. anchored requires the inclusion height in the section 13.1 window, that header stored on the object as an interrupt, and plant match. Plant match alone is not enough. On mismatch and on not read, d_g is not defined. claimed_block is the path body’s block_hash and block_height when those fields can be read, otherwise not defined. The measurement is distinct from Rule A4.2. A skip is not a plant comparison. Verify does not GET the path from Bari again. The pack field index_reads lists only those issued plant heights.
A match on a document whose network is synthetic is not a mainnet fact.
17.4 Several words of one slot at once
Section 11.1 folds the WORDS_PER_SLOT words of the selected slot with a wrapping sum mod 2^64. That sum is associative and commutative, so a kernel that loads all SLOT_BYTES bytes of the slot (16 KiB under the section 6 constants) on every step and mixes several of its words with the current S at once, by SIMD or by compiler or JIT vectorisation, gives the same S_folded, seal, leg, checkpoint, and waypoint bytes as the one-word-at-a-time loop. It is the section 11.1 loop, not an efficiency (Rule I6). Every step still loads every byte of the slot and mixes every word with that step’s S. A kernel that skips, caches, summarises, or reuses any word or value across steps is a break whatever its output on a vector, and the permanent negatives of section 16 stay non-matching.
17.5 Floor baseline and the low-end host
Informative. This subsection adds no rule. Section 11.1’s informative paragraph calls commodity hardware with SHA-256 instructions, for example an AMD Ryzen 5 5600H, “the baseline that sets the floor and the gate policy.” Section 15.5 names the low end of the owned hosts a measurement covers: an Intel Core i7-10610U laptop, which has no SHA-NI. Section 15.5 is the measuring method. It says that method informs the value a policy sets, and that it does not set one. Section 11.1’s paragraph does not set a lever value either. The two sentences name two hosts. They do not set two lever values. Every host runs the same section 11.1 step.
19. Fixed issues — do not reintroduce
Normative. This is the only section of this file that names an earlier construction or an earlier defect. Each entry names a defect class, why it was a defect, and the rule of this file that forbids it. An implementation, a pack vector, or a later revision of this file that reintroduces any of them does not conform. An entry is never removed while the rule it points to stands. The labels F1–F15 are list positions, not rule IDs.
Republished from pcoc 1b9f632 (spec/ six-doc pack; same bytes at e8030ba). Raw markdown.