Phase 1 of DAEMON_ORIGIN: decoders accept envelope flag bit 6 - #61
Conversation
|
Reviewed by execution, not read-through — full results, one revision needed. The revision: the committed fixture needs a (adjust to your fixture's actual path) in the same commit. Given three instances, I'll also take a repo-wide audit of un-attributed golden directories as a follow-up on my side — your fixture is following house pattern; the house pattern keeps meeting the same trap. What I verified, all green:
Design points I'm explicitly endorsing for the record: not adding the flag to Green matrix on the twin after the gitattributes fix = merge. |
Three incidents of the same class (agent-token vectors, adapter vendored fixtures, PR #61 wire vectors): a byte-comparing test meets Windows checkout EOL normalization and fails on byte-identical-looking content. The two existing per-directory .gitattributes protected only the suites that had already been burned; this covers every fixture directory so the next suite to adopt byte comparison (the correct golden form) does not rediscover the trap on its first Windows run. Includes the subc-transport/tests/golden path ahead of PR #61's fixture landing.
|
Follow-up that simplifies your revision to a rebase: I landed the repo-wide fixture protection on master (18bc8ad) — every golden/fixture directory now carries |
CONSUMER-IMPACT: envelope flag bit 6 is now DAEMON_ORIGIN; all decoders must accept it before a daemon that emits it is deployed. Bit 7 remains reserved and rejected.
618b2d0 to
081bf14
Compare
|
Rebased onto The twin The failure it caught is the expected CRLF one, which is good news for the diagnosis. let generated = serde_json::to_string_pretty(&generated_fixture()).unwrap() + "\n";
assert_eq!(fs::read_to_string(&path).unwrap(), generated);
I have not verified the green side, only the mechanism. Per your standing instruction: if it stays red after the twin moves to Also worth noting for the swift leg — the twin's |
Phase 1 of the two-phase rollout you ratified for #6. Decoder capability only — nothing sets the bit and nothing requires it. Does not close #6.
Why this ships alone
An old deployed decoder hard-fails an unknown reserved bit. So every decoder has to accept bit 6, and be deployed, before any daemon emits it. Phase 1 is therefore the long-lead item and it's independent of the Phase 2 question still open below.
What changed
FLAG_DAEMON_ORIGIN = 0b0100_0000;FLAG_RESERVED_MASKnarrowed0b1100_0000→0b1000_0000;is_daemon_origin()/with_daemon_origin()/without_daemon_origin(), the clear being one mask op so a future splice call site inlines to a single AND. Not added toFlags::new— ordinary emitters keep producing clear frames, and opting in stays explicit.envelope.ts,reserved_flag_bitsretained for bit 7.DAEMON_ORIGIN_FLAG: UInt8 = 0x40,EnvelopeHeader.daemonOrigin, local validator mask0xc0→0x80.SubcFed/Framing/FedFrameCodec.swiftuntouched: its JSONcall_frameheader is a different protocol with no flags byte.Header::encode,compute_proof), compares against the committed fixture by default, rewrites only underUPDATE_GOLDEN=1. Rust, TS, and Swift all consume the same fixture; the new vector carries0x40at header offset 6.subc-protocol0.13.0,@cortexkit/subc-client0.9.0, docs, and theCONSUMER-IMPACT:trailer.Bit 7 stays reserved and rejected — it's the tripwire for the next allocation, and I deliberately did not loosen validation for both bits.
Verification
Four mask mutations, both directions, both languages — because the trap here is loosening both reserved bits at once, which a bit-6-works test cannot see:
0b1100_0000ReservedFlagBits { flags: 64 }✓0b1100_0000reserved flag bits set in flags 0b01000000✓0Flags(128)instead ofErr✓0The generator was also checked for self-confirmation: corrupting byte 6 of the committed fixture reddens the test without
UPDATE_GOLDEN=1, and the only diff was that row — so it's a whole-fixture byte compare, not a field-by-field read of the file it validates.Gates:
subc-protocol32,wire_vectors1, workspace--locked676 (1 pre-existing ignored), clippy-D warningsclean, fmt clean, both fleet scripts clean,bun test138 pass / 18 skip / 0 fail. JSON body goldens show no diff, as expected — they carry body schemas, not envelope bytes, so they cannot prove this flag.Two things you need to know before merging
1. The Swift half is UNVERIFIED. There is no Swift toolchain on this box, so neither I nor the reviewer could run it. The edits were reviewed by reading against the Rust and TS equivalents and look structurally identical, but that is inspection, not a green test. A macOS reviewer needs:
I'd rather flag this than let a passing-looking summary imply coverage that doesn't exist.
2.
subc-protocol0.13.0 collides with #59. Both take it to 0.13.0. Whichever you merge second needs a rebase and re-bump — mechanical, and the version-cascade script enforces it. Tell me which you want first and I'll rebase the other.What gates Phase 2
Phase 2 (splice clears on forward, daemon sets on authored errors, SDK predicates require the bit) must not start until this has landed and every consumer that decodes frames without the SDK has a deployed decoder. Publishing an SDK is not sufficient — an old deployed decoder still hard-fails the first daemon-origin frame.
I re-ran that census with the dependency-based instrument (
subc-transportwithoutsubc-client-rs). Five repos do not inherit an SDK fix: astrocyte, broca, cerebellum, claustrum, insula. Six are SDK-backed: callosum, fusiform, plexus, prefrontal, thalamus, wernicke.Worth one caution on method: the naive symbol grep the plan suggests (
rg 'decode_header|HEADER_LEN'across the fleet) gave me 4 out of 4 false positives — a WAL frame header in broca, a JSON federation cap in callosum, a crypto envelope in claustrum, and HTTP headers in prefrontal. Name collisions onHEADER_LENare common enough that the dependency graph is the only reliable instrument here.Also still open from my earlier comment on #6: whether
control_error_body_frameneeds splitting into authored-vs-relayed helpers, since the control plane re-authors envelopes around module-suppliedErrorBodyvalues and stamping there would let a module forge retry authority through a rejected route bind. That's a Phase 2 blocker, not a Phase 1 one.Need help on this PR? Tag
@codesmithwith what you need. Autofix is disabled.