Align roadmap footnotes with the roadmap grammar - #552
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThe change standardizes roadmap citations as inline links and refactors client, fragmentation, and fixture tests to propagate setup and validation failures explicitly instead of relying on selected ChangesRoadmap link formatting
Fallible test control flow
Possibly related PRs
Suggested labels: Suggested reviewers: Poem
Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (1 error)
✅ Passed checks (19 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
The mapsplice roadmap grammar rejects footnote references
("unsupported inline node `footnoteReference`"). Replace every [^N]
footnote reference in docs/roadmap.md with an inline link at the
reference site (as a parenthetical "see ..." or a direct link on the
existing phrase) and remove the footnote definition block at the end of
the file. All link targets are preserved. No wording beyond the link
conversions, no ordering, numbering, or checkbox states change.
3994287 to
8509c39
Compare
Reviewer's GuideConvert roadmap footnote references to inline links to satisfy the mapsplice roadmap grammar and maintain markdownlint compliance, without altering roadmap wording or semantics. File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- The new inline references mix ADR numbers, design names, and raw filenames as link text (e.g.
ADR 0001vsrust-binary-router-library-design.md); consider standardizing the visible text so link targets read consistently throughout the roadmap. - The repeated inline references to the formal verification guide in 15.2.x now duplicate the full section title each time; you could tighten these by structuring the bullets to refer once to the guide and then list the specific questions, reducing redundancy and improving readability.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The new inline references mix ADR numbers, design names, and raw filenames as link text (e.g. `ADR 0001` vs `rust-binary-router-library-design.md`); consider standardizing the visible text so link targets read consistently throughout the roadmap.
- The repeated inline references to the formal verification guide in 15.2.x now duplicate the full section title each time; you could tighten these by structuring the bullets to refer once to the guide and then list the specific questions, reducing redundancy and improving readability.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8509c39baa
ℹ️ 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".
| returns a bounded channel sender alongside a `Response::MultiPacket` so | ||
| handlers can opt into streaming ergonomically.[^adr-0001] | ||
| handlers can opt into streaming ergonomically (see | ||
| [ADR 0001](adr-001-multi-packet-streaming-response-api.md)). |
There was a problem hiding this comment.
Reconcile inline links with the required footnote format
This converts the roadmap citation to an inline link, and the same change removes every footnote definition, even though repository instructions require GitHub-flavoured [^1] references. Since the stated parser constraint makes that representation impossible, document a scoped roadmap exception or extend the parser before converting the canonical roadmap; otherwise this file is knowingly left non-conformant.
AGENTS.md reference: AGENTS.md:L308-L309
Useful? React with 👍 / 👎.
|
|
||
| - [x] 9.2.1. Introduce a `FragmentAdapter` trait as described in the | ||
| fragmentation design.[^fragmentation-design] Fragmentation behaviour must | ||
| [fragmentation design](generic-message-fragmentation-and-re-assembly-design.md). |
There was a problem hiding this comment.
Shorten the two new overlong link lines
This newly added list continuation is 82 columns long, exceeding the mandated 80-column limit; the streaming-design link on line 407 introduces the same regression. Shorten the link text or restructure these references so both changed lines remain within the documented limit.
AGENTS.md reference: AGENTS.md:L304-L304
Useful? React with 👍 / 👎.
The whitaker `no_expect_outside_tests` (and unwrap) lint flags
`.expect()`/`.unwrap()` in helper and fixture functions that are not
recognised as tests. Around fifty such calls across the client and
fragment test helpers left `make lint` red.
Fix the calls at the root rather than suppressing the lint:
- Convert value-returning helpers (server spawners, connect and harness
helpers) to return `Result<_, Box<dyn Error + Send + Sync>>` and
propagate with `?`; callers that are `#[rstest]` tests return
`TestResult`, while plain `#[tokio::test]` callers keep `.expect()`
in the test body (allowed, and required by `panic_in_result_fn`).
- Replace `.expect()` inside spawned-task closures with `let ... else`
early returns so the `JoinHandle` type is unchanged.
- Recover poisoned mutexes in fixed-signature hook closures via
`PoisonError::into_inner` instead of `.expect("lock")`.
- Build `NonZeroUsize` fixtures from literals through a small `nz`
helper.
Behaviour is preserved throughout; this is error-plumbing only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address review feedback on the footnote-to-inline-link conversion in docs/roadmap.md: - Standardize the visible link text: cite ADRs as `ADR NNNN` and design documents by a short descriptive name rather than a raw filename, so the inline references read consistently. - Reference the formal verification guide once in the §15.2 introduction and let each task cite only its specific section, removing the repeated full guide link. - Keep every changed line within the 80-column limit by linking the distinguishing keyword and leaving "design" in the surrounding prose. Document why the roadmap cannot use GitHub-flavoured footnotes: the mapsplice roadmap grammar rejects footnote references, so roadmap-shaped Markdown must cite references inline. Record this scoped exception in AGENTS.md and the documentation style guide. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Gates Failed
Enforce advisory code health rules
(1 file with Code Duplication)
Our agent can fix these. Install it.
Gates Passed
5 Quality Gates Passed
Reason for failure
| Enforce advisory code health rules | Violations | Code Health Impact | |
|---|---|---|---|
| helpers.rs | 1 advisory rule | 10.00 → 9.39 | Suppress |
Quality Gate Profile: Pay Down Tech Debt
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.
| Arc<dyn Fn(T) -> Pin<Box<dyn Future<Output = T> + Send>> + Send + Sync>; | ||
|
|
||
| /// Spawns a TCP listener and returns the address and a handle to accept a connection. | ||
| /// |
There was a problem hiding this comment.
❌ New issue: Code Duplication
The module contains 2 functions with similar structure: FailingSerializer.deserialize,FailingSerializer.serialize
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 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 `@AGENTS.md`:
- Around line 310-314: Remove the comma before the essential “because” clause in
AGENTS.md lines 310-314, changing “instead, because” to “instead because”; make
the corresponding punctuation correction in docs/documentation-style-guide.md
lines 65-68, changing “footnotes, because” to “footnotes because,” and preserve
the required Triage paragraph.
In `@src/client/tests/helpers.rs`:
- Around line 32-45: Update the shared fixture helper spawn_listener to return a
Result instead of panicking on TcpListener::bind, local_addr, or accept
failures. Preserve the accepted stream and address on success, make the spawned
accept task expose its inner Result, and propagate the revised result type
through every caller using ? alongside the existing fallible helpers
assert_builder_option and connect_with_server.
In `@src/client/tests/request_hooks.rs`:
- Around line 91-122: The run_hook_test and run_hook_test_with_capture helpers
currently discard test-body and server-task failures. Change their test_body
bounds to futures returning TestResult, propagate body errors with ?, propagate
server.await failures with ?, and return Ok(()) from shared test bodies and
closures that now satisfy the Result contract.
In `@src/fragment/tests/adapter_tests.rs`:
- Around line 16-25: Centralize the duplicated non-zero constructor by defining
the shared helper as non_zero_usize in the fragment-test utility, then remove
the local helper from src/fragment/tests/adapter_tests.rs lines 16-25 and import
the shared symbol; likewise remove the local helper from
src/fragment/tests/reassembler_tests.rs lines 22-31 and import non_zero_usize
there.
In `@src/fragment/tests/reassembler_tests.rs`:
- Around line 33-44: Update the reassembler_with_first_fragment fixture to
return Result<Reassembler, ReassemblyError> and propagate the initial
reassembler.push result with ?. Preserve the assertion that the first fragment
yields Ok(None), while allowing setup errors to flow through the fixture instead
of being masked by a generic panic.
In `@tests/fixtures/client_pair_harness.rs`:
- Around line 41-47: Replace the panic-based runtime initialization in the
fixture’s constructor with a fallible constructor returning TestResult<Self>,
and propagate tokio::runtime::Runtime::new() errors using ?. Remove the
Default-based construction and update all client pair harness fixture call sites
to invoke the fallible constructor with ?, preserving shared fixture setup
without panics.
🪄 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: ASSERTIVE
Plan: Pro Plus
Run ID: c13099df-4b6c-4f17-9707-830d47b07194
📒 Files selected for processing (15)
AGENTS.mddocs/documentation-style-guide.mddocs/roadmap.mdsrc/client/tests/error_handling.rssrc/client/tests/helpers.rssrc/client/tests/messaging.rssrc/client/tests/request_hooks.rssrc/client/tests/streaming.rssrc/client/tests/tracing.rssrc/fragment/tests/adapter_tests.rssrc/fragment/tests/fragmenter_tests.rssrc/fragment/tests/reassembler_tests.rstests/fixtures/client_lifecycle.rstests/fixtures/client_pair_harness.rstests/fixtures/panic.rs
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
leynos/rust-prover-tools(auto-detected)leynos/shared-actions(auto-detected)leynos/whitaker(auto-detected)
| - Exception: roadmap-shaped Markdown edited with `mapsplice` (for example | ||
| `docs/roadmap.md`) must use inline links instead, because the `mapsplice` | ||
| roadmap grammar rejects footnote references (`unsupported inline node | ||
| footnoteReference`). Cite references inline, either as a parenthetical | ||
| `(see [target](path))` or by linking an existing phrase. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove the comma before the essential “because” clauses.
Apply the same punctuation correction in both guidance documents.
AGENTS.md#L310-L314: changeinstead, becausetoinstead because.docs/documentation-style-guide.md#L65-L68: changefootnotes, becausetofootnotes because.
Triage: [type:grammar]
As per coding guidelines, keep Markdown guidance consistent with the documented formatting rules. As per path instructions, grammar findings require a Triage: paragraph.
🧰 Tools
🪛 LanguageTool
[formatting] ~311-~311: If the ‘because’ clause is essential to the meaning, do not use a comma before the clause.
Context: ...admap.md) must use inline links instead, because the mapsplice` roadmap grammar rej...
(COMMA_BEFORE_BECAUSE)
📍 Affects 2 files
AGENTS.md#L310-L314(this comment)docs/documentation-style-guide.md#L65-L68
🤖 Prompt for 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.
In `@AGENTS.md` around lines 310 - 314, Remove the comma before the essential
“because” clause in AGENTS.md lines 310-314, changing “instead, because” to
“instead because”; make the corresponding punctuation correction in
docs/documentation-style-guide.md lines 65-68, changing “footnotes, because” to
“footnotes because,” and preserve the required Triage paragraph.
Sources: Coding guidelines, Path instructions, Linters/SAST tools
| pub async fn spawn_listener() -> (SocketAddr, tokio::task::JoinHandle<TcpStream>) { | ||
| let listener = TcpListener::bind("127.0.0.1:0") | ||
| .await | ||
| .expect("bind listener"); | ||
| let addr = listener.local_addr().expect("listener addr"); | ||
| let Ok(listener) = TcpListener::bind("127.0.0.1:0").await else { | ||
| panic!("bind listener"); | ||
| }; | ||
| let Ok(addr) = listener.local_addr() else { | ||
| panic!("listener addr"); | ||
| }; | ||
| let accept = tokio::spawn(async move { | ||
| let (stream, _) = listener.accept().await.expect("accept client"); | ||
| let Ok((stream, _)) = listener.accept().await else { | ||
| panic!("accept client"); | ||
| }; | ||
| stream | ||
| }); | ||
| (addr, accept) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Return listener failures from the shared fixture.
Line 32 exports this helper for other client tests, but Lines 33-42 replace
fallible networking with panic!. Return a Result from spawn_listener,
including the accept task's inner result, then propagate it through callers.
This preserves the fallible contract already introduced in
assert_builder_option and connect_with_server.
As per coding guidelines, “In production code and shared fixtures, return
Result and propagate errors with ?; avoid .expect() and panics.”
🤖 Prompt for 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.
In `@src/client/tests/helpers.rs` around lines 32 - 45, Update the shared fixture
helper spawn_listener to return a Result instead of panicking on
TcpListener::bind, local_addr, or accept failures. Preserve the accepted stream
and address on success, make the spawned accept task expose its inner Result,
and propagate the revised result type through every caller using ? alongside the
existing fallible helpers assert_builder_option and connect_with_server.
Source: Coding guidelines
| async fn run_hook_test<F, T>(configure_hooks: F, test_body: T) -> TestResult | ||
| where | ||
| F: FnOnce(crate::client::WireframeClientBuilder) -> crate::client::WireframeClientBuilder, | ||
| T: for<'a> FnOnce( | ||
| &'a mut TestClient, | ||
| ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + 'a>>, | ||
| { | ||
| let (addr, server) = spawn_echo_server().await; | ||
| let mut client = connect_client_with_hooks(addr, configure_hooks).await; | ||
| let (addr, server) = spawn_echo_server().await?; | ||
| let mut client = connect_client_with_hooks(addr, configure_hooks).await?; | ||
| test_body(&mut client).await; | ||
| drop(client); | ||
| let _ = server.await; | ||
| Ok(()) | ||
| } | ||
|
|
||
| /// Variant for tests that need the capturing server. | ||
| async fn run_hook_test_with_capture<F, T>(configure_hooks: F, test_body: T) -> Vec<Vec<u8>> | ||
| async fn run_hook_test_with_capture<F, T>( | ||
| configure_hooks: F, | ||
| test_body: T, | ||
| ) -> Result<Vec<Vec<u8>>, BoxError> | ||
| where | ||
| F: FnOnce(crate::client::WireframeClientBuilder) -> crate::client::WireframeClientBuilder, | ||
| T: for<'a> FnOnce( | ||
| &'a mut TestClient, | ||
| ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + 'a>>, | ||
| { | ||
| let (addr, server) = spawn_capturing_server().await; | ||
| let mut client = connect_client_with_hooks(addr, configure_hooks).await; | ||
| let (addr, server) = spawn_capturing_server().await?; | ||
| let mut client = connect_client_with_hooks(addr, configure_hooks).await?; | ||
| test_body(&mut client).await; | ||
| drop(client); | ||
| server.await.expect("server completes") | ||
| Ok(server.await?) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Propagate hook-body and server-task failures.
Line 96 fixes the test-body output to (), forcing shared bodies at Lines
203-215 to panic instead of returning their errors. Line 102 also discards a
possible JoinError. Require Future<Output = TestResult>, await the body
with ?, and use server.await?. Return Ok(()) from shared bodies and
their closures.
As per coding guidelines, “In production code and shared fixtures, return
Result and propagate errors with ?; avoid .expect() and panics.”
🤖 Prompt for 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.
In `@src/client/tests/request_hooks.rs` around lines 91 - 122, The run_hook_test
and run_hook_test_with_capture helpers currently discard test-body and
server-task failures. Change their test_body bounds to futures returning
TestResult, propagate body errors with ?, propagate server.await failures with
?, and return Ok(()) from shared test bodies and closures that now satisfy the
Result contract.
Source: Coding guidelines
| /// Construct a [`NonZeroUsize`] from a compile-time-known non-zero literal. | ||
| /// | ||
| /// # Panics | ||
| /// Panics if `n` is zero; callers pass non-zero literals only. | ||
| const fn nz(n: usize) -> NonZeroUsize { | ||
| match NonZeroUsize::new(n) { | ||
| Some(v) => v, | ||
| None => panic!("value must be non-zero"), | ||
| } | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Centralize the duplicated non-zero helper.
Extract this helper into a shared fragment-test utility, name it non_zero_usize, and
import it in both modules.
src/fragment/tests/adapter_tests.rs#L16-L25: remove the local helper and import the
shared helper.src/fragment/tests/reassembler_tests.rs#L22-L31: remove the local helper and import
the shared helper.
As per coding guidelines, “extract reusable logic instead of duplicating code”.
📍 Affects 2 files
src/fragment/tests/adapter_tests.rs#L16-L25(this comment)src/fragment/tests/reassembler_tests.rs#L22-L31
🤖 Prompt for 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.
In `@src/fragment/tests/adapter_tests.rs` around lines 16 - 25, Centralize the
duplicated non-zero constructor by defining the shared helper as non_zero_usize
in the fragment-test utility, then remove the local helper from
src/fragment/tests/adapter_tests.rs lines 16-25 and import the shared symbol;
likewise remove the local helper from src/fragment/tests/reassembler_tests.rs
lines 22-31 and import non_zero_usize there.
Source: Coding guidelines
| #[fixture] | ||
| fn reassembler_with_first_fragment( | ||
| #[default(1)] message_id: u64, | ||
| #[default(&[])] first_payload: &'static [u8], | ||
| ) -> Reassembler { | ||
| let mut reassembler = Reassembler::new( | ||
| NonZeroUsize::new(8).expect("non-zero"), | ||
| Duration::from_secs(30), | ||
| ); | ||
| let mut reassembler = Reassembler::new(nz(8), Duration::from_secs(30)); | ||
| let first = FragmentHeader::new(MessageId::new(message_id), FragmentIndex::zero(), false); | ||
| assert!( | ||
| reassembler | ||
| .push(first, first_payload) | ||
| .expect("first fragment accepted") | ||
| .is_none() | ||
| matches!(reassembler.push(first, first_payload), Ok(None)), | ||
| "first fragment should be accepted without completing the message", | ||
| ); | ||
| reassembler |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- src/fragment/tests/reassembler_tests.rs (lines 1-120) ---'
sed -n '1,120p' src/fragment/tests/reassembler_tests.rs | cat -n
echo
echo '--- Reassembler::push occurrences ---'
rg -n "fn push|push\(" src -g '*.rs' | sed -n '1,120p'Repository: leynos/wireframe
Length of output: 10078
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- fallible rstest fixtures / Result-returning fixtures ---'
rg -n "^\s*#\[fixture\]|\bfn .*-> Result<" src -g '*.rs' | sed -n '1,200p'
echo
echo '--- nearby reassembler fixture usages ---'
sed -n '110,220p' src/fragment/tests/reassembler_tests.rs | cat -nRepository: leynos/wireframe
Length of output: 20486
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- app/builder/core.rs fixture definitions and uses ---'
sed -n '214,280p' src/app/builder/core.rs | cat -n
echo
echo '--- message_assembler/state_tests.rs fixture definitions and uses ---'
sed -n '1,120p' src/message_assembler/state_tests.rs | cat -nRepository: leynos/wireframe
Length of output: 7648
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- app/builder/core.rs relevant fixture/test excerpt ---'
sed -n '220,260p' src/app/builder/core.rs | cat -n
echo
echo '--- message_assembler/state_tests.rs relevant fixture/test excerpt ---'
sed -n '1,80p' src/message_assembler/state_tests.rs | cat -nRepository: leynos/wireframe
Length of output: 5071
Return this fixture as Result<Reassembler, ReassemblyError> and use ? for the first push. The assert!(matches!(...)) path masks setup failures behind a generic panic; keep fixture failures flowing as errors.
🤖 Prompt for 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.
In `@src/fragment/tests/reassembler_tests.rs` around lines 33 - 44, Update the
reassembler_with_first_fragment fixture to return Result<Reassembler,
ReassemblyError> and propagate the initial reassembler.push result with ?.
Preserve the assertion that the first fragment yields Ok(None), while allowing
setup errors to flow through the fixture instead of being masked by a generic
panic.
Source: Coding guidelines
| // `Default` cannot propagate errors, so fail loudly if the runtime cannot | ||
| // be created. `let ... else { panic! }` avoids the bare | ||
| // `expect`/`unwrap`/`unwrap_or_else` panics the test-only lints forbid | ||
| // while preserving the fail-fast behaviour. | ||
| let Ok(runtime) = tokio::runtime::Runtime::new() else { | ||
| panic!("failed to create runtime"); | ||
| }; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
Propagate runtime creation errors from the fixture.
let Ok(runtime) = ... else { panic!(...) } still aborts shared fixture setup; it only replaces .expect(...) with another panic to evade clippy::expect_used. Remove the Default-based construction, expose a fallible constructor returning TestResult<Self>, and update fixture call sites to propagate ?.
As per coding guidelines, “In production code and shared fixtures, return Result and propagate errors with ?; avoid .expect() and panics.”
🤖 Prompt for 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.
In `@tests/fixtures/client_pair_harness.rs` around lines 41 - 47, Replace the
panic-based runtime initialization in the fixture’s constructor with a fallible
constructor returning TestResult<Self>, and propagate
tokio::runtime::Runtime::new() errors using ?. Remove the Default-based
construction and update all client pair harness fixture call sites to invoke the
fallible constructor with ?, preserving shared fixture setup without panics.
Sources: Coding guidelines, Path instructions
Summary
This branch aligns the combined development roadmap with the mapsplice roadmap
grammar, which rejects footnote references ("unsupported inline node
footnoteReference"). All fourteen[^...]footnote reference sites indocs/roadmap.md
are converted to inline links — either a parenthetical "(see target)"
at the reference site or a direct link on the existing phrase — and the
footnote definition block at the end of the file is removed. Every link
target is preserved. No wording beyond the link conversions, ordering,
numbering, or checkbox states change.
Review walkthrough
Validation
mapsplice append docs/roadmap.md <dummy-phase>: exit 0 (grammar-clean)mapsplice append docs/zero-copy-frame-and-payload-migration-roadmap.md <dummy-phase>: exit 0bunx markdownlint-cli2 docs/roadmap.md: 0 errorsNotes
## Phase N:headings with task tables and fails the checker ("roadmap must contain at least one numbered phase"), but docs/roadmap.md describes itself as "merging previous roadmap documents into a single source of truth", so the 1.0 detailed roadmap is treated as superseded and deliberately left unchanged.MD013clean; only lines authored by this change were rewrapped.Summary by Sourcery
Documentation:
References