Skip to content

relayburn-cli: make pending-stamp adapter compatible with phf registry (follow-up to #307)#311

Merged
willwashburn merged 3 commits into
mainfrom
rust-port/harness-substrate-fix
May 6, 2026
Merged

relayburn-cli: make pending-stamp adapter compatible with phf registry (follow-up to #307)#311
willwashburn merged 3 commits into
mainfrom
rust-port/harness-substrate-fix

Conversation

@willwashburn

Copy link
Copy Markdown
Member

Summary

Follow-up to #307 (commit fb47c86) addressing the CodeRabbit review comment at #306 (comment).

The bug

crates/relayburn-cli/src/harnesses/registry.rs declares the harness registry as

static ADAPTERS: phf::Map<&'static str, &'static dyn HarnessAdapter> = phf_map! { ... };

while pending_stamp::adapter returned Box<dyn HarnessAdapter>. The Wave 2 plan documented in the same file (lines 28–31 of the original) said codex and opencode would be "constructed through super::pending_stamp::adapter", but a runtime Box cannot satisfy a &'static dyn HarnessAdapter value bound. The codex / opencode adapter PRs (#248-e / #248-f) would not have compiled.

The fix — Option A (Box::leak)

Add pub fn adapter_static(config: PendingStampAdapter) -> &'static dyn HarnessAdapter next to the existing adapter. The new function Box::leaks the impl so it can be stored as &'static. Wave 2 adapters register via:

static CODEX_ADAPTER: LazyLock<&'static dyn HarnessAdapter> =
    LazyLock::new(|| pending_stamp::adapter_static(...));

Why option A over option B (runtime OnceLock<HashMap<...>>)

  • The registry only ever holds ~3 harnesses (claude, codex, opencode); the leak is bytes, unrecoverable until process exit, which is exactly when an adapter would be dropped anyway.
  • Keeps the phf::Map perfect-hash lookup intact — the original PR called this out as a deliberate cold-start optimisation for burn --help / burn summary.
  • No Send + Sync bound spread, no first-lookup runtime cost, no Arc wrapper to hand back a borrow.
  • The existing adapter (returning Box) is preserved for unit tests and bespoke drivers that want a droppable instance.

The Box::leak is documented on adapter_static so callers know what they're opting into.

Test added

harnesses/registry.rs::tests::pending_stamp_adapter_static_fits_phf_registry constructs a fake pending-stamp config, runs it through adapter_static, and proves the resulting &'static dyn HarnessAdapter satisfies the same value-type bound that ADAPTERS requires. This is the regression test that would have caught the original architectural mismatch.

Test plan

  • cargo build -p relayburn-cli clean
  • cargo test --workspace green (605 sdk + 10 cli + 2 integration + 3 doc-tests, 0 failed)
  • RUSTDOCFLAGS="-D warnings" cargo doc -p relayburn-cli --no-deps clean (also fixed three pre-existing rustdoc warnings around private-item links)
  • New test pending_stamp_adapter_static_fits_phf_registry runs and passes

The Wave 2 plan in `harnesses/registry.rs` calls for codex/opencode to
register through `pending_stamp::adapter`, but that factory returned a
runtime `Box<dyn HarnessAdapter>` while the `ADAPTERS` map is a
compile-time `phf::Map<&'static str, &'static dyn HarnessAdapter>`.
A `Box` cannot satisfy the `&'static` value bound, so the codex /
opencode adapters would not have compiled when their PRs landed.

Add `pending_stamp::adapter_static` that `Box::leak`s the impl and
returns `&'static dyn HarnessAdapter`. Wave 2 adapters wrap the call
in a `LazyLock<&'static dyn HarnessAdapter>` next to their module
declaration. The leak is bounded (one Box per harness, ~3 ever) and
adapters live until process exit anyway, so it's the right trade-off
versus converting the registry to a runtime `OnceLock<HashMap<...>>`.

Add a regression test in `harnesses/registry.rs` that proves a
`pending_stamp::adapter_static` value satisfies the same value-type
bound `ADAPTERS` requires.

Follow-up to #307. Addresses the CodeRabbit review comment at
#306 (comment).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e7c714a6-a7f3-4b93-97eb-97ab83ba489e

📥 Commits

Reviewing files that changed from the base of the PR and between b0a7542 and 2a75b3c.

📒 Files selected for processing (1)
  • crates/relayburn-cli/src/harnesses/registry.rs

📝 Walkthrough

Walkthrough

Adds a static-lifetime factory adapter_static(config) -> &'static dyn HarnessAdapter alongside the existing boxed adapter(...), and updates registry docs and tests to exercise storing and retrieving a leaked static pending-stamp adapter in both eager (static) and runtime registries.

Changes

Static HarnessAdapter Factory & Registry Integration

Layer / File(s) Summary
Public API / Data Shape
crates/relayburn-cli/src/harnesses/pending_stamp.rs
Adds adapter(config) -> Box<dyn HarnessAdapter> and adapter_static(config) -> &'static dyn HarnessAdapter (the latter leaks a boxed adapter to produce a 'static reference). Doc comments explain leak-by-design semantics.
Documentation / Narrative
crates/relayburn-cli/src/harnesses/registry.rs
Replaces prior Wave 2 wiring commentary with updated documentation describing three Wave 2 adapters and rationale for static vs runtime wiring and adapter_static usage in eager registries.
Test Imports & Fixtures
crates/relayburn-cli/src/harnesses/registry.rs (test module)
Adds imports (Arc, LazyLock, pending_stamp types) and introduces module-scoped fixtures: FAKE_PENDING_STAMP_ADAPTER (via adapter_static), FAKE_EAGER_REGISTRY (static map), and FAKE_RUNTIME_REGISTRY (runtime scaffold).
Eager Registry Tests
crates/relayburn-cli/src/harnesses/registry.rs (tests)
Adds dyn_adapter_round_trip_by_name test that looks up the fake adapter in the static eager registry and asserts adapter name and session root values.
Runtime Registry Tests & Type Assertion
crates/relayburn-cli/src/harnesses/registry.rs (tests)
Adds pending_stamp_adapter_static_fits_runtime_registry which copies the static adapter into a runtime registry value, verifies name and session root, and includes a compile-time assertion that the static adapter return type exactly matches the runtime registry's value type.
Deterministic Ordering Tests
crates/relayburn-cli/src/harnesses/registry.rs (tests)
Reworks and expands tests validating stable, deterministic ordering of harness name listing to align with new static registry fixtures.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • AgentWorkforce/burn#307: Directly related — also modifies pending_stamp adapter and registry wiring; this PR introduces adapter_static() used by the registry tests.

Poem

🐰 I forged a static mold, quite bold,
A tiny box that won't grow old.
In registries it takes its place,
Leaked and steady, holds its grace.
Tests hop by and nod: "It's gold!"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding static adapter compatibility with the phf registry as a follow-up to PR #307.
Description check ✅ Passed The description is directly related to the changeset, explaining the architectural mismatch, the fix via Box::leak, rationale vs alternatives, and testing performed.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rust-port/harness-substrate-fix

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d091cbb695

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +200 to +201
let map: std::collections::HashMap<&'static str, &'static dyn HarnessAdapter> =
std::iter::once(("codex", leaked)).collect();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exercise adapter_static through phf_map!

This regression test is meant to prove compatibility with the production ADAPTERS table, but it inserts the leaked adapter into a runtime HashMap instead of a phf_map! static. That misses the key constraint of the real registry path (compile-time/static initialization), so the test can pass even if codex/opencode wiring still cannot be represented in ADAPTERS. Please assert compatibility via a phf-shaped static check rather than HashMap so this catches the original class of failure.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in b0a7542 by going with the architectural option ("split eager vs runtime registries") rather than just rewriting the test in place.

Why a split: pending_stamp::adapter_static does Box::leak at runtime, so the resulting &'static dyn HarnessAdapter is not a const expression and phf_map! can't hold it directly. The previous registry doc-comments implied codex/opencode would land in ADAPTERS, but that was wrong — those slots could only ever hold const references like &CLAUDE_ADAPTER. The new layout reflects the real constraint:

  • EAGER_ADAPTERS: phf::Map<…> — claude (const & to a static unit struct).
  • RUNTIME_ADAPTERS: LazyLock<HashMap<&'static str, &'static dyn HarnessAdapter>> — codex / opencode (adapter_static-built leaked references).

lookup probes phf first then falls back to the runtime tier, so common-case lookups still pay only a single perfect-hash probe.

Test now exercises the real production path. The regression test (pending_stamp_adapter_static_fits_runtime_registry) uses a module-scoped FAKE_RUNTIME_REGISTRY: LazyLock<HashMap<…>> with the exact value type production uses for RUNTIME_ADAPTERS. If adapter_static ever stopped returning &'static dyn HarnessAdapter, construction of that fixture would fail to compile.

Belt-and-braces: I also added a compile-time assertion that pins adapter_static's signature to the registry's value bound:

const _ASSERT_ADAPTER_STATIC_FITS_REGISTRY:
    fn(PendingStampAdapter) -> &'static dyn HarnessAdapter =
    pending_stamp::adapter_static;

Verified by deliberate breakage: I temporarily made adapter_static return Box<dyn HarnessAdapter> and confirmed both the runtime fixture and the const assertion fail to compile (expected \&dyn HarnessAdapter`, found `Box`). Reverted and cargo test --workspace` is green.

The previous registry doc-comments claimed codex/opencode would land
in the `phf::Map`, but pending-stamp adapters are constructed at
runtime via `Box::leak` and cannot appear in `phf_map!` (which needs
const expressions). Split the registry into two tiers that match the
real constraint:

* `EAGER_ADAPTERS: phf::Map<…>` — for stateless unit-struct adapters
  whose value is a const reference (claude lands here).
* `RUNTIME_ADAPTERS: LazyLock<HashMap<…>>` — for adapters built via
  `pending_stamp::adapter_static` whose `&'static dyn HarnessAdapter`
  is produced at runtime (codex / opencode land here).

`lookup` checks the phf tier first, then falls back to the runtime
tier; common-case lookups still pay only a single perfect-hash probe.

Rewrites the regression test to exercise the actual production
wiring path: `FAKE_RUNTIME_REGISTRY` uses the same `LazyLock<HashMap<
&'static str, &'static dyn HarnessAdapter>>` value type that
production uses, so insertion of a leaked `pending_stamp::adapter_
static(...)` reference proves compatibility with the registry it
will actually live in. Adds a compile-time assertion
(`_ASSERT_ADAPTER_STATIC_FITS_REGISTRY`) that pins
`adapter_static`'s signature to the registry's value bound — if the
factory regresses to `Box<dyn HarnessAdapter>`, both the runtime
test fixture and the const assertion fail to compile.

Addresses Codex review comment on PR #311.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/relayburn-cli/src/harnesses/registry.rs`:
- Around line 115-118: list_harness_names currently dereferences
RUNTIME_ADAPTERS (a LazyLock) which forces its initialization and also relies on
HashMap key order, causing non-deterministic help output; instead, introduce a
separate static ordered list (e.g. RUNTIME_ADAPTER_NAMES: &[&'static str']) that
contains runtime adapter names in the desired deterministic order and change
list_harness_names to extend the eager names with that static slice without
touching RUNTIME_ADAPTERS; update references to RUNTIME_ADAPTERS only where
runtime initialization is actually required.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9d14b0e7-8a92-43fb-bfbc-82c6e90bc0b5

📥 Commits

Reviewing files that changed from the base of the PR and between d091cbb and b0a7542.

📒 Files selected for processing (1)
  • crates/relayburn-cli/src/harnesses/registry.rs

Comment thread crates/relayburn-cli/src/harnesses/registry.rs
…erministic ordering (review fix round 2)

list_harness_names previously called RUNTIME_ADAPTERS.keys(), which
forces LazyLock init via Deref and defeats the cold-start goal stated
in the registry's own doc comment (burn --help shouldn't pay
harness-table construction cost). It also leaked HashMap's
HashDoS-randomized iteration order into user-facing help output, so
the harness list could flicker between runs once Wave 2 adapters
land.

Fix:

* Add a sibling RUNTIME_ADAPTER_NAMES: &[&str] static next to
  RUNTIME_ADAPTERS. Doc comment calls out the Wave 2 sync requirement:
  every codex (#248-e) / opencode (#248-f) PR must uncomment two rows
  in lockstep — its RUNTIME_ADAPTERS insert and its
  RUNTIME_ADAPTER_NAMES entry.

* list_harness_names now extends with RUNTIME_ADAPTER_NAMES.iter().
  RUNTIME_ADAPTERS is no longer touched on the help path.

* Replace the empty-list test with list_harness_names_is_deterministic,
  which snapshots the expected ordering against an EXPECTED_HARNESS_NAMES
  constant and asserts two consecutive calls return the identical Vec.
  Doc comment leaves a cold-start proof: by inspection,
  list_harness_names doesn't touch the LazyLock, so the test passes
  without ever forcing init.

* Add runtime_adapter_names_match_runtime_adapters as a sync-invariant
  test: every name in RUNTIME_ADAPTER_NAMES must resolve through
  lookup. Empty no-op today, pinned for Wave 2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@willwashburn willwashburn merged commit 31b998b into main May 6, 2026
3 checks passed
@willwashburn willwashburn deleted the rust-port/harness-substrate-fix branch May 6, 2026 13:42
willwashburn added a commit that referenced this pull request May 6, 2026
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