Stage S-1a: parity hashing covers the whole dictionary; failures are logged, counted and marked INCOMPLETE (FABRIC-3.9.0 §8.1)

- MAX_DICT_ENTRIES (2048) removed: the entry list is allocated to the
  dictionary's size; a cyclic chain is detected (Floyd) instead of capped.
- Colon-body walk bounded by vm->here, not a 1024-cell cap.
- Every failure prints PARITY:INCOMPLETE #n with its full reason, logs at
  LOG_ERROR, bumps sk_parity_incomplete_count(), and the record reads
  hash=INCOMPLETE / PARITY:FAIL code=4 rather than a plausible hash.
- Pre-existing #GP fixed: the walker dereferenced inline body data (S"
  bytes, offsets); it now follows only known dictionary entries.
- LIT operands that are entry pointers are hashed by word_id, not address,
  so a mature dictionary hashes identically across ISAs.

Probe (reverted): 563 -> 2563 words hashed, identical on amd64/aarch64/
riscv64; cyclic chain -> INCOMPLETE, counter 2. Boot parity unchanged
(531, 0x572daac83b797332). 3-ISA acceptance passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-24 12:51:34 -04:00
co-authored by Claude Opus 5.5
parent 9bf2e8ec95
commit ee3db96ea8
35 changed files with 164134 additions and 37 deletions
+29 -5
View File
@@ -66,6 +66,13 @@
#define SK_BOOTSTRAP_ARENA_FAIL 1
#define SK_BOOTSTRAP_INIT_FAIL 2
#define SK_BOOTSTRAP_DICT_FAIL 3
/* FABRIC-3.9.0.md §8.1 (Stage S-1a, 2026-09-24): the canonical dictionary
* hash could not cover the whole dictionary (a cyclic/corrupt link chain,
* the entry list could not be allocated, or a colon body ran off the end
* of VM memory without EXIT). The M7.1a line prints hash=INCOMPLETE and the
* result line PARITY:FAIL with this code -- never a plausible-looking hash
* computed over part of the dictionary. */
#define SK_BOOTSTRAP_PARITY_INCOMPLETE 4
/**
* ParityPacket - Summary of VM state for comparison
@@ -79,6 +86,8 @@ typedef struct {
uint32_t here_offset; /* vm->here (bytes used in dictionary) */
uint32_t latest_word_id; /* vm->latest->word_id (stable ID) */
uint64_t header_hash64; /* Canonical dictionary hash (FNV-1a) */
int hash_complete; /* 1 if header_hash64 covers the whole dictionary;
* 0 = INCOMPLETE, header_hash64 meaningless (S-1a) */
/* === M7.1b: POST Parity === */
uint32_t tests_total; /* Total tests executed */
@@ -135,21 +144,36 @@ void sk_parity_print(const ParityPacket *pkt);
* - physics (runtime)
* - transition_metrics (pointer)
*
* @param vm Pointer to VM instance
* @return 64-bit FNV-1a hash
* FABRIC-3.9.0.md §8.1 (Stage S-1a, 2026-09-24): covers the WHOLE
* dictionary -- no fixed cap on entries (was 2048) or on a colon body's
* cells (was 1024). *complete is set to 1 if every entry and every colon
* body was hashed; to 0 if the link chain is cyclic (corrupt), the entry
* list could not be allocated, or a colon body runs off the end of VM
* memory without EXIT. On 0 the returned value is meaningless, the failure
* is logged at LOG_ERROR, and sk_parity_incomplete_count() is bumped.
*
* @param vm Pointer to VM instance
* @param complete Out: 1 = complete, 0 = incomplete (must not be NULL)
* @return 64-bit FNV-1a hash (valid only when *complete == 1)
*/
uint64_t sk_dict_canonical_hash(struct VM *vm);
uint64_t sk_dict_canonical_hash(struct VM *vm, int *complete);
/*
* sk_dict_word_count - Count dictionary entries
*
* Traverses from vm->latest to NULL.
* Traverses from vm->latest to NULL -- the whole chain, no cap (S-1a). A
* cyclic (corrupt) chain is detected rather than walked forever: it is
* logged, counted, and 0 is returned.
*
* @param vm Pointer to VM instance
* @return Number of dictionary entries
* @return Number of dictionary entries (0 on a cyclic chain)
*/
uint32_t sk_dict_word_count(struct VM *vm);
/* Number of times a dictionary walk or canonical hash has come out
* incomplete since boot (S-1a) -- 0 in any healthy run. */
uint32_t sk_parity_incomplete_count(void);
/**
* FNV-1a constants
*/