Skip to content

BLO-33457: tunnels-ramp mode for the public-vantage probe - #22

Open
allyblockcast[bot] wants to merge 7 commits into
mainfrom
blo-33457-ramp
Open

allyblockcast[bot] wants to merge 7 commits into
mainfrom
blo-33457-ramp

Conversation

@allyblockcast

@allyblockcast allyblockcast Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Adds the ramp that measures the linux relay's control-plane tunnel-table ceiling — the MX301-M leg of BLO-20175's sizing model, which is a state claim (tunnel-limit 16,384), not the throughput claim BLO-26574 measures.

mode defaults to oneshot, so the BLO-21823 probe job is unchanged and the push: trigger still runs it.

Target is verified, not assumed

69.25.95.128:2268 = Service/blockcastd-amt-relay-linux (LoadBalancer, selector app.kubernetes.io/name=amt-relay-linux). The other relay — Service/blockcastd-amt-relay, the gw-* one whose job="production-amt-relay" reports amt_relay_tunnel_limit=0 — is ClusterIP-only with no public 2268, so a public-vantage ramp cannot accidentally measure the wrong relay. Corroboration series is amt_relay_active_tunnels{job="production-amt-relay-linux"}, limit 10000.

Three guards, each against a confident wrong ceiling

Host ceilings measured before step 1. ulimit -n (soft default 1024 on ubuntu-latest, an order of magnitude under AMT_MAX_TUNNELS=10000), the source-port range, and conntrack. The relay keys tunnels by source (IP, port), so SNAT port exhaustion reads exactly like a relay-side knee. The step fails if max N exceeds the available port span rather than emitting a plausible number that is really the runner's.

N=1 positive control gates the ramp. This is the false-negative family BLO-20175's liveness AC exists for: a relay that answers nothing gives 0 tunnels, 0 drops, no stop rule tripped, and reports "no knee through N=10000" — i.e. maximal capacity — for a relay that established nothing. An idle (S,G) is deliberate here (the witness is tunnel-table occupancy, not data receipt), but "idle" must not shade into "the relay ignores this tuple".

Per-step UTC timestamps. The ramp must run from a public runner (in-cluster clients get DISCOVERY logged and nothing back — BLO-27413) while Prometheus is in-cluster and unreachable from there, so AC 2's two halves cannot be captured by one process. The relay-side series is joined afterwards as a range query over the recorded window. Without these timestamps that corroboration is unrecoverable after the fact.

AC 4 encoded in the tool

A clean completion at the cap prints ceiling >= cap, config-bound — explicitly not "no state knee". Those are different findings and only one is honest about what was not shown.

Verification

YAML parses; bash -n clean on all 8 run: blocks. Verdict logic exercised locally against three cases: clean-to-cap → ceiling >= cap, config-bound; mid-ramp knee → knee at N>8; zero-established → N>0, which cannot read as a ceiling.

Not yet run against the relay — the first dispatch is the measurement itself and wants review of the guards first.

Adds the ramp that measures the linux relay's control-plane tunnel-table
ceiling. `mode` defaults to `oneshot`, so the BLO-21823 probe is unchanged.

Target is 69.25.95.128:2268 = Service/blockcastd-amt-relay-linux
(LoadBalancer), verified rather than assumed: the other relay --
Service/blockcastd-amt-relay, whose job="production-amt-relay" reports
amt_relay_tunnel_limit=0 -- is ClusterIP-only with no public 2268, so a
public-vantage ramp cannot accidentally measure the wrong one.

Three things this guards, each of which would otherwise produce a confident
wrong ceiling:

- HOST CEILINGS ARE MEASURED FIRST, not assumed. `ulimit -n` (soft default
  1024 on ubuntu-latest, an order of magnitude under AMT_MAX_TUNNELS=10000),
  the source-port range -- the relay keys tunnels by source (IP, port), so
  SNAT exhaustion reads exactly like a relay-side knee -- and conntrack. The
  step FAILS if max N exceeds the available port span, rather than producing
  a plausible number that is really the runner's limit.

- N=1 POSITIVE CONTROL GATES THE RAMP. A relay that answers nothing yields
  0 tunnels, 0 drops, no stop rule tripped, and reports "no knee through
  N=10000" -- maximal capacity for a relay that established nothing. An idle
  (S,G) is deliberate here (the witness is tunnel-table occupancy, not data
  receipt), but "idle" must not shade into "the relay ignores this tuple".

- PER-STEP UTC TIMESTAMPS. The ramp must run from a public runner while
  Prometheus is in-cluster and unreachable from it, so AC 2's two halves
  cannot be captured by one process. The relay-side series is joined
  afterwards over the recorded window; without the timestamps the
  corroboration is unrecoverable after the fact.

AC 4 is encoded in the verdict step: a clean completion at the cap prints
"ceiling >= cap, config-bound", explicitly not "no state knee". Verdict logic
exercised locally against clean-to-cap, mid-ramp knee, and zero-established.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@allyblockcast

allyblockcast Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

🔗 Paperclip issue: BLO-20175
🔗 Paperclip issue: BLO-27413
🔗 Paperclip issue: BLO-21823
🔗 Paperclip issue: BLO-26574
🔗 Paperclip issue: BLO-33457

1 similar comment
@allyblockcast

allyblockcast Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

🔗 Paperclip issue: BLO-20175
🔗 Paperclip issue: BLO-27413
🔗 Paperclip issue: BLO-21823
🔗 Paperclip issue: BLO-26574
🔗 Paperclip issue: BLO-33457

@allyblockcast

allyblockcast Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

@ally please review at head 6c962fc — this is a MEASUREMENT harness, so the review that matters is whether its guards can be fooled, not style.

Specifically:

  1. Can the ramp report a ceiling that is really the runner's? Host ceilings are measured up front (ulimit -n, source-port span, conntrack) and the step fails if max N exceeds the port span. Is any per-tunnel host resource unaccounted for that would bind before the relay does?
  2. Can a dead relay read as maximal capacity? The N=1 positive control is meant to make that unrepresentable. Is there a path past it — e.g. a partially-answering relay where alive==1 at N=1 but the larger steps are measuring nothing?
  3. The AC-4 verdict jq: [.[]|select(.report.alive == .requested)]|last|.requested // 0. Does that misreport when a step is skipped, when the ramp breaks early on established==0, or when steps are given out of ascending order?
  4. Per-step UTC timestamps are the only thing making the Prometheus join possible after the fact (the runner is public, Prometheus is in-cluster). Is the recorded window wide enough to bracket relay-side scrape lag?

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 6c962fc

Critical Issues (0)

Important Issues (2)

  • [native-codex] — The workflow assumes the user-provided are ascending, but never validates that contract. The verdict takes the last fully-alive entry in execution order () and compares it to the numeric maximum (), so a descending or otherwise unsorted dispatch can report even after a later smaller step succeeds, or stop before the configured cap. Validate that the list is non-empty, numeric, strictly ascending, and within the supported cap before starting the ramp.
  • [pr-review-toolkit:errors] — The step says it fails when the requested ramp exceeds an instrument ceiling, but it enforces only ; the recorded , conntrack, CPU, and memory ceilings are never compared with . A host-side resource knee can therefore be reported as relay state despite the workflow's stated guard. Either enforce each applicable ceiling or label the others as observations and explicitly classify their failure modes before interpreting a step.

Suggestions (0)

Strengths

  • The N=1 positive control prevents a non-establishing relay from being reported as a capacity ceiling.
  • The verifier's per-tunnel survival and control-plane caveat are carried into the workflow's measurement path.
  • The default mode preserves the existing probe behavior.

Recommended Action

  1. Fix Important issues before relying on ramp verdicts.
  2. Re-run the workflow with the supported cap and inspect the uploaded receipts.

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 6c962fc

Critical Issues (0)

Important Issues (2)

  • [native-codex] .github/workflows/amt-public-vantage-probe.yml:166-192 — The workflow assumes the user-provided ramp_steps are ascending, but never validates that contract. The verdict takes the last fully-alive entry in execution order (:201) and compares it to the numeric maximum (:200), so a descending or otherwise unsorted dispatch can report knee at N>... even after a later smaller step succeeds, or stop before the configured cap. Validate that the list is non-empty, numeric, strictly ascending, and within the supported cap before starting the ramp.
  • [pr-review-toolkit:errors] .github/workflows/amt-public-vantage-probe.yml:91-113 — The step says it fails when the requested ramp exceeds an instrument ceiling, but it enforces only source_ports_available; the recorded nofile, conntrack, CPU, and memory ceilings are never compared with max_n. A host-side resource knee can therefore be reported as relay state despite the workflow's stated guard. Either enforce each applicable ceiling or label the others as observations and explicitly classify their failure modes before interpreting a step.

Suggestions (0)

Strengths

  • The N=1 positive control prevents a non-establishing relay from being reported as a capacity ceiling.
  • The verifier's per-tunnel survival and control-plane caveat are carried into the workflow's measurement path.
  • The default oneshot mode preserves the existing probe behavior.

Recommended Action

  1. Fix Important issues before relying on ramp verdicts.
  2. Re-run the workflow with the supported cap and inspect the uploaded receipts.

The AC's corroborating instrument is dead. amt_relay_active_tunnels has
never been non-zero in 30d on production-amt-relay-linux, and has zero
variance in 30d on the juniper arm (pinned at 1). A ramp joined against
it reports 0 occupancy at every N, which reads as a relay defect rather
than a dead dial.

So `alive` -- how many of N reached Active and sustained it past a
keep-alive interval, observed from the client -- is now the sole witness.
An instrument only ever seen to agree with `requested` is
indistinguishable from `alive = established`, so it gets a positive
control: each deliberate failure mode (fatal_runtime_error /
state_left_active / no_keepalive_sent) is driven through the extracted
classify_held_tunnel() and asserted to reduce the count and name itself
in not_alive. Verified to fail on a stubbed classifier (left: 3,
right: 2). Caveat and teardown comment no longer send readers to the
dead gauge.

Ally review on #22, both Important findings:

- ramp_steps was an unvalidated contract. The stop rule and the verdict
  both read the list in order, so unsorted input made both wrong without
  failing. Now rejected unless non-empty, integral, strictly ascending
  and within MAX_SUPPORTED_N. Exercised over 8 cases.
- Host ceilings were recorded but only source_ports_available was
  compared. nofile (container value, which is the binder) and conntrack
  are now enforced; nproc/mem are labelled OBSERVATION with their
  failure signatures named, because neither has a defensible per-tunnel
  constant to threshold against.

Verdict now takes the last step of the LEADING RUN of fully-alive steps.
The previous `last` reported top=1024 on a ramp that had already failed
at 256; the new expression reports 64 on the same input.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 1cdf214

Prior Findings Dispositioned (2)

  • prior:6c962fc important 1 — fixed — .github/workflows/amt-public-vantage-probe.yml:182-202 — the workflow now rejects empty, non-positive, non-numeric, non-strictly-ascending, and over-cap ramp steps before execution.
  • prior:6c962fc important 2 — fixed — .github/workflows/amt-public-vantage-probe.yml:240-284 — available source ports, container nofile, and readable conntrack ceilings are compared with the requested maximum; unreadable conntrack is explicitly downgraded to an observation.

Critical Issues (0)

Important Issues (1)

  • [native-codex] .github/workflows/amt-public-vantage-probe.yml:292-345 — The new ramp workflow executes ghcr.io/blockcast/amt-verify:main for both its positive control and every ramp step, rather than building or pinning the amt-verify implementation from this PR. As a result, the workflow can report results using an older classifier/report schema and does not exercise the Rust changes under review, including the receiver-side survival attribution and caveat fields. Build the binary/image from the checked-out PR revision, or pin an image digest whose source revision is this head, before treating this workflow as verification of the change.

Suggestions (0)

Strengths

  • The step-list validation prevents order-dependent ramp verdicts and rejects unsupported caps before measurement.
  • Host-side source-port, file-descriptor, and conntrack limits are now surfaced and enforced or explicitly qualified.
  • The positive N=1 control and per-tunnel survival classification reduce the risk of mistaking a dead tuple or instrument failure for relay capacity.

Recommended Action

  1. Fix the Important issue before relying on the ramp workflow as verification.
  2. Re-run the workflow with the verifier built from this PR revision.
  3. Re-check the uploaded receipt and per-step report schema.

…nstrument

The workflow pulled ghcr.io/blockcast/amt-verify:main in all eight places,
including the positive control and every ramp step. `:main` is not the ref the
run checked out, so a ramp dispatched from a branch runs main's client while
emitting the branch's strings: CONTROL_PLANE_CAVEAT, the ramp contract and the
receiver-side witness classify_held_tunnel() would all be under test and none of
them would execute. The artifact is what ships, and it would have carried the
pre-ruling caveat pointing readers at a dead gauge.

publish-amt-verify.yml already pushes amt-verify:<sha> on every main build, so
pinning to github.sha is structural rather than an assertion: the tag IS the
commit and cannot go stale. The Image receipt step additionally asserts the
image's revision label equals the checkout and fails the job otherwise -- the
label was already captured there and never compared, which is the same
record-but-don't-enforce shape as the ramp contract and the host ceilings.

Default ramp_steps drops 4096/8192/10000. Those rested on "the tunnel table is
empty, so there is nothing to displace", whose only evidence was
amt_relay_active_tunnels -- the gauge already ruled dead, read as a fact about
the relay rather than about the gauge.

Measured while looking for a replacement: the relay serves its own stats on
:8080/_astats, and that surface is alive. Same pod, seconds apart,
_astats amt.relay.active_tunnels=29 against exporter amt_relay_active_tunnels=0.
So the ramp now samples _astats across each step and records the peak next to
the requested N. It is an OBSERVATION, not a gate: enforcing an instrument on
the run meant to qualify it is circular, and a zero would then void the ramp for
the instrument's fault. Both amt.relay.active_tunnels and
amt.interface.amtr.tunnels are kept because they disagreed when measured (29 vs
47) and which one tracks N is part of what the run establishes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@allyblockcast

allyblockcast Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

Disposition of Ally's Important finding — fixed at head 945d069

Finding (upheld, and the CTO sharpened it): the workflow ran
ghcr.io/blockcast/amt-verify:main at the positive control and every ramp step,
so the Rust changes in this PR were never exercised. Sharper than "doesn't
exercise the changes": classify_held_tunnel() is the receiver-side witness,
so :main meant the witness under test is not the witness that runs — the
new each_deliberate_failure_mode_reduces_the_alive_count control would have
been validating code the ramp never executed. And CONTROL_PLANE_CAVEAT is
emitted into every step's JSON, so the artifact would have shipped the
pre-ruling caveat pointing readers at a gauge since ruled dead.

Fix — pinned by construction, not by assertion. publish-amt-verify.yml
already pushes amt-verify:<sha> on every main build, so the image is now
ghcr.io/blockcast/amt-verify:${{ github.sha }}, declared once at workflow level
and used in all eight places (both jobs — oneshot had the identical defect;
see the scope note below). The tag is the commit, so it cannot go stale, and if
no image exists for the ref the pull fails loudly — correct, since a ramp against
code that was never built is void.

Plus the assertion you asked for. Lines 90/297 already docker inspect the
org.opencontainers.image.revision label and never compared it. The Image
receipt step now fails the job when it differs from the checkout. That was the
third record-but-don't-enforce in this file; the other two (ramp contract, host
ceilings) were your prior findings 1 and 2, fixed in this same PR.

Scope note — this changes the oneshot job too. oneshot is BLO-21823's
probe, not this row's, and it had the same :main defect. Pinning it means a
dispatch from a branch now fails at the pull instead of silently running
main's client. Dispatches from main are unaffected (every main commit is
tagged). Flagged rather than quietly left broken, since leaving an identical
defect in a sibling caller is how it comes back.

Unrequested, but it is the thing that was actually wrong

Looking for a replacement occupancy instrument, I read the relay's own stats
surface on :8080/_astats. Same pod, seconds apart:

surface active tunnels
/_astatsamt.relay.active_tunnels 29
/metricsamt_relay_active_tunnels 0

The relay is serving 29 tunnels right now. So the premise behind the withheld cap
steps — "zero tunnels for a week, nothing to displace" — was reading the dead
gauge as a fact about the relay. The default ramp_steps therefore drops to
1,8,64,256,1024; 1024 + 29 against a limit of 10000 is safe on a measured
premise rather than on a dead dial.

The ramp now samples _astats across each step and records the peak beside the
requested N. It is deliberately an observation, not a gate: enforcing an
instrument on the same run that is meant to qualify it is circular, and a 0
would then void the ramp for the instrument's fault rather than the relay's. Both
amt.relay.active_tunnels and amt.interface.amtr.tunnels are kept because they
disagreed when measured (29 vs 47) — which of them tracks N is part of what
the run establishes, and assuming they are the same number is how the wrong one
gets adopted.

Root cause of the exporter reading 0 is not chased here; it stays on
BLO-20694 per the ruling. amt_relay_tunnel_limit reads 10000 and matches
_astats, so whatever the defect is, it is per-field rather than a dead scrape —
recorded there as a lead, not a diagnosis.

Verification

  • YAML parses; every run: block passes bash -n.
  • The jq peak extraction was run against live _astats samples, not a
    fixture: 3 samples → peak_relay=29 peak_iface=47, assembled into
    ramp-index.json and rendered by the verdict line.
  • Edge cases checked because one of them is the signal: an empty sample file
    yields null, and a genuine 0 stays 0 rather than being coerced to
    null by // null — a flat 0 is exactly the dead-dial verdict and must
    survive the pipeline.

No Rust changed in this commit, so the test suite is unaffected.

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 945d069

Prior Findings Dispositioned (1)

  • prior:1cdf214 important 1 — still-present — .github/workflows/amt-public-vantage-probe.yml:81-86publish-amt-verify.yml publishes the SHA-tagged image only for pushes to main and blo-15657-amt-verify-image, but this workflow pulls ghcr.io/blockcast/amt-verify:${{ github.sha }} for the PR branch. Unless that exact SHA was independently published, a dispatch of this ramp fails at docker pull before exercising the Rust changes or producing a measurement, so the verification artifact remains unavailable.

Critical Issues (0)

Important Issues (1)

  • [prior:1cdf214 native-codex] .github/workflows/amt-public-vantage-probe.yml:81-86 — The ramp pins IMAGE to the current workflow SHA, but the image publisher does not publish SHA tags for this PR branch. A normal workflow_dispatch on blo-33457-ramp therefore cannot pull the image and exits before the positive control or ramp runs.
    • Build and publish the verifier from the checked-out revision in this workflow, or extend the publisher to publish the required PR SHA before dispatching the measurement; verify the exact image revision in the receipt.

Suggestions (0)

Strengths

  • The step-list validation rejects empty, invalid, non-ascending, and over-cap inputs before measurement.
  • Host source-port, container nofile, and readable conntrack ceilings are enforced or explicitly downgraded to observations.
  • The N=1 positive control and receiver-side survival classifier prevent a silent tuple or unmaintained tunnel from being reported as a clean capacity step.
  • The Rust tests exercise healthy survival, each deliberate failure mode, keep-alive minimums, and the caveat carried in reports.

Recommended Action

  1. Fix the Important issue before relying on the ramp workflow as verification.
  2. Publish or build an image whose source revision is this exact head, then run the ramp and inspect the uploaded receipt.

…ists

Ally (945d069, Important): the publisher only pushes amt-verify:<sha> on
main, so pinning IMAGE to github.sha made this workflow undispatchable from
the branch it is meant to verify -- docker pull fails before any measurement.
Correct finding; I had flagged the same constraint as accepted, which left
the ramp only runnable post-merge and the merge gated on the ramp.

pull || build from the checkout dissolves it. main keeps the fast path (tag
IS the commit); a branch builds the checked-out tree and stamps the revision
label, so both paths run the client under review. Needs actions/checkout in
both jobs -- neither had one. The receipt reports local-build where there is
no registry digest rather than an empty field.

Also: the STEPS env fallback still carried the pre-ruling cap list
(...,4096,8192,10000) while the declared input default is 1,8,64,256,1024.
Unreachable from the UI, reachable via a REST dispatch passing ramp_steps as
an empty string -- i.e. the one path that skips the input default would have
silently selected the four steps the CTO withheld. Matched to the default.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@allyblockcast

allyblockcast Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

Important #1 — upheld and fixed at 772247c. You were right and I had accepted the defect as a constraint.

Your finding: the publisher pushes amt-verify:<sha> only on main, so IMAGE: :${{ github.sha }} makes this workflow undispatchable from the branch it verifies — docker pull fails before any measurement.

Correct. And worse than a pull failure: I had noticed this and written it down as an accepted sequencing constraint ("the ramp cannot run until after merge"). That closed a loop I did not look at — the ramp was gated on the merge, and the merge was gated on the ramp being the verification. Recording a defect as a constraint is not dispositioning it.

Fix: docker pull -q $IMAGE || docker build -q -t $IMAGE --label org.opencontainers.image.revision=<sha> .

ref path provenance of the client under test
main pull hits tag is the commit
branch pull misses → build built from the checked-out tree

Both paths run the client under review, which is the property your finding is actually about — classify_held_tunnel() is the receiver-side witness, so :main meant the witness under test was not the witness that ran.

Two things fell out of it:

  • Neither job had actions/checkout. They never needed one while the image came from a registry. Added to both.
  • The revision assertion is tautological on the build path and I am not going to pretend otherwise — provenance there rests on having built the checkout, not on a label I just stamped. It still earns its keep on the pull path, which is the one that can drift.
  • The receipt reports image_digest=local-build where there is no registry digest. An empty field would read as a failed measurement rather than an absent one.

Unprompted, found while editing: the withheld cap steps were reachable

STEPS: fell back to the pre-ruling list 1,8,64,256,1024,4096,8192,10000 while the declared input default is 1,8,64,256,1024. Unreachable from the UI — but a REST workflow_dispatch passing ramp_steps: "" skips the input default and takes the fallback, silently selecting the four steps the CTO withheld pending a working relay-side occupancy instrument. Matched to the default.

I had claimed in the issue thread that the cap "can't be re-entered by accepting a default". That was half-true: I changed the default and left the fallback. Same shape as the caveat string — fixed in one place, live in the other.

Verification

772247c is dispatched as a ramp at N=1 only (run 34719002968) — the point is not the measurement, it is that the build path and the receipt assertion execute on a branch at all. No docker daemon in my environment, so this run is the first exercise of that path; I'd rather it fail here than under 1024 tunnels.

Conflict was additive on both sides at the same anchor in the probe's
Vantage receipt step: main (#20, BLO-33455) appended the NIC line-rate
lines to that run block, this branch added actions/checkout as the next
step. Both kept, ordered run-block-then-step.

Validated as a merge rather than by reading the diff: cargo test on the
result, 124 + 16 + 9 + 2 + 19 + 5 + 4 + 1 pass, 0 fail -- including this
branch's each_deliberate_failure_mode_reduces_the_alive_count and main's
#23 sequence-loss tests, which is what a line diff cannot tell you.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 772247c

Prior Findings Dispositioned (1)

  • prior:1cdf214 important 1 — fixed — .github/workflows/amt-public-vantage-probe.yml:339-350 — the workflow pulls the SHA-tagged image and falls back to building from the checked-out tree, then verifies the embedded revision matches github.sha before the ramp proceeds.

Critical Issues (0)

Important Issues (0)

Suggestions (0)

Strengths

  • The workflow validates ramp ordering, positivity, and the supported maximum before making network measurements.
  • Host source-port, container file-descriptor, and readable conntrack ceilings are enforced or explicitly downgraded to observations.
  • The N=1 positive control and receiver-side per-tunnel survival classifier prevent a silent tuple or unmaintained tunnel from being reported as a clean capacity step.
  • The Rust tests cover healthy survival, each deliberate failure mode, keep-alive minimums, and propagation of the measurement caveat.

Recommended Action

  1. No Critical or Important issues remain from this pass.
  2. Keep the ramp receipt and candidate _astats qualification evidence attached to the workflow run before using higher withheld steps.

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 113cb83

Prior Findings Dispositioned (1)

  • prior:1cdf214 important 1 — fixed — .github/workflows/amt-public-vantage-probe.yml:339-350 — the workflow pulls the SHA-tagged image and falls back to building from the checked-out tree, then verifies the embedded revision matches github.sha before the ramp proceeds.

Critical Issues (0)

Important Issues (0)

Suggestions (0)

Strengths

  • The workflow validates ramp ordering, positivity, and the supported maximum before making network measurements.
  • Host source-port, container file-descriptor, and readable conntrack ceilings are enforced or explicitly downgraded to observations.
  • The N=1 positive control and receiver-side per-tunnel survival classifier prevent a silent tuple or unmaintained tunnel from being reported as a clean capacity step.
  • The Rust tests cover healthy survival, each deliberate failure mode, keep-alive minimums, and propagation of the measurement caveat.

Recommended Action

  1. No Critical or Important issues remain from this pass.
  2. Keep the ramp receipt and candidate _astats qualification evidence attached to the workflow run before using higher withheld steps.

…read instrument

The 21:12Z ramp run produced verdict="knee at N>64" from a step whose only
cause was `timed out after 30s in state Idle` x8. That string names no binder:
it is equally consistent with the relay's tunnel table, public-path loss, and
the runner's NAT/source-port limits. The attribution rule was in the caveat and
in a comment, but the verdict did not read it -- the errors were printed next
to the claim rather than gating it. A 3% shortfall at N=256 against a relay
holding ~29 tunnels with AMT_MAX_TUNNELS=10000 is not a table-full condition,
so this would have fed a wrong knee into BLO-20175's sizing model.

Attribution is now a POSITIVE test for relay-side causes, not a blocklist of
host-side symptoms, so an unrecognised error falls through to "not a relay
knee" -- the safe direction. A blocklist spelling of this was written first and
its own negative control caught it matching "refused" inside "relay refused:
tunnel table full".

Second defect, same family: `jq` exits 0 and prints nothing on an empty file,
so `|| echo unreachable` was dead code. astats-baseline.json came back 0 bytes
and both ndjson files empty, and the receipt rendered that as `baseline_pre_ramp=`
-- an unreachable instrument shown in the same shape as one that answered zero.
The sampler ran from the public runner; _astats is in-cluster only.

Third: the qualification rubric offered QUALIFIED or DEAD DIAL and nothing else,
so a column of nulls would have been recorded as the seventh dead dial. Nulls
mean NOT SAMPLED. _astats was read live at relay=29/iface=47 from in-cluster
hours earlier, so condemning it on a reading nobody took would have been wrong.

Validated by replaying the real 21:12Z artifact plus four controls
(relay-attributed -> knee; mixed -> not knee; clean-to-cap -> config-bound;
novel error -> fails safe).

Refs BLO-33457.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: 627da00

Critical Issues (0)

Important Issues (1)

  • [pr-review-toolkit:errors] .github/workflows/amt-public-vantage-probe.yml:332-350 — The host-ceiling guard compares the requested N with the total conntrack table size and total ephemeral-port span, not the currently available capacity. Existing flows on the GitHub runner can consume either resource, so a ramp can pass this preflight and then report a shortfall caused by runner NAT/source-port exhaustion as if it were relay state. The receipt records nf_conntrack_max and the port range but does not measure occupancy or reserve an actually-free budget.
    • Measure available conntrack entries and usable source ports immediately before the ramp, or fail the run when current occupancy cannot be established; compare the largest requested N against that available budget and label the result non-attributable otherwise.

Suggestions (0)

Strengths

  • The workflow validates non-empty, numeric, strictly ascending ramp steps and bounds them to the validated maximum.
  • The SHA-image fallback now builds from the checked-out tree and verifies the embedded client revision before measurement.
  • The N=1 positive control and receiver-side survival classifier prevent a silent tuple or unmaintained tunnel from being reported as clean capacity.
  • The Rust tests cover healthy survival, deliberate failure modes, keep-alive minimums, and propagation of the measurement caveat.

Recommended Action

  1. Fix the Important issue before relying on a ramp result as a relay capacity measurement.
  2. Re-run the ramp only after the preflight can distinguish total host limits from available host capacity.

`alive` never witnessed the relay. It is `state == Active && keepalives_sent
>= 1`, and all three not-alive reasons (fatal_runtime_error,
state_left_active, no_keepalive_sent) are client-local. keepalives_sent is
incremented after this process's own send_to returns. `rx_datagrams` was
collected and reported but was not in the predicate -- and could not be, since
a relay owes an idle established gateway no unprompted traffic.

That would be a weak witness on its own. What makes it an unusable one is the
other half: run_tunnels builds every gateway with `builder(relay)`, so all N
bind the host's default source address and differ only by ephemeral port,
while linux-amt as deployed matches a tunnel on the outer source ADDRESS alone
(`tunnel->addr.ip4 == iph->saddr` in amt_request_handler) and overwrites
source_port from the newest Request. N gateways from one address are therefore
ONE relay tunnel entry -- and every aliased gateway still reads Active and
still sends keep-alives, because its Updates fail the relay's MAC check
silently. So `alive` reports N while the relay holds 1, and no amount of
reading the artifact reveals it.

The existing positive control cannot catch this: deliberately failing a
gateway stops it SENDING, which is the one thing the classifier can see. It
exercises the plumbing, not the confound. The AC anticipated the shape --
"an alive only ever observed to equal requested is indistinguishable from
alive = established" -- and it is worse than that: indistinguishable from
alive = 1.

Fix is a disqualifier the verdict gates on rather than more prose beside the
number: report `distinct_outer_sources` (structurally 1 here), and make the
ramp verdict check it FIRST, superseding every branch including clean-to-cap.
A ramp that sails to the cap on one source address is the worst case, not the
best -- it reports a ceiling having established one tunnel. An absent field
defaults to shared, so an older artifact fails safe.

Controls: shared+clean-to-cap fires; field-absent (the real 21:12Z shape,
alive 248/256) fires; partial provisioning fires; distinct sources does NOT
fire and falls through to the real verdict, so the gate is not clamped shut
against a rig that outgrows it. tunnels_mode asserts distinct_outer_sources
== 1 exactly, so giving the gateways distinct sources must fail this test and
force the field to be set honestly.

This mode cannot produce a relay tunnel-state ceiling. BLO-33636 fixes the
keying; linux-amt kernel/selftests/amt_capacity.sh already provisions 129
distinct source IPs and takes ground truth from the relay's admit/refuse
reply.

Refs BLO-33457, BLO-33636.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@allyblockcast

allyblockcast Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

@ally please re-review at head a660d4c — focus on the new verdict gate and on whether I have retired your Important correctly rather than dodged it.

Your Important (host-ceiling guard measures totals, not available capacity) is correct, and I have not implemented the occupancy measurement. I took the second branch you offered — "label the result non-attributable otherwise" — because while checking it I found a dominant defect that makes the occupancy fix insufficient on its own:

alive never witnessed the relay at all. It is state == Active && keepalives_sent >= 1; all three not-alive reasons are client-local, and keepalives_sent increments after this process's send_to returns. Meanwhile run_tunnels builds every gateway with builder(relay), so all N share the host's default source address — and linux-amt as deployed matches a tunnel on the outer source address alone (tunnel->addr.ip4 == iph->saddr, amt_request_handler), overwriting source_port from the newest Request. So N gateways from one address are one relay tunnel entry, while every aliased gateway still reads Active and still sends keep-alives (its Updates fail the relay's MAC check silently). alive reports N; the relay holds 1.

So a conntrack/source-port fix would have made a non-attributable number precisely non-attributable. Both your failure mode and mine land in the same place — the run cannot be sized against — and the gate now says so for the stronger reason, superseding every branch including clean-to-cap.

Worth your attention: the existing positive control cannot catch this. each_deliberate_failure_mode_reduces_the_alive_count fails a gateway by stopping it sending, which is the one thing the classifier can see. It tests the plumbing, not the confound.

Controls on the new gate: shared+clean-to-cap fires · field-absent (the real 21:12Z artifact shape, alive 248/256) fires · partial provisioning fires · distinct sources does NOT fire and falls through to the real verdict, so the gate is not permanently clamped. tunnels_mode asserts distinct_outer_sources == 1 exactly, so a future per-source rig must fail that test and set the field deliberately.

If you think the occupancy measurement should still land on top of this, say so and I will file it — but I would rather not spend it on a mode I am retiring for the state claim.

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.

0 participants