Skip to content

[WS2] Add CP-aware Attention contract and dispatch metadata - #236

Merged
Flink-ddd merged 16 commits into
RL-Align:testfrom
Bsgg1:feat/issue-235-attention-cp-contract
Aug 24, 2026
Merged

[WS2] Add CP-aware Attention contract and dispatch metadata #236
Flink-ddd merged 16 commits into
RL-Align:testfrom
Bsgg1:feat/issue-235-attention-cp-contract

Conversation

@Bsgg1

@Bsgg1 Bsgg1 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Scope

PR1 defines the typed Attention contract and strict dispatch metadata used by the later CP reference, decode, backward, and acceptance PRs.

Contract

Area Enforced behavior
Ownership Validates TP-local GQA heads, CP sequence ownership, and stable global block indices
Reduction FP32 online-softmax Out/LSE state and final-write downcast
Split-KV Explicit policy and complete batch x TP x CP x owner provenance; mismatches fail closed
RoPE and KV cache Records position, cache, page, dtype, and fused-boundary identity
Strict core rlkernel.attention.deterministic_core.v1 is the shared production identity

Validation

Check Result
Focused contract and dispatch suite 71 passed, 2 skipped
Full local reviewed regression 717 passed, 1054 skipped after excluding three unavailable Triton tests
Integrated 8xH100 acceptance Passed: strict provenance accepted across 2/4/8 ranks

Boundary

This PR owns contract construction and capability gating. CP execution, deterministic arithmetic, and H100 evidence are validated by PR279 and PR285.

Commit: 51b8f52

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a3f90838-b4f0-4aff-ba1d-2c274e1aa8ac

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds a typed WS2 CP-aware attention contract, validates attention metadata, adds capability-aware registry dispatch with provenance, and documents and tests strict backend rejection behavior.

Changes

WS2 attention contract and dispatch

Layer / File(s) Summary
Contract model and attention metadata
docs/design/ws2-cp-attention-contract.md, rl_engine/kernels/attention_contract.py
Defines TP/CP sharding, deterministic FP32 reduction, KV-cache, RoPE, batching, validation, serialization, and public contract types.
Capability-aware registry dispatch
rl_engine/kernels/registry.py, docs/design/ws2-cp-attention-contract.md
Adds get_attention_op(contract), registers backend capabilities, filters incompatible candidates, rejects undeclared fallbacks, and records provenance.
Contract and dispatch validation
tests/test_attention_contract.py
Tests contract construction, sharding, reduction, RoPE, KV-cache, packed layouts, capability matching, rejection behavior, and provenance.
Dispatch documentation and CI coverage
docs/design/runtime-dispatch.md, docs/operators/attention.md, .github/workflows/ci.yml
Documents WS2 dispatch rules, explicit rejection, unchanged legacy dispatch, and CI execution of the contract tests.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: flink-ddd, kjldefeated, zhangj1an

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: adding the WS2 CP-aware Attention contract and dispatch metadata.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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 `@rl_engine/kernels/attention_contract.py`:
- Around line 403-420: Rename the loop-bound active_blocks variables in the
sequence consistency loop and the exclusive_page_owners loop to distinct names,
updating their references within each loop. Preserve the existing list-based
active_blocks binding and all validation and ownership logic.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: bb3bdb48-0e1e-4f2b-9707-bebdb8e47072

📥 Commits

Reviewing files that changed from the base of the PR and between 6df029a and b7ba64b.

📒 Files selected for processing (6)
  • docs/design/runtime-dispatch.md
  • docs/design/ws2-cp-attention-contract.md
  • docs/operators/attention.md
  • rl_engine/kernels/attention_contract.py
  • rl_engine/kernels/registry.py
  • tests/test_attention_contract.py

Comment thread rl_engine/kernels/attention_contract.py Outdated
@Bsgg1
Bsgg1 requested a review from bitborne as a code owner July 19, 2026 15:59
Comment thread rl_engine/kernels/attention_contract.py Outdated
object.__setattr__(self, "mode", _enum_value(AttentionMode, self.mode, "mode"))
object.__setattr__(self, "dtype", _enum_value(AttentionDType, self.dtype, "dtype"))
batch_size = _positive_int(self.batch_size, "batch_size")
_positive_int(self.query_sequence_length, "query_sequence_length")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Small contract gap: for prefill, should we reject cases where query_sequence_length doesn’t match sharding.local_sequence_length? Right now an inconsistent contract like query_sequence_length=2048 with local_sequence_length=1024 can still be constructed.

global_token_positions = _integer_tuple(
self.global_token_positions, "global_token_positions"
)
if not cache_positions or any(position < 0 for position in cache_positions):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Another small gap: cache_positions is only checked for shape/non-negativity, but not against global_token_positions. For decode identity, we probably want to reject cases like cache_positions=(999,) when the declared tokens are only 0..16.

@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.

🧹 Nitpick comments (2)
tests/test_attention_contract.py (2)

458-466: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Exercise registry rejection for packed-layout incompatibility.

This test only calls AttentionBackendCapability.incompatibilities(...); it never proves that KernelRegistry.get_attention_op() rejects an otherwise declared backend. Register the capability and assert the dispatch error includes the packed-varlen incompatibility, preventing registry regressions from bypassing this check.

🤖 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/test_attention_contract.py` around lines 458 - 466, Extend
test_packed_layout_requires_declared_backend_support to register the capability
with KernelRegistry and invoke get_attention_op() for the packed-varlen
contract. Assert that dispatch is rejected and the resulting error includes
“packed varlen layout is unsupported,” while retaining the direct
incompatibilities assertion.

100-101: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover the stated TP=4/CP=4 target, not only TP=2/CP=2.

The representability test and its declared backend only exercise TP2/CP2, while the PR objective explicitly calls out a Qwen3-8B TP4/CP4 BF16 contract. Add a TP4/CP4 case with matching local sequence/block metadata and capability support so four-way head ownership and CP validation are covered.

Also applies to: 110-116

🤖 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/test_attention_contract.py` around lines 100 - 101, Extend the
representability test’s world-size parameters to include the stated TP=4/CP=4
scenario, and add matching local sequence/block metadata plus backend capability
support for that case. Update the relevant test setup around the
representability test and its declared backend so four-way tensor-parallel head
ownership and context-parallel validation are exercised while preserving the
existing TP2/CP2 coverage.
🤖 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.

Nitpick comments:
In `@tests/test_attention_contract.py`:
- Around line 458-466: Extend
test_packed_layout_requires_declared_backend_support to register the capability
with KernelRegistry and invoke get_attention_op() for the packed-varlen
contract. Assert that dispatch is rejected and the resulting error includes
“packed varlen layout is unsupported,” while retaining the direct
incompatibilities assertion.
- Around line 100-101: Extend the representability test’s world-size parameters
to include the stated TP=4/CP=4 scenario, and add matching local sequence/block
metadata plus backend capability support for that case. Update the relevant test
setup around the representability test and its declared backend so four-way
tensor-parallel head ownership and context-parallel validation are exercised
while preserving the existing TP2/CP2 coverage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 19a56bfb-94c2-40c6-b17b-1422b2d0c29f

📥 Commits

Reviewing files that changed from the base of the PR and between 6d826df and 8a4f9eb.

📒 Files selected for processing (2)
  • docs/design/ws2-cp-attention-contract.md
  • tests/test_attention_contract.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/design/ws2-cp-attention-contract.md

Signed-off-by: inaniloquentee <3051000145@qq.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: 2

🤖 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 `@rl_engine/kernels/attention_contract.py`:
- Around line 606-618: The RoPE validation around position_ids and
query_position_offsets/key_position_offsets currently checks only lengths and
must enforce CP token ownership. Require position metadata for RoPE-aware
requests, validate dense position_ids against global_block_token_starts and
local_block_offsets, and reject offset-only metadata when ownership is
non-contiguous; add regression coverage for blocks with non-contiguous global
starts such as (0, 3072).
- Around line 495-500: Update theta validation in the attention contract around
self.theta to reject non-finite values after converting to float. Require
math.isfinite(theta) alongside the existing positive-value check before
object.__setattr__ stores the normalized value, while preserving the current
error behavior and message.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: b24f9885-0127-42d0-bd01-c402cfe94026

📥 Commits

Reviewing files that changed from the base of the PR and between 8a4f9eb and d39f0a5.

📒 Files selected for processing (3)
  • docs/design/ws2-cp-attention-contract.md
  • rl_engine/kernels/attention_contract.py
  • tests/test_attention_contract.py

Comment on lines +495 to +500
if isinstance(self.theta, bool) or not isinstance(self.theta, (float, int)):
raise AttentionContractError(f"theta must be a positive number; got {self.theta!r}")
theta = float(self.theta)
if theta <= 0.0:
raise AttentionContractError(f"theta must be a positive number; got {self.theta!r}")
object.__setattr__(self, "theta", theta)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject non-finite theta values.

float("nan") and float("inf") pass theta <= 0.0. They can produce invalid RoPE semantics and non-standard JSON values in provenance. Require math.isfinite(theta) before storing the value.

Proposed fix
-        if theta <= 0.0:
+        if not math.isfinite(theta) or theta <= 0.0:
             raise AttentionContractError(f"theta must be a positive number; got {self.theta!r}")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if isinstance(self.theta, bool) or not isinstance(self.theta, (float, int)):
raise AttentionContractError(f"theta must be a positive number; got {self.theta!r}")
theta = float(self.theta)
if theta <= 0.0:
raise AttentionContractError(f"theta must be a positive number; got {self.theta!r}")
object.__setattr__(self, "theta", theta)
if isinstance(self.theta, bool) or not isinstance(self.theta, (float, int)):
raise AttentionContractError(f"theta must be a positive number; got {self.theta!r}")
theta = float(self.theta)
if not math.isfinite(theta) or theta <= 0.0:
raise AttentionContractError(f"theta must be a positive number; got {self.theta!r}")
object.__setattr__(self, "theta", theta)
🤖 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 `@rl_engine/kernels/attention_contract.py` around lines 495 - 500, Update theta
validation in the attention contract around self.theta to reject non-finite
values after converting to float. Require math.isfinite(theta) alongside the
existing positive-value check before object.__setattr__ stores the normalized
value, while preserving the current error behavior and message.

Comment thread rl_engine/kernels/attention_contract.py Outdated
Comment on lines +606 to +618
if self.rope.position_ids is not None and len(self.rope.position_ids) not in {
query_sequence_length,
self.sharding.local_sequence_length,
}:
raise AttentionContractError(
"position_ids must describe the local query sequence or full local "
"sequence length"
)
for field in ("query_position_offsets", "key_position_offsets"):
offsets = getattr(self.rope, field)
if offsets is not None and len(offsets) != batch_size:
raise AttentionContractError(
f"{field} must contain one entry per logical batch entry"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Bind RoPE positions to CP token ownership.

The current checks only validate lengths. They accept absent position metadata and accept position_ids=range(local_sequence_length) for non-contiguous CP blocks. For example, blocks with global starts (0, 3072) would assign the second local block positions starting at 1024, not 3072. This changes the RoPE phase and can produce incorrect attention results.

Require position identity for RoPE-aware requests. Validate dense IDs against global_block_token_starts and local_block_offsets. Reject an offset-only representation when it cannot represent non-contiguous ownership. Add regression coverage for this case.

🤖 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 `@rl_engine/kernels/attention_contract.py` around lines 606 - 618, The RoPE
validation around position_ids and query_position_offsets/key_position_offsets
currently checks only lengths and must enforce CP token ownership. Require
position metadata for RoPE-aware requests, validate dense position_ids against
global_block_token_starts and local_block_offsets, and reject offset-only
metadata when ownership is non-contiguous; add regression coverage for blocks
with non-contiguous global starts such as (0, 3072).

@inaniloquentee
inaniloquentee changed the base branch from main to test August 12, 2026 16:13

@Flink-ddd Flink-ddd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@Flink-ddd
Flink-ddd merged commit 1f36aee into RL-Align:test Aug 24, 2026
3 checks passed
Flink-ddd pushed a commit that referenced this pull request Aug 24, 2026
…PR4)

Wire the CP attention path into the cross-configuration planner/runtime for the
Qwen3-8B TP=2 CP=2 BF16 target.

The PR4 criterion "rollout and training descriptors bind to the same semantic
attention contract" cannot hold literally: training runs full-sequence prefill
over a CP-sharded sequence while rollout runs vLLM paged-KV chunked prefill, so
the two AttentionContract instances always differ. Binding is therefore split
into three tiers -- identity must match bit for bit, reduction semantics must
match each other and the WS2 mandate, and materialization differences are
recorded and measured rather than rejected.

reduction.engine stays in the recorded tier so a Transformer Engine merge oracle
on one side does not fail the binding; reduction.order and acc_dtype stay in the
semantic tier because that is the WS2 claim.

Also adds the first two framework-shaped RuntimeMaterializer implementations.
Before this the only one was CpuSmokeMaterializer over a synthetic CPU model,
and every named scenario was planning-only. Neither adapter imports megatron or
vllm, so the binding rules run on CPU in CI.

Determinism is probed on both sides and compared, because the two frameworks
mean different things by it: Megatron asserts NCCL_ALGO and leaves TF32 and BF16
reduced-precision reduction unmanaged, while vLLM hard-sets ten NCCL variables
and disables both. Mismatches in NCCL_ALGO, NCCL_PROTO and CUBLAS_WORKSPACE_CONFIG
are blocking; the rest are recorded.

Fixes a latent break on the way: the planner normalizes dtype knobs to torch
spellings (bfloat16) while AttentionDType uses short ones (bf16), so passing a
normalized knob into the enum raised.

Stacked on #236 (attention contract) and #238 (deterministic CP reference), on
top of #230 (cross-configuration framework).

Part of #235

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q3Ar3z9fHEBFQQHddSEMaw
Flink-ddd pushed a commit that referenced this pull request Aug 24, 2026
Three fields could differ between the two sides without the binding noticing.

dtype was in no tier at all, so a BF16 rollout could bind to an FP16 training
pass and produce a drift number attributable to nothing. It joins the semantic
tier, with allow_dtype_difference for the #235 PR5 sweep that deliberately
scores BF16 against an FP32 reference.

batch_size was likewise unchecked. Batch invariance is a claim about results not
changing with batch makeup, so two sides scoring different batches are not
comparable and it belongs to identity.

split_kv_policy has no field in the #236 contract, so it only reached
side_configs and never took part in binding. Callers now pass it through
rollout_recorded_extra / training_recorded_extra so the difference is at least
visible in provenance; it can move into the contract once #236 grows the field.

Part of #235

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q3Ar3z9fHEBFQQHddSEMaw
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.

3 participants