Phase 70-C enables detection of merge relay patterns where multiple inner loops update the same owner-owned variable. This allows compile-time validation that relay sources can be merged at owner scope's exit PHI. Changes: - phase70c-merge-relay.md: New SSOT doc for merge relay semantics - normalized_joinir_min.rs: +2 integration tests Tests: normalized_dev 54/54 PASS, lib 950/950 PASS Design: Validator accepts multiple relays to same owner (individually valid) 🤖 Generated with Claude Code Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
5.7 KiB
Phase 70-C: Merge Relay (Multiple Inner Loops → Same Owner)
Status: ✅ Completed Date: 2025-12-13
Overview
Phase 70-C implements detection and validation support for "merge relay" patterns where multiple inner loops update the same owner-owned variable.
This builds upon Phase 70-B's simple passthrough multihop support by handling the case where a single owner scope must merge updates from multiple relay sources.
Merge Relay Pattern
Definition
Merge Relay: Multiple inner loops update the same ancestor-owned variable, requiring the owner to merge all relay updates at its exit PHI.
Example
loop L1 {
local total = 0 // owned by L1
loop L2_A {
total++ // L2_A → L1 relay
}
loop L2_B {
total += 10 // L2_B → L1 relay
}
}
// L1 exit: merge both L2_A and L2_B's updates to 'total'
OwnershipPlans
L2_A Plan:
OwnershipPlan {
scope_id: ScopeId(2),
relay_writes: [
RelayVar {
name: "total",
owner_scope: ScopeId(1), // L1
relay_path: [ScopeId(2)], // Single hop
}
],
owned_vars: [], // L2_A doesn't own total
}
L2_B Plan:
OwnershipPlan {
scope_id: ScopeId(3),
relay_writes: [
RelayVar {
name: "total",
owner_scope: ScopeId(1), // L1 (same owner)
relay_path: [ScopeId(3)], // Single hop
}
],
owned_vars: [], // L2_B doesn't own total
}
L1 Plan:
OwnershipPlan {
scope_id: ScopeId(1),
owned_vars: [
ScopeOwnedVar {
name: "total",
is_written: true, // Owner accepts relays
}
],
relay_writes: [], // No further relay (L1 is owner)
}
Design Decisions
1. PERMIT with Owner Merge
Decision: Merge relay is PERMITTED (not rejected) because:
- Multiple inner loops updating a shared variable is a legitimate pattern
- Owner scope can merge all updates at its exit PHI
- Each inner loop has a valid single-hop relay to the same owner
2. Validation Strategy
OwnershipPlanValidator accepts merge relay if:
- Each relay has valid
relay_path(non-empty, first hop is current scope) - No self-conflict (scope doesn't both own and relay the same variable)
- All relays point to the same owner scope (validated separately per relay)
Note: Validator validates individual plans - it doesn't check cross-plan consistency (e.g., "two different scopes relay to the same owner"). This is intentional - each relay is independently valid.
3. Runtime Support
Phase 70-C Status: Detection and validation only (dev-only)
Runtime Implementation (Phase 70-D+):
- Owner scope exit PHI must merge values from all relay sources
- Each relay source appears as a separate carrier in owner's loop_step
- PHI merges:
total_final = phi(total_init, total_from_L2_A, total_from_L2_B)
Implementation
Files Modified
- tests/normalized_joinir_min.rs
test_phase70c_merge_relay_multiple_inner_loops_detected(): AST → OwnershipPlan detectiontest_phase70c_merge_relay_same_owner_accepted(): Validator accepts merge relay
Test Coverage
Test 1: AST Detection
- Input: 3-level nested loop (L1 owns, L2_A and L2_B relay)
- Verification:
- 2 relay plans detected
- Both relay to the same owner (L1)
- Single-hop relay paths
- Owner plan marks variable as written
Test 2: Validator Acceptance
- Input: Two OwnershipPlans with single-hop relay to same owner
- Verification:
- Both plans pass
OwnershipPlanValidator::validate_relay_support() - No runtime_unsupported errors
- Both plans pass
Test Results
$ NYASH_JOINIR_NORMALIZED_DEV_RUN=1 cargo test --features normalized_dev --test normalized_joinir_min test_phase70c
running 2 tests
test test_phase70c_merge_relay_same_owner_accepted ... ok
test test_phase70c_merge_relay_multiple_inner_loops_detected ... ok
test result: ok. 2 passed; 0 failed; 0 ignored
Regression Check:
- normalized_dev: 54/54 PASS ✅
- lib tests: 950/950 PASS ✅
- Zero regressions
Acceptance Criteria
- Merge relay pattern fixture created (2 inner loops → 1 owner)
- AST analyzer correctly detects merge relay (2 relay plans)
- OwnershipPlanValidator accepts merge relay (no runtime_unsupported error)
- Tests pass with normalized_dev feature
- No regressions in lib tests
Future Work (Phase 70-D+)
Runtime Execution Support
Required for full merge relay execution:
-
Exit PHI Generation:
- Owner scope must generate PHI with N+1 inputs (init + each relay source)
- Example:
phi(total_init, total_from_L2_A, total_from_L2_B)
-
Carrier Propagation:
- Each relay source must appear in owner's loop_step signature
- Owner boundary must collect all relay values
-
Integration Testing:
- E2E test: 2 inner loops → owner → verify final merged value
- Combination: Multihop + Merge (3-layer nested with multiple relays)
-
Edge Cases:
- More than 2 relay sources
- If-branches with relay (conditional merge)
- Multihop + Merge (nested relay with multiple sources)
Related Documents
- Phase 65: Multihop Design
- Phase 70-A: Runtime Guard
- Phase 70-B: Simple Passthrough
- Phase 56: Ownership-Relay Architecture
Changelog
- 2025-12-13: Phase 70-C completed - Merge relay detection and validation
- 2 tests added (AST detection, validator acceptance)
- 54/54 normalized_dev tests pass
- 950/950 lib tests pass (zero regressions)