Stage S-1b: dictionary word ids grow with the dictionary; a word that can't get an id is refused whole (FABRIC-3.9.0 §8.1)

- word_id_map / recycled_word_ids are heap tables (one shared capacity,
  64 doubling), grown all-or-nothing by allocate-copy-free. Numbering is
  unchanged, so boot parity is unchanged.
- vm_create_word() takes the id before linking; on failure it frees the
  entry, sets vm->error, logs LOG_ERROR and counts
  (vm_dictionary_word_id_refusals()). No word runs with WORD_ID_INVALID.
- Readers of the id space bound by vm->next_word_id, not DICTIONARY_SIZE.
- transition_heat gets its own capacity and grows on record (was sized
  once -- a heap overflow under a dynamic bound); every reader bounds by it.
- Stadium id->cell rows start empty and grow on dispatch; a failed grow is
  logged, counted and not admitted. S-2 (the cell pool) stays open.

Probe (reverted), identical on amd64/aarch64/riscv64: +2000 words get real
ids (capacity 4096); a word at id 2738 lands 200x in the rolling window and
is Stadium-admitted; FORGET recycles across the old boundary; an injected
grow failure refuses the word whole (NULL, error, counter, dictionary
unchanged) and the retry succeeds. 3-ISA acceptance passes; boot parity
(531, 0x572daac83b797332) and the dict_hash set unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-24 13:59:43 -04:00
co-authored by Claude Opus 5.5
parent ee3db96ea8
commit 1dbd0c74ed
26 changed files with 55442 additions and 83 deletions
+4 -4
View File
@@ -92,7 +92,7 @@ void physics_pre_execute(VM* vm, DictEntry* word, DictEntry* prev)
physics_execution_heat_increment(word);
uint32_t word_id = word->word_id;
if (word_id >= DICTIONARY_SIZE) return;
if (word_id >= vm->next_word_id) return; /* S-1b: ids are bounded by next_word_id, not DICTIONARY_SIZE */
/* Loop #2: Rolling Window of Truth */
rolling_window_record_execution(&vm->rolling_window, word_id);
@@ -116,15 +116,15 @@ void physics_pre_execute(VM* vm, DictEntry* word, DictEntry* prev)
}
/* Record transition from previous word to current word */
transition_metrics_record(prev->transition_metrics, word_id, DICTIONARY_SIZE);
transition_metrics_record(prev->transition_metrics, word_id, vm->next_word_id);
/* Update probability cache to find most likely next word */
transition_metrics_update_cache(prev->transition_metrics, DICTIONARY_SIZE);
transition_metrics_update_cache(prev->transition_metrics, vm->next_word_id);
uint32_t speculated_word_id = prev->transition_metrics->most_likely_next_word_id;
/* Speculative prefetch of most likely next word */
if (speculated_word_id < DICTIONARY_SIZE &&
if (speculated_word_id < vm->next_word_id &&
transition_metrics_should_speculate(prev->transition_metrics, speculated_word_id))
{
DictEntry* spec_entry = vm_dictionary_lookup_by_word_id(vm, speculated_word_id);