Payload bound and chunking -- FABRIC-3.6.md task 3.5
sk_hermes_publish() now enforces SK_HERMES_CHUNK_MAX_PAYLOAD (1024) on every message's payload_len uniformly, chunked or not -- closing the gap task 3.3 explicitly parked. A chunk carrier is [SkHermesChunkHeader][content slice], slice capped at 1024 - sizeof(header) rather than 1024 itself, so every message on the wire satisfies the same one-block bound vm_interpret()'s own drain limit already requires -- a future chunk-aware drain never has to special-case a carrier that can't be handed to vm_interpret() as-is. Deliberately no chunking-sender API: building one would need kernel-Hermes to own chunk-buffer memory with a real lifetime it has no way to track (kept alive until every subscriber drains it). Sending is a loop pattern a caller writes with sk_hermes_chunk_count() + sk_hermes_publish(), demonstrated by this task's own self-test. sk_hermes_reassemble() is pure and memory-agnostic: validates msg_id agreement, exact seq coverage, and per-chunk slice sizes before a single memcpy, with the total length computed once and checked against the caller's buffer once -- never order-dependent on which chunk happens to overflow. Verified live on all three architectures: a 1024-byte payload as one message, a 1025-byte send refused outright with the ledger untouched, and a 3000-byte payload split into 3 chunks, drained, and reassembled byte-exact against the original. dict_hash unmoved and identical across architectures. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
2b1ba031a5
commit
8a2ee0fdad
+33
-1
@@ -902,10 +902,42 @@ ruling]** cannot be written precisely until Captain Bob settles the named sub-it
|
||||
real disk-backed block storage, which a throwaway diagnostic has no business perturbing.
|
||||
`dict_hash` for Hermes/Hestia unmoved and identical across all three architectures (this
|
||||
task touches no capsule).
|
||||
- [ ] **3.5** — **Payload bound and chunking** (B4) **[needs ruling 3.0d]**. Max 1024 bytes per
|
||||
- [x] **3.5** — **Payload bound and chunking** (B4) **[needs ruling 3.0d]**. Max 1024 bytes per
|
||||
message; larger payloads sent as ordered chunks, reassembled before drain. *Check:*
|
||||
1024-byte payload single message; 3000-byte payload chunked and reassembled byte-exact;
|
||||
1025-byte single-message send refused.
|
||||
2026-09-22 · `logs/20260922-063122/amd64/`, `logs/20260922-063629/aarch64/`,
|
||||
`logs/20260922-064147/riscv64/` — all three reach `[zuse@Hera] ok>`, zero `UNKNOWN WORD`,
|
||||
`mkcapsule --lint capsules/` clean (38 files, 0 violations, unchanged -- no capsule
|
||||
touched). Sizing decision (checked with `advisor()`, not itself a separate ruling -- see
|
||||
the header's own note): `SK_HERMES_CHUNK_MAX_PAYLOAD` (1024) bounds every message's
|
||||
`payload_len` **uniformly**, chunked or not -- a chunk carrier is
|
||||
`[SkHermesChunkHeader][content slice]` where the slice is capped at
|
||||
`1024 - sizeof(header)`, not 1024 itself. Rejected the literal alternative (slice up to
|
||||
1024, carrier up to `1024+sizeof(header)`): under that reading a chunk carrier could never
|
||||
be handed to `vm_interpret()` as-is, making a future chunk-aware drain a special case
|
||||
instead of the same one-block invariant every other message already satisfies.
|
||||
`sk_hermes_publish()` (task 3.3) now enforces this bound directly, closing the gap that
|
||||
task's own doc comment explicitly parked ("3.3 does not enforce a payload size limit").
|
||||
**Deliberately no chunking-SENDER API** (`sk_hermes_publish_chunked()` or similar) --
|
||||
`advisor()` flagged that building one raises a real memory-lifetime question kernel-Hermes
|
||||
has never answered (chunk buffers would need to stay alive until every subscriber drains
|
||||
them, and nothing here can know when that is); sending is a loop pattern a caller writes
|
||||
with `sk_hermes_chunk_count()` + `sk_hermes_publish()`, demonstrated by this task's own
|
||||
self-test rather than hidden behind a new allocator. `sk_hermes_reassemble()` is pure and
|
||||
memory-agnostic (caller-owned input chunks, caller-owned output buffer) -- validates
|
||||
`msg_id` agreement, exact `seq` coverage `{0..n_chunks-1}` with `is_last` on exactly the
|
||||
last, and every non-final slice at exactly `SK_HERMES_CHUNK_MAX_SLICE` bytes, before a
|
||||
single `memcpy`; the reassembled total length is computed once from validated seq
|
||||
completeness and checked against `out_buf_cap` once, so a too-small output buffer is
|
||||
refused cleanly with zero partial copy rather than order-dependently on whichever chunk
|
||||
happens to overflow. Self-test (kernel_main.c) proves exactly the task's three checks: a
|
||||
1024-byte payload as one message; a 1025-byte single-message send refused outright, no
|
||||
allocation, ledger untouched; a 3000-byte payload split into 3 chunks, drained off a
|
||||
synthetic subscriber's own pending queue (task 3.3 primitives), reassembled, and confirmed
|
||||
byte-exact against the original. Ledger audit and `stadium_conserved()` hold before and
|
||||
after. `dict_hash` for Hermes/Hestia unmoved and identical across all three architectures
|
||||
(this task touches no capsule).
|
||||
- [ ] **3.6** — **ACK/NACK and private-channel negotiation** (B1) **[needs ruling 3.0a]**.
|
||||
`request` → `grant`/`deny` on the common channel; a grant creates a private topic;
|
||||
close tears it down. ACK/NACK are message types on the existing allocator.
|
||||
|
||||
Reference in New Issue
Block a user