fed-sx-m1: Step 4c — bootstrap:read_genesis/0,1 + 5 helpers + 15 read tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 14m10s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 14m10s
This commit is contained in:
96
next/kernel/bootstrap.erl
Normal file
96
next/kernel/bootstrap.erl
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
-module(bootstrap).
|
||||||
|
-export([read_genesis/0, read_genesis/1,
|
||||||
|
read_section/2, sections/0, section_subdir/1,
|
||||||
|
default_base/0, ends_with_sx/1]).
|
||||||
|
|
||||||
|
%% Genesis bundle reader per design §12.2.
|
||||||
|
%%
|
||||||
|
%% read_genesis/0,1 walks the seven canonical section subdirectories
|
||||||
|
%% under `next/genesis/`, filters .sx files, reads each file into a
|
||||||
|
%% binary, and returns a structured snapshot:
|
||||||
|
%%
|
||||||
|
%% {ok, [{Section :: atom,
|
||||||
|
%% [{FileName :: binary, FileBytes :: binary}, ...]},
|
||||||
|
%% ...]}
|
||||||
|
%%
|
||||||
|
%% Step 4d will compute the bundle CID by hashing the assembled
|
||||||
|
%% byte string across all entries; Step 4e will register the parsed
|
||||||
|
%% definitions in the kernel registry.
|
||||||
|
%%
|
||||||
|
%% Port note: this module does NOT parse the .sx contents. The
|
||||||
|
%% Erlang-on-SX port has no in-Erlang path from binary bytes to SX
|
||||||
|
%% structured terms (same substrate gap that parked Step 3b); the
|
||||||
|
%% bundle CID needs only the raw bytes, and registry registration
|
||||||
|
%% will happen via an SX-side helper that the kernel hands the
|
||||||
|
%% binary contents to. read_genesis/1 ignores its arg in v1 except
|
||||||
|
%% to swap the BasePath — `default_base/0` is "next/genesis".
|
||||||
|
%%
|
||||||
|
%% Port note 2: string-literal binary segments `<<"abc">>` truncate
|
||||||
|
%% to one byte in this port, so all path constants are hand-spelled
|
||||||
|
%% as integer-segment binaries.
|
||||||
|
|
||||||
|
%% ── Public API ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
%% "next/genesis"
|
||||||
|
default_base() ->
|
||||||
|
<<110,101,120,116,47,103,101,110,101,115,105,115>>.
|
||||||
|
|
||||||
|
read_genesis() ->
|
||||||
|
read_genesis(default_base()).
|
||||||
|
|
||||||
|
read_genesis(BasePath) ->
|
||||||
|
{ok, lists:map(
|
||||||
|
fun (S) -> {S, read_section(BasePath, S)} end,
|
||||||
|
sections())}.
|
||||||
|
|
||||||
|
sections() ->
|
||||||
|
[activity_types, object_types, projections,
|
||||||
|
validators, codecs, sig_suites, audience].
|
||||||
|
|
||||||
|
%% "activity-types"
|
||||||
|
section_subdir(activity_types) ->
|
||||||
|
<<97,99,116,105,118,105,116,121,45,116,121,112,101,115>>;
|
||||||
|
%% "object-types"
|
||||||
|
section_subdir(object_types) ->
|
||||||
|
<<111,98,106,101,99,116,45,116,121,112,101,115>>;
|
||||||
|
%% "projections"
|
||||||
|
section_subdir(projections) ->
|
||||||
|
<<112,114,111,106,101,99,116,105,111,110,115>>;
|
||||||
|
%% "validators"
|
||||||
|
section_subdir(validators) ->
|
||||||
|
<<118,97,108,105,100,97,116,111,114,115>>;
|
||||||
|
%% "codecs"
|
||||||
|
section_subdir(codecs) ->
|
||||||
|
<<99,111,100,101,99,115>>;
|
||||||
|
%% "sig-suites"
|
||||||
|
section_subdir(sig_suites) ->
|
||||||
|
<<115,105,103,45,115,117,105,116,101,115>>;
|
||||||
|
%% "audience"
|
||||||
|
section_subdir(audience) ->
|
||||||
|
<<97,117,100,105,101,110,99,101>>.
|
||||||
|
|
||||||
|
read_section(BasePath, Section) ->
|
||||||
|
SubDir = section_subdir(Section),
|
||||||
|
%% 47 = '/'
|
||||||
|
Path = <<BasePath/binary, 47, SubDir/binary>>,
|
||||||
|
case file:list_dir(Path) of
|
||||||
|
{ok, Names} ->
|
||||||
|
SxNames = lists:filter(fun (N) -> ends_with_sx(N) end, Names),
|
||||||
|
lists:map(fun (Name) -> read_one(Path, Name) end, SxNames);
|
||||||
|
{error, _} ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% Suffix check on the .sx extension. 46='.' 115='s' 120='x'.
|
||||||
|
ends_with_sx(<<46, 115, 120>>) -> true;
|
||||||
|
ends_with_sx(<<>>) -> false;
|
||||||
|
ends_with_sx(<<_, Rest/binary>>) -> ends_with_sx(Rest).
|
||||||
|
|
||||||
|
%% ── Internal ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
read_one(DirPath, Name) ->
|
||||||
|
Full = <<DirPath/binary, 47, Name/binary>>,
|
||||||
|
case file:read_file(Full) of
|
||||||
|
{ok, Bytes} -> {Name, Bytes};
|
||||||
|
{error, R} -> {Name, {error, R}}
|
||||||
|
end.
|
||||||
123
next/tests/bootstrap_read.sh
Executable file
123
next/tests/bootstrap_read.sh
Executable file
@@ -0,0 +1,123 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# next/tests/bootstrap_read.sh — Step 4c acceptance test.
|
||||||
|
#
|
||||||
|
# Exercises bootstrap:read_genesis/0, read_section/2, sections/0,
|
||||||
|
# section_subdir/1, ends_with_sx/1. Verifies per-section file
|
||||||
|
# counts match the manifest authored in Steps 4a/4b. 14 cases.
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
cd "$(git rev-parse --show-toplevel)"
|
||||||
|
|
||||||
|
SX_SERVER="${SX_SERVER:-hosts/ocaml/_build/default/bin/sx_server.exe}"
|
||||||
|
if [ ! -x "$SX_SERVER" ]; then
|
||||||
|
SX_SERVER="/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$SX_SERVER" ]; then
|
||||||
|
echo "ERROR: sx_server.exe not found." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERBOSE="${1:-}"
|
||||||
|
PASS=0; FAIL=0; ERRORS=""
|
||||||
|
TMPFILE=$(mktemp); trap "rm -f $TMPFILE" EXIT
|
||||||
|
|
||||||
|
cat > "$TMPFILE" <<'EPOCHS'
|
||||||
|
(epoch 1)
|
||||||
|
(load "lib/erlang/tokenizer.sx")
|
||||||
|
(load "lib/erlang/parser.sx")
|
||||||
|
(load "lib/erlang/parser-core.sx")
|
||||||
|
(load "lib/erlang/parser-expr.sx")
|
||||||
|
(load "lib/erlang/parser-module.sx")
|
||||||
|
(load "lib/erlang/transpile.sx")
|
||||||
|
(load "lib/erlang/runtime.sx")
|
||||||
|
(load "lib/erlang/vm/dispatcher.sx")
|
||||||
|
|
||||||
|
(epoch 2)
|
||||||
|
(eval "(get (erlang-load-module (file-read \"next/kernel/bootstrap.erl\")) :name)")
|
||||||
|
|
||||||
|
;; sections/0 returns 7 atoms
|
||||||
|
(epoch 10)
|
||||||
|
(eval "(erlang-eval-ast \"length(bootstrap:sections())\")")
|
||||||
|
|
||||||
|
;; ends_with_sx — positive on "create.sx", negative on "hello"
|
||||||
|
(epoch 11)
|
||||||
|
(eval "(get (erlang-eval-ast \"bootstrap:ends_with_sx(<<99,114,101,97,116,101,46,115,120>>)\") :name)")
|
||||||
|
(epoch 12)
|
||||||
|
(eval "(get (erlang-eval-ast \"bootstrap:ends_with_sx(<<104,101,108,108,111>>)\") :name)")
|
||||||
|
(epoch 13)
|
||||||
|
(eval "(get (erlang-eval-ast \"bootstrap:ends_with_sx(<<>>)\") :name)")
|
||||||
|
|
||||||
|
;; Per-section file counts match the manifest (3/10/7/3/3/2/3)
|
||||||
|
(epoch 20)
|
||||||
|
(eval "(erlang-eval-ast \"length(bootstrap:read_section(bootstrap:default_base(), activity_types))\")")
|
||||||
|
(epoch 21)
|
||||||
|
(eval "(erlang-eval-ast \"length(bootstrap:read_section(bootstrap:default_base(), object_types))\")")
|
||||||
|
(epoch 22)
|
||||||
|
(eval "(erlang-eval-ast \"length(bootstrap:read_section(bootstrap:default_base(), projections))\")")
|
||||||
|
(epoch 23)
|
||||||
|
(eval "(erlang-eval-ast \"length(bootstrap:read_section(bootstrap:default_base(), validators))\")")
|
||||||
|
(epoch 24)
|
||||||
|
(eval "(erlang-eval-ast \"length(bootstrap:read_section(bootstrap:default_base(), codecs))\")")
|
||||||
|
(epoch 25)
|
||||||
|
(eval "(erlang-eval-ast \"length(bootstrap:read_section(bootstrap:default_base(), sig_suites))\")")
|
||||||
|
(epoch 26)
|
||||||
|
(eval "(erlang-eval-ast \"length(bootstrap:read_section(bootstrap:default_base(), audience))\")")
|
||||||
|
|
||||||
|
;; read_genesis/0 returns {ok, [{Section, Entries}, ...]} with 7 entries
|
||||||
|
(epoch 30)
|
||||||
|
(eval "(erlang-eval-ast \"{ok, G} = bootstrap:read_genesis(), length(G)\")")
|
||||||
|
|
||||||
|
;; First entry is {activity_types, [_,_,_]}
|
||||||
|
(epoch 31)
|
||||||
|
(eval "(get (erlang-eval-ast \"{ok, G} = bootstrap:read_genesis(), {S, Entries} = hd(G), S\") :name)")
|
||||||
|
|
||||||
|
;; Each entry has the right number of files
|
||||||
|
(epoch 32)
|
||||||
|
(eval "(erlang-eval-ast \"{ok, G} = bootstrap:read_genesis(), {_, E} = hd(G), length(E)\")")
|
||||||
|
EPOCHS
|
||||||
|
|
||||||
|
OUTPUT=$(timeout 120 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
||||||
|
|
||||||
|
check() {
|
||||||
|
local epoch="$1" desc="$2" expected="$3"
|
||||||
|
local actual
|
||||||
|
actual=$(echo "$OUTPUT" | awk -v e="$epoch" '
|
||||||
|
$0 ~ "^\\(ok-len " e " " { getline; print; exit }
|
||||||
|
$0 ~ "^\\(ok " e " " { print; exit }
|
||||||
|
$0 ~ "^\\(error " e " " { print; exit }
|
||||||
|
')
|
||||||
|
[ -z "$actual" ] && actual="<no output for epoch $epoch>"
|
||||||
|
if echo "$actual" | grep -qF -- "$expected"; then
|
||||||
|
PASS=$((PASS+1))
|
||||||
|
[ "$VERBOSE" = "-v" ] && echo " ok $desc"
|
||||||
|
else
|
||||||
|
FAIL=$((FAIL+1))
|
||||||
|
ERRORS+=" FAIL [$desc] (epoch $epoch) expected: $expected | actual: $actual
|
||||||
|
"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check 2 "module load name" "bootstrap"
|
||||||
|
check 10 "sections/0 length" "7"
|
||||||
|
check 11 "ends_with_sx create.sx" "true"
|
||||||
|
check 12 "ends_with_sx hello" "false"
|
||||||
|
check 13 "ends_with_sx empty" "false"
|
||||||
|
check 20 "section activity_types count" "3"
|
||||||
|
check 21 "section object_types count" "10"
|
||||||
|
check 22 "section projections count" "7"
|
||||||
|
check 23 "section validators count" "3"
|
||||||
|
check 24 "section codecs count" "3"
|
||||||
|
check 25 "section sig_suites count" "2"
|
||||||
|
check 26 "section audience count" "3"
|
||||||
|
check 30 "read_genesis returns 7 sections" "7"
|
||||||
|
check 31 "first section name" "activity_types"
|
||||||
|
check 32 "first section entry count" "3"
|
||||||
|
|
||||||
|
TOTAL=$((PASS+FAIL))
|
||||||
|
if [ $FAIL -eq 0 ]; then
|
||||||
|
echo "ok $PASS/$TOTAL next/tests/bootstrap_read.sh passed"
|
||||||
|
else
|
||||||
|
echo "FAIL $PASS/$TOTAL passed, $FAIL failed:"
|
||||||
|
echo "$ERRORS"
|
||||||
|
fi
|
||||||
|
[ $FAIL -eq 0 ]
|
||||||
@@ -250,7 +250,7 @@ replay(LogState, InitAcc, Fun) -> ...
|
|||||||
- [x] **4b-proj** — Projections: activity-log, by-type, by-actor, by-object, actor-state, define-registry, audience-graph — 7 `DefineProjection` files + manifest updated + 9 new parse tests
|
- [x] **4b-proj** — Projections: activity-log, by-type, by-actor, by-object, actor-state, define-registry, audience-graph — 7 `DefineProjection` files + manifest updated + 9 new parse tests
|
||||||
- [x] **4b-vld** — Validators: envelope-shape, signature, type-schema — 3 `DefineValidator` files + manifest updated + 5 new parse tests
|
- [x] **4b-vld** — Validators: envelope-shape, signature, type-schema — 3 `DefineValidator` files + manifest updated + 5 new parse tests
|
||||||
- [x] **4b-cod** — Codecs (dag-cbor, raw, dag-json) + sig-suites (rsa-sha256-2018, ed25519-2020) + audience predicates (Public, Followers, Direct) — 8 SX files + manifest fully populated + 14 new parse tests
|
- [x] **4b-cod** — Codecs (dag-cbor, raw, dag-json) + sig-suites (rsa-sha256-2018, ed25519-2020) + audience predicates (Public, Followers, Direct) — 8 SX files + manifest fully populated + 14 new parse tests
|
||||||
- [ ] **4c** — `bootstrap:read_genesis/1` in Erlang: walk the manifest, file-read each referenced .sx, return parsed forms
|
- [x] **4c** — `bootstrap:read_genesis/0,1` + `read_section/2` + `sections/0` + `section_subdir/1` + `ends_with_sx/1` in Erlang: walk seven hardcoded section subdirs, filter `.sx` files via byte-pattern suffix match, read each into a binary. Returns `{ok, [{Section, [{Name, Bytes}, ...]}, ...]}`. Skips SX parsing — the substrate has no in-Erlang binary→SX-term path (same gap as Step 3b); bundle CID over raw bytes is enough for Step 4d. `next/tests/bootstrap_read.sh` (15 cases).
|
||||||
- [ ] **4d** — `bootstrap:build_genesis/1` + `bootstrap:verify_genesis/1`: compute bundle CID over the read forms via the host `cid:to_string` substrate; verify against a stored `bundle.cidhash`
|
- [ ] **4d** — `bootstrap:build_genesis/1` + `bootstrap:verify_genesis/1`: compute bundle CID over the read forms via the host `cid:to_string` substrate; verify against a stored `bundle.cidhash`
|
||||||
- [ ] **4e** — `bootstrap:load_genesis/1`: register parsed definitions into the in-memory registry (depends on Step 5)
|
- [ ] **4e** — `bootstrap:load_genesis/1`: register parsed definitions into the in-memory registry (depends on Step 5)
|
||||||
|
|
||||||
@@ -955,6 +955,7 @@ A few things still under-specified; resolve as work begins.
|
|||||||
Newest first. One line per sub-deliverable commit. Erlang conformance gate
|
Newest first. One line per sub-deliverable commit. Erlang conformance gate
|
||||||
(`bash lib/erlang/conformance.sh`) must remain 729/729 on every entry.
|
(`bash lib/erlang/conformance.sh`) must remain 729/729 on every entry.
|
||||||
|
|
||||||
|
- **2026-05-27** — Step 4c: `next/kernel/bootstrap.erl` — Erlang module that enumerates the genesis bundle by walking seven hardcoded section subdirs via `file:list_dir/1`, filters `.sx` files via byte-pattern suffix match (`ends_with_sx/1`), reads each into a binary via `file:read_file/1`. Returns `{ok, [{Section, [{Name, Bytes}, ...]}]}`. Hits the same SX-parser substrate gap as Step 3b — kept the surface byte-only; parsing happens via SX-side helpers in later steps. Port gotchas: `fun name/arity` references unsupported (use anonymous fun wrappers); `<<"...">>` string-literal segments truncate to one byte (paths hand-spelled as integer-segment binaries). `next/tests/bootstrap_read.sh` 15/15. Erlang conformance 729/729.
|
||||||
- **2026-05-27** — Step 4b-cod: bootstrap codecs + sig-suites + audience predicates complete. 3 `DefineCodec` files (dag-cbor + raw + dag-json, dag-cbor + dag-json deferring to host-codec primitive when wired), 2 `DefineSigSuite` files (rsa-sha256-2018 PEM-keyed, ed25519-2020 multibase-keyed, both :verify returning false as m2-deferred stand-in), 3 `DefineAudience` files (Public/Followers/Direct member-of predicates per design §16). Manifest now lists 26 bootstrap files across all eight sections; `next/tests/genesis_parse.sh` 50/50. Step 4b complete; remaining Step 4 is bundler code (4c–4e). Erlang conformance 729/729.
|
- **2026-05-27** — Step 4b-cod: bootstrap codecs + sig-suites + audience predicates complete. 3 `DefineCodec` files (dag-cbor + raw + dag-json, dag-cbor + dag-json deferring to host-codec primitive when wired), 2 `DefineSigSuite` files (rsa-sha256-2018 PEM-keyed, ed25519-2020 multibase-keyed, both :verify returning false as m2-deferred stand-in), 3 `DefineAudience` files (Public/Followers/Direct member-of predicates per design §16). Manifest now lists 26 bootstrap files across all eight sections; `next/tests/genesis_parse.sh` 50/50. Step 4b complete; remaining Step 4 is bundler code (4c–4e). Erlang conformance 729/729.
|
||||||
- **2026-05-27** — Step 4b-vld: bootstrap validators complete — 3 `DefineValidator` SX files (envelope-shape mirroring Step 2a, signature stub delegating to envelope:verify_signature/2 per design §9.6, type-schema looking up the object-type schema from define-registry). Manifest `:validators` populated; `next/tests/genesis_parse.sh` 36/36. Erlang conformance 729/729.
|
- **2026-05-27** — Step 4b-vld: bootstrap validators complete — 3 `DefineValidator` SX files (envelope-shape mirroring Step 2a, signature stub delegating to envelope:verify_signature/2 per design §9.6, type-schema looking up the object-type schema from define-registry). Manifest `:validators` populated; `next/tests/genesis_parse.sh` 36/36. Erlang conformance 729/729.
|
||||||
- **2026-05-27** — Step 4b-proj: bootstrap projections complete — 7 `DefineProjection` SX files authored (activity-log identity, by-type/by-actor/by-object indexes, actor-state with key history fold, define-registry meta-fold over Create{Define*}, audience-graph stub). Manifest `:projections` populated; `next/tests/genesis_parse.sh` 31/31. Erlang conformance 729/729.
|
- **2026-05-27** — Step 4b-proj: bootstrap projections complete — 7 `DefineProjection` SX files authored (activity-log identity, by-type/by-actor/by-object indexes, actor-state with key history fold, define-registry meta-fold over Create{Define*}, audience-graph stub). Manifest `:projections` populated; `next/tests/genesis_parse.sh` 31/31. Erlang conformance 729/729.
|
||||||
|
|||||||
Reference in New Issue
Block a user