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:
co-authored by
Claude Opus 5.5
parent
9bf2e8ec95
commit
ee3db96ea8
@@ -145,3 +145,81 @@ Evidence is named **before** each stage runs.
|
||||
- `dict_hash` unchanged (531, `0x572daac83b797332`);
|
||||
- POST passes;
|
||||
- clean kernel and hosted builds.
|
||||
- [x] **Stage S-1a — parity hashing covers the whole dictionary (`FABRIC-3.9.0.md` §8.1, class C).**
|
||||
2026-09-24. Captain Bob: *"all as proposed, commit and start S-1."*
|
||||
|
||||
**The change** (`src/starkernel/vm/parity.c`, `include/starkernel/vm/parity.h`):
|
||||
- **`MAX_DICT_ENTRIES` (2048) removed.** The walk covers the whole link chain. The entry list
|
||||
is allocated to the dictionary's actual size (`kmalloc` in the kernel, `malloc` hosted) and
|
||||
freed after.
|
||||
- **Cycle check replaces the cap.** The only thing the cap guarded against, a corrupt cyclic
|
||||
chain, is now detected directly (Floyd: a second pointer moving two links per step).
|
||||
- **The colon-body walk is bounded by `vm->here`,** not a 1024-cell cap. A body that reaches
|
||||
`HERE` without `EXIT` or a NULL cell is marked incomplete, not quietly truncated.
|
||||
- **Failures follow §8.1's rules.** Every failure (cyclic chain, entry list not allocated,
|
||||
malformed body) is:
|
||||
- printed in full to the console, `PARITY:INCOMPLETE #n -- <why>`. `log_message`'s line
|
||||
buffer is 64 bytes and cut every reason off mid-sentence.
|
||||
- logged at `LOG_ERROR` for the persisted record;
|
||||
- counted (`sk_parity_incomplete_count()`).
|
||||
- **An incomplete hash is marked as such.** The record prints `hash=INCOMPLETE` (new
|
||||
`ParityPacket.hash_complete`) and `PARITY:FAIL code=4`
|
||||
(`SK_BOOTSTRAP_PARITY_INCOMPLETE`), never a value that looks valid.
|
||||
- `sk_dict_word_count` returns 0 on a cyclic chain, logged and counted.
|
||||
|
||||
**Two pre-existing defects found by the probe, both fixed in the walker:**
|
||||
1. **The colon-body walker dereferenced inline data.** It treated every cell of a body as a
|
||||
`DictEntry*` and followed it. Only `LIT`'s operand was known to be data. `S"` string
|
||||
bytes, branch offsets and other inline operands were followed too.
|
||||
- This was harmless while parity only ever ran at early boot, before any colon bodies
|
||||
existed.
|
||||
- The first hash of a mature dictionary took a #GP (vector 13) and halted the kernel. This
|
||||
is the "interrupt exception" Captain Bob reported (`logs/20260924-094537/amd64/`,
|
||||
`-095154/amd64/`).
|
||||
- The **original** code faults the same way (`logs/20260924-095730/amd64/`), so the
|
||||
defect predates S-1a.
|
||||
- **Fix:** a cell is dereferenced only if it is a known dictionary entry, found by binary
|
||||
search in a sorted copy of the entry list. Any other cell is hashed as its raw value and
|
||||
never followed.
|
||||
2. **A `LIT` operand holding an entry pointer was hashed by address.** `['] WORD` compiles
|
||||
`LIT <DictEntry*>`, a native address that differs per ISA (seen in `ACL-BOOT`'s
|
||||
`ACL-PIN` sequences). So the mature-dictionary hash differed across ISAs:
|
||||
- amd64 `0x945a76bc6ace1fdc`, aarch64 `0x8cc1255e6a40c12e` (`-100252`, `-100732`).
|
||||
- **Fix:** rule 2 (word_id, never address) now applies to such an operand. The walker
|
||||
hashes a tag byte (`0xE7`) plus the entry's `word_id`, so it can't collide with a plain
|
||||
number. Any other literal is still hashed as its value.
|
||||
|
||||
A third defect, an incomplete counter one too high on aarch64 only (`-101803`, `-102504`,
|
||||
`-103207`, `-103820`), was not in parity. It was the out-of-bounds stack write fixed in its
|
||||
own commit (the entry above).
|
||||
|
||||
**Evidence.** A probe (reverted) at the end of boot:
|
||||
1. hashed Hera's full dictionary;
|
||||
2. registered 2,000 more words (`PZ0000`…) and hashed again;
|
||||
3. made the chain cyclic and asked for both the word count and the hash.
|
||||
|
||||
The results are byte-identical on all three ISAs (`logs/20260924-115524/amd64/`,
|
||||
`-115954/aarch64/`, `-120533/riscv64/`):
|
||||
|
||||
| Probe step | Result |
|
||||
|---|---|
|
||||
| before | `word_count=563 hash=0x224007f9af328111`, `PARITY:OK` |
|
||||
| after +2000 | `word_count=2563 hash=0xcf85876337e75a84`, `PARITY:OK`. Past the old 2048 cap: the whole dictionary is hashed. |
|
||||
| cyclic | `PARITY:INCOMPLETE #1`/`#2 -- dictionary link chain is cyclic (corrupt)`, two `[HADES][ERROR]` lines, `word_count=0 hash=INCOMPLETE`, `PARITY:FAIL code=4` |
|
||||
| counter | `incomplete_count=2` (word count + hash) |
|
||||
|
||||
The "after" record shows `latest_id=4294967295`. That is `WORD_ID_INVALID`: the 2,000
|
||||
probe words ran past the 1024 stable word ids, and each logged "out of stable IDs, disabling
|
||||
metrics". That is S-1b's defect, not S-1a's. The §8.1 row now notes that this path does log
|
||||
a warning, but neither counts nor refuses.
|
||||
|
||||
**Acceptance** on the clean tree, with the probe reverted (`logs/20260924-121207/amd64/`,
|
||||
`-122500/aarch64/`, `-123759/riscv64/`):
|
||||
- Boot M7.1a is unchanged: `word_count=531 hash=0x572daac83b797332`. At boot there are no
|
||||
colon bodies or entry-pointer literals yet, so neither fix changes the boot hash.
|
||||
- The `dict_hash` set is unchanged: Hera `0xda9742ce9c3376a2`, Hestia `0x413e7d086f342d8b`,
|
||||
Artemis `0xdfd68316d5f5ba85`.
|
||||
- The O-A checks pass: G interleave A = 40/B = 40/48 runs, `HHH` precedence, and a typed
|
||||
`1 2 + .` gives `3` mid-stream.
|
||||
- Mint → WIREBIND → `USE` → `1 2 + .` → `3`. Idle log growth 0. Zero `UNKNOWN WORD` or
|
||||
faults. QEMU exits cleanly.
|
||||
|
||||
Reference in New Issue
Block a user