fed-sx-m2: resolve Blockers #1 — fix er-bif-http-listen marshaller bridge
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s

The er-bif-http-listen BIF body in lib/erlang/runtime.sx referenced
er-http-resp-to-sx / er-http-req-of-sx — helpers deleted by 78eae9ef
("fed-sx-m1: 8b-bridge cleanup") because the BIF body never picked
them up. Listener bound but every request handler crashed on first
call to the undefined helpers; curl got 000 / empty body.

Rewrote the sx-handler bridge to thread through the live marshallers
that the cleanup commit's message claimed were already in use:

  Inbound: SX Dict {:method :path :query :headers :body}
    -> er-request-dict-to-proplist
    -> Erlang request proplist matching http_server:route/2 shape
       (binaries for path/method/body, dict-like proplist for headers)

  Outbound: Erlang [{status, N}, {headers, [{Bin, Bin}, ...]}, {body, Bin}]
    -> er-proplist-to-dict
    -> SX Dict matching what native http-listen serialises
       (er-to-sx-deep auto-converts binary values to strings and
       flattens the 2-tuple headers cons to a nested SX dict)

This is technically substrate work in lib/erlang/runtime.sx but
stays within the m2 briefing's allowed exception scope — the http
BIF wrappers (Step 8a / 8e / now 12-prep) are the explicit substrate
carve-outs. Unblocks Step 12's REAL two-instance smoke test rather
than an in-process loopback variant.

Test: next/tests/http_server_tcp.sh 5/5
  - GET / -> 200
  - GET /.well-known/sx-capabilities -> 200 (body contains "kernel:")
  - GET /no-such-path -> 404
  - POST /activity (no bearer) -> 401
  - POST /activity (bad bearer) -> 401

No-regression gates green: Erlang conformance 761/761,
httpc_request 10/10, dispatch_http 10/10, http_listen_bif 5/5,
discovery_fetch 11/11, http_multi_actor 44/44, http_marshal 10/10.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-07 13:51:06 +00:00
parent 9a204e84ab
commit 8d33d02f92
2 changed files with 66 additions and 16 deletions

View File

@@ -1590,7 +1590,33 @@
(not (er-fun? handler))
(raise (er-mk-error-marker (er-mk-atom "badarg")))
:else (let
((sx-handler (fn (req-dict) (er-http-resp-to-sx (er-apply-fun handler (list (er-http-req-of-sx req-dict)))))))
;; Bridge between native http-listen and Erlang handler.
;;
;; Inbound: native passes Req as SX Dict
;; {:method :path :query :headers :body}
;; converted to Erlang request proplist via the live
;; er-request-dict-to-proplist marshaller — that's the
;; same shape http_server:route/2 consumes (binaries
;; for path/method/body, dict-like proplist for headers).
;;
;; Outbound: Erlang handler returns
;; [{status, Int}, {headers, [{Bin, Bin}, ...]}, {body, Bin}]
;; converted back to SX Dict via er-proplist-to-dict —
;; binary values become SX strings, the headers cons
;; flattens to a nested SX dict (via er-to-sx-deep's
;; proplist-2tuple detection). Matches what native
;; http-listen serialises to the wire.
;;
;; (Step 8b-bridge originally shipped parallel
;; er-http-req-of-sx / er-http-resp-to-sx helpers; commit
;; 78eae9ef deleted them as dead because the BIF body
;; still referenced them — Blockers #1. This rewrite
;; threads through the live marshallers instead.)
((sx-handler
(fn (req-dict)
(let ((req-pl (er-request-dict-to-proplist req-dict)))
(let ((resp-pl (er-apply-fun handler (list req-pl))))
(er-proplist-to-dict resp-pl))))))
(http-listen port sx-handler))))))
;; httpc:request/4(Url, Method, Headers, Body) - BRIEFING-EXCEPTION:

View File

@@ -1041,21 +1041,17 @@ Pre-existing regressions inherited from the M1 closeout. Out of m2
scope (substrate, not `next/**`), tracked here so iteration can
proceed.
1. **`next/tests/http_server_tcp.sh` 0/5** — pre-existing regression
introduced by `78eae9ef` (`fed-sx-m1: 8b-bridge cleanup`).
`lib/erlang/runtime.sx:1593` still references `er-http-resp-to-sx`
and `er-http-req-of-sx` in `er-bif-http-listen`'s sx-handler body,
but the cleanup commit removed both helpers without rewriting the
BIF. Listener binds (TCP socket accepts), but every request handler
crashes on first call to the undefined helpers — curl gets 000 /
empty body. Fix needs to rewrite the sx-handler body around the
live `er-request-dict-to-proplist` / `er-proplist-to-dict`
helpers (which the cleanup commit's message claimed are already
in use, but which the BIF body never picked up). Substrate work,
belongs on `loops/erlang`. m2 work continues against the in-process
HTTP layer (`http_marshal.sh` 10/10, `http_publish_fold.sh` 10/10)
until resolved. Confirmed pre-existing by stashing 1a's changes and
re-running on the unmodified m1 closeout HEAD.
1. **`next/tests/http_server_tcp.sh` 0/5** — ~~pre-existing
regression~~ **RESOLVED 2026-06-07** during Step 12 prep. The
`er-bif-http-listen` sx-handler in `lib/erlang/runtime.sx`
referenced the now-deleted `er-http-resp-to-sx` /
`er-http-req-of-sx` helpers; rewrote the bridge to thread
through the live `er-request-dict-to-proplist` (inbound) +
`er-proplist-to-dict` (outbound) marshallers — the same shape
`http_server:route/2` already consumes and emits. 5/5 now
passing. This is the surface Step 12's real two-instance smoke
test (rather than an in-process loopback) uses to spin up each
instance's HTTP listener.
2. **Native `http-request` (HTTP client) primitive missing** —
~~discovered during Step 8e prep~~ **RESOLVED 2026-06-07** by
@@ -1086,6 +1082,34 @@ proceed.
Newest first.
- **2026-06-07** — Blockers #1 RESOLVED. The
`er-bif-http-listen` sx-handler in `lib/erlang/runtime.sx`
referenced `er-http-resp-to-sx` / `er-http-req-of-sx` —
helpers deleted by `78eae9ef` because the BIF body never
picked them up. Rewrote the bridge to thread through the
live marshallers `er-request-dict-to-proplist` (inbound
SX Dict → Erlang request proplist matching what
`http_server:route/2` consumes) and `er-proplist-to-dict`
(outbound Erlang response proplist → SX Dict matching
what the native http-listen primitive serialises to the
wire). The marshallers convert binary header values to
strings + flatten the nested headers proplist via
`er-to-sx-deep`'s 2-tuple detection, so the response
shape matches what http-listen expects without any
additional shape coercion.
`next/tests/http_server_tcp.sh` 5/5 (GET /, capabilities,
unknown → 404, POST /activity no/bad bearer → 401).
Conformance 761/761 + 6 adjacent gates (httpc_request,
dispatch_http, http_listen_bif, discovery_fetch,
http_multi_actor, http_marshal) all green.
This is technically substrate work in lib/erlang/runtime.sx,
but stays within the m2 briefing's allowed exception scope
(the http BIF wrappers — Step 8a / 8e / now 12-prep — are
the explicit substrate carve-outs). Unblocks Step 12's
REAL two-instance smoke test (rather than an in-process
loopback variant).
- **2026-06-07** — Step 10c (closes Step 10): peer-actor doc
fetch + cache write. New `next/kernel/discovery_fetch.erl`
produces a 1-arity FetchFn closure for