Skip to content

docs: note what a RAN function id tells a dApp, and mark the shipped ledger rows - #139

Merged
Thecave3 merged 5 commits into
mainfrom
docs/interop-ran-function-ids
Nov 4, 2026
Merged

Thecave3 merged 5 commits into
mainfrom
docs/interop-ran-function-ids

Conversation

@Thecave3

@Thecave3 Thecave3 commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Two documentation changes to docs/interop.md, one commit each. No code, wire or version change, so no ledger row.

RAN function ids (refs #87, closed)

#87 asked how a dApp tells which service model an id names, since a function entry has no name or version. It was closed with a position: id 2 is one service model, the NVIDIA L1 KPM, on both Aerial and OAI. This adds a short "RAN function ids" section:

  • the entry is the id, two id lists and an opaque blob, so the id is the only identifier, and libe3 does not interpret it;
  • ids by stack: OAI 1 (Spectrum) and 2 (L1 KPM), Aerial 2, OCUDU 10 and 11;
  • the agreement on id 2 lives in the two codebases, and libe3 cannot verify it;
  • OCUDU's entry has an optional name and version; libe3's has neither, and sm_name and sm_version are declared but never sent. Carrying them would let a dApp verify the id. Not done.

Ledger statuses

Seven ledger rows still said unmerged after their PRs landed and shipped. Each now says released in the first release that carries it:

Row PR First release
JSON payloads nested, granted values reported #118 0.2.6
dApp lifecycle: CONNECTION_LOST, RELEASED_BY_RAN #120 0.2.7
ASN.1 periodicity to 0..60000000 #122 0.2.8
ASN.1 setup strings, size bounds, fixtures #123 0.2.9
subscriptionTime ends a subscription #129 0.2.10

The four rows that had an empty PR column now link theirs.

A function entry carries an id, two id lists and an opaque blob, with no name
and no version, so the id is the only thing that says which service model a
function is. Id 2 is one service model, the NVIDIA L1 KPM, on both Aerial and
OAI; OCUDU uses ids 10 and 11 and an optional name and version in the entry.
The agreement on id 2 lives in the two codebases, and libe3 cannot verify it.
Record that on the interop page, and that carrying a name and version would
let a dApp check it.

Refs #87

Assisted-by: Claude:claude-sonnet-5-5
Signed-off-by: Thecave3 <thecave003@gmail.com>
Seven rows still read `unmerged` after their PRs landed and were released.
Each now names the first release that carries it: JSON nesting and granted
values in 0.2.6, the dApp lifecycle in 0.2.7, the wider periodicity in 0.2.8,
the setup strings, size bounds and fixtures in 0.2.9, and subscriptionTime in
0.2.10. The four rows with an empty PR column now link their PR.

Assisted-by: Claude:claude-sonnet-5-5
Signed-off-by: Thecave3 <thecave003@gmail.com>
@Thecave3 Thecave3 changed the title docs: say what a RAN function id does and does not tell a dApp docs: note what a RAN function id tells a dApp, and mark the shipped ledger rows Nov 4, 2026
@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

CI report — 53fd479 — ✅ all checks passed

Workflow Result Time Run
Commit policy ✅ success 0m17s #214
Docs ✅ success 1m07s #20
E2E dApp Integration ✅ success 2m46s #202
E2E Topologies (multi-dApp / multi-RAN) ✅ success 4m10s #200
Full-loop Latency Benchmark ✅ success 7m02s #201
Unit Tests ✅ success 10m29s #228
Build libe3py wheels ⏭️ not started (no changed file matches its paths filter) — —
latrec portability ⏭️ not started (no changed file matches its paths filter) — —
MPMC Queue Benchmark ⏭️ not started (no changed file matches its paths filter) — —
Docs

Doxygen warnings

0 warning(s) in the files this pull request changes, which fail the check; 403 more elsewhere, which do not. The full list is in the job log and the doxygen-warnings artifact of the Docs workflow.

Gaps in files this pull request does not change, by file:

Warnings File
53 include/libe3/types.hpp
37 include/libe3/sm_interface.hpp
35 include/libe3/e3_connector.hpp
34 include/libe3/e3_agent.hpp
32 include/libe3/e3_interface.hpp
29 include/libe3/c_api.h
19 src/c_api.cpp
17 include/libe3/logger.hpp
17 swig/libe3_swig.hpp
17 swig/e3_dapp_session.hpp
E2E Topologies (multi-dApp / multi-RAN)

zmq/ipc

  • ✅ 1 RAN - 1 dApp: indications=5
    • dapp peer=t11 ran=ran-solo sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=1 max=1 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
  • ✅ 1 RAN - 2 dApps: dApp#1 ind=5 sub=1, dApp#2 ind=6 sub=2, RAN saw 2 dApps
    • dapp1 peer=t12 ran=ran-shared sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
    • dapp2 peer=t12 ran=ran-shared sub=2 indications=6 seq=[0..5] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:6 2-5:0 6-10:0 >10:0]
  • ✅ 2 RANs - 1 dApp: from ran-a ind=5, from ran-b ind=5
    • dapp peer=t2a ran=ran-a sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=1 max=1 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
    • dapp peer=t2b ran=ran-b sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]

zmq/tcp

  • ✅ 1 RAN - 1 dApp: indications=5
    • dapp peer=default ran=ran-solo sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=1 max=1 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
  • ✅ 1 RAN - 2 dApps: dApp#1 ind=5 sub=1, dApp#2 ind=6 sub=2, RAN saw 2 dApps
    • dapp1 peer=default ran=ran-shared sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=1 max=1 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
    • dapp2 peer=default ran=ran-shared sub=2 indications=6 seq=[0..5] dropped=0 (0%) age_ms(avg=1 max=1 @seq=0) hist[<=1:6 2-5:0 6-10:0 >10:0]
  • ✅ 2 RANs - 1 dApp: from ran-a ind=5, from ran-b ind=5
    • dapp peer=default ran=ran-a sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
    • dapp peer=off100 ran=ran-b sub=1 indications=5 seq=[0..4] dropped=0 (0%) age_ms(avg=0 max=0 @seq=0) hist[<=1:5 2-5:0 6-10:0 >10:0]
E2E dApp Integration

✅ posix/ipc

  • dApp exit: 0
  • Indications received: 7

✅ posix/tcp

  • dApp exit: 0
  • Indications received: 7

✅ zmq/ipc

  • dApp exit: 0
  • Indications received: 7

✅ zmq/tcp

  • dApp exit: 0
  • Indications received: 7
Full-loop Latency Benchmark

Full-loop latency

Full-loop latency benchmark (N=1265 after 50 warmup)

All values in microseconds (μs). Link: zmq, transport: ipc, encoding: ASN.1 APER.

# Description Tags mean p50 p99 max
1 Collect indication data RECORD_BEGIN to ENCODE_E3SM_BEGIN 0.08 0.07 0.20 0.39
2 Create & encode indication ENCODE_E3SM_BEGIN to ENCODE_E3SM_DONE 0.46 0.43 0.66 10.75
3 Encode E3AP (indication) EMIT_ENTER to ENQUEUE, then DEQUEUE to ENCODE_E3AP_DONE 1.48 1.44 1.82 10.70
4 Queuing (indication) ENQUEUE to DEQUEUE 1.10 0.70 10.73 13.77
5 Delivery (indication) ENCODE_E3AP_DONE to SEND_DONE 0.36 0.15 3.69 15.94
6 E3 wire (RAN -> dApp) SEND_DONE to RECV 24.24 23.08 35.55 42.34
7 Decode E3AP (indication) RECV to DECODE_E3AP_DONE 0.78 0.63 3.44 13.06
8 libe3 dispatch (indication) DECODE_E3AP_DONE to DELIVER_BEGIN 0.08 0.07 0.16 0.29
9 Decode indication DELIVER_BEGIN to DECODE_E3SM_DONE 0.22 0.20 0.40 0.85
10 Process data DECODE_E3SM_DONE to ENCODE_E3SM_BEGIN 0.03 0.03 0.03 0.13
11 Create & encode control ENCODE_E3SM_BEGIN to ENCODE_E3SM_DONE 0.13 0.13 0.27 0.62
12 Encode E3AP (control) EMIT_ENTER to ENQUEUE, then DEQUEUE to ENCODE_E3AP_DONE 1.73 1.72 2.08 11.64
13 Queuing (control) ENQUEUE to DEQUEUE 2.06 0.79 9.76 20.13
14 Delivery (control) ENCODE_E3AP_DONE to SEND_DONE 3.35 3.56 4.72 13.41
15 E3 wire (dApp -> RAN) SEND_DONE to RECV 27.87 27.30 38.14 253.19
16 Decode E3AP (control) RECV to DECODE_E3AP_DONE 1.09 1.06 1.76 11.33
17 libe3 dispatch (control) DECODE_E3AP_DONE to DECODE_E3SM_BEGIN 0.12 0.11 0.24 0.42
18 Decode & handle control DECODE_E3SM_BEGIN to DECODE_E3SM_DONE 0.15 0.13 0.34 10.63
Total Total round-trip 65.63 62.04 92.61 289.44

ubuntu-latest, Release build, ZMQ + IPC, ASN.1 APER.

These numbers are measured inside a GitHub Actions container and should be treated as an upper bound on E3AP's and the library's own latency, not a representative deployment measurement.

Ready to merge (fast-forward only)

A maintainer can land the reviewed commits with:

git fetch origin
git checkout main && git merge --ff-only 53fd47995b4606ef3fb3e08fb233f0c2ce691504 && git push origin main

Head: 53fd47995b4606ef3fb3e08fb233f0c2ce691504 (branch docs/interop-ran-function-ids). If --ff-only fails as non-fast-forward, the branch must be rebased on the latest main.

One comment per PR, rewritten in place once every workflow for 53fd479 finished.

The CI report waited for the workflows that had run and always exited 0, so it
could not serve as the required check once the test workflows stop starting on
documentation-only PRs: a workflow that a `paths` filter keeps off has no check
to wait for, and one that should have started but did not looks the same.

Read each workflow's `pull_request` filter and apply GitHub's rules to the PR's
changed files (workflow_triggers.py), so the filters are the one definition of
what counts as code. The report now waits for every workflow the files say
should start, and exits non-zero when a run failed, when an expected workflow
never started, or when CI did not finish in time. A workflow kept off by its
filter is listed as not started and is not an error. A trigger the parser does
not understand counts as unfiltered, so the failure mode is a loud one.

The report no longer waits on its own check, and the NONCODE regex that had
drifted from detect_code_changes.sh is gone.

Assisted-by: Claude:claude-sonnet-5-5
Signed-off-by: Thecave3 <thecave003@gmail.com>
A documentation-only PR started seven workflows and 26 jobs. Twenty of them
spun up a runner, checked out and skipped every real step, only so that each
required check would report something: a workflow kept off by a `paths` filter
never reports, and a job skipped with `if:` reports "skipped", which a required
check does not accept.

With the ruleset requiring only the CI report, which now knows what should have
started (see the previous commit), the test workflows can use a workflow-level
`paths` filter instead. pr-tests, e2e-dapp-test, e2e-topologies and
full-loop-bench start when any changed file is code, as detect_code_changes.sh
defines it, and not otherwise. The last pattern of each filter brings back the
workflow's own file, so a PR that edits a workflow is tested by it.

The five copies of the "Detect code changes" job, and every step-level `if:`
on its output, go away. commit-trailers.yml keeps its own gate for the
per-commit build.

test_workflow_filters.py guards the result and runs in the CI report job: the
four filters stay identical, they agree with detect_code_changes.sh on a spread
of real paths, and a new unfiltered workflow cannot start on documentation PRs
without being added to a list on purpose.

Assisted-by: Claude:claude-sonnet-5-5
Signed-off-by: Thecave3 <thecave003@gmail.com>
CONTRIBUTING.md still said a `Detect code changes` job gates the build and test
workflows. They now start only when a changed file is code, and the CI report is
the one required check: it says when it fails and what a contributor adding a
pull request workflow has to do.

Assisted-by: Claude:claude-sonnet-5-5
Signed-off-by: Thecave3 <thecave003@gmail.com>
@Thecave3
Thecave3 merged commit 53fd479 into main Nov 4, 2026
22 checks passed
@Thecave3
Thecave3 deleted the docs/interop-ran-function-ids branch November 4, 2026 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant