Skip to content

feat(LF-12): Pipeline DAG with StepId derivation + OrchestrationBridge adapter#300

Merged
AdaWorldAPI merged 2 commits into
mainfrom
claude/pr-s1-pipeline-dag
Apr 30, 2026
Merged

feat(LF-12): Pipeline DAG with StepId derivation + OrchestrationBridge adapter#300
AdaWorldAPI merged 2 commits into
mainfrom
claude/pr-s1-pipeline-dag

Conversation

@AdaWorldAPI
Copy link
Copy Markdown
Owner

Summary

  • PipelineDag (482 LOC): Kahn's algorithm topological executor over Vec<UnifiedStep> with depends_on DAG edges
  • StepId derived, not stored: UnifiedStep::id() computes FNV-1a over step_id bytes — eliminates the id: 0 landmine across all 4 callers (cypher_bridge, serve, lance_membrane, orchestration_impl)
  • OrchestrationBridge adapter: execute_via_bridge<B>(&self, bridge: &B) wires PipelineDag into the canonical contract pattern — F4/G2 won't fork the executor
  • Cycle detection: Kahn's algorithm catches multi-node cycles and self-loops
  • Error types: PipelineError::{MissingDependency, CycleDetected, StepFailed, DuplicateStepId}

Review notes

  • Keystone PR — unblocks Wave-2 PRs F4 (PostgREST dispatch) and G2 (Disambiguator wiring)
  • Synchronous-only executor; async fan-out is an explicit follow-up (documented in module doc)
  • type StepId = u64 alias kept in contract; depends_on: Vec<StepId> added to UnifiedStep

Test plan

  • 12 pipeline tests: empty DAG, single step, linear chain, diamond, 3-node cycle, 2-node cycle, self-loop, missing dep, duplicate StepId, step failure halt, wide fan-out, execute_via_bridge
  • test_distinct_step_ids_when_step_id_strings_differ — regression test (failing-first proven on old id: 0 code)
  • 307 contract tests pass, 182 planner tests pass, 23 callcenter pass, 46 cognitive-shader-driver pass

https://claude.ai/code/session_01NYGrxVopyszZYgLBxe4hgj


Generated by Claude Code

claude added 2 commits April 29, 2026 20:01
Add `StepId` type alias and `id`/`depends_on` fields to `UnifiedStep`
in the contract crate, enabling DAG-based step dependency tracking.
Implement topological executor in `lance-graph-planner::pipeline` using
Kahn's algorithm with cycle detection, missing-dep validation, and
execute_with callback. 10 tests cover empty DAG, single step, linear
chain, diamond, cycle detection (2-node and 3-node), missing dependency,
duplicate StepId, step failure halting, and wide fan-out.

Unblocks: PR-F4, PR-G2, PR-G6

https://claude.ai/code/session_01NYGrxVopyszZYgLBxe4hgj
…dmine

Closes the three loose ends called out by the S1 reviewer on the
PipelineDag PR:

1. (Tier 1) Remove the `pub id: StepId` field from `UnifiedStep`.
   Every caller was hard-coding `id: 0`, collapsing the DAG to a
   single colliding node. Replace with a derived `id()` method that
   FNV-1a-hashes `step_id`. Two distinct step_id strings now produce
   distinct numeric ids deterministically. All 4 callers
   (cypher_bridge.rs, serve.rs, lance_membrane.rs, orchestration_impl.rs)
   drop the `id: 0` line; `pipeline.rs` calls `step.id()` instead of
   `step.id`. The type alias `pub type StepId = u64` stays — pipeline
   keys its DAG and dependency edges off it.

2. (Tier 2) Add `PipelineDag::execute_via_bridge<B: OrchestrationBridge>`
   adapter so PR-F4 / PR-G2 cross-domain routing does not fork into a
   parallel closure-based executor. Tests cover end-to-end routing
   through a counting bridge.

3. (Tier 2) Add `cycle_detected_self_loop` test for the 1 -> 1 case;
   `CycleDetected.involved` now provably contains the self-looping
   step.

Critical: the regression test `test_distinct_step_ids_when_step_id_strings_differ`
was written FIRST and verified to fail under the old design (compile
error: `id` is a field, not a method) before the fix landed.

Tests: contract 307 pass (was 306); planner 182 pass (was 170, +12 in
pipeline module covering the three loose ends); callcenter 23 pass;
cognitive-shader-driver 46 pass (standalone build).

https://claude.ai/code/session_01NYGrxVopyszZYgLBxe4hgj
Copy link
Copy Markdown

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

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: 5e568c34e4

ℹ️ 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 +353 to +357
pub fn id(&self) -> StepId {
// FNV-1a over step_id bytes — deterministic, collision-resistant.
let mut h: u64 = 0xcbf29ce484222325;
for b in self.step_id.as_bytes() {
h ^= *b as u64;
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 Keep StepId stable after DAG construction

UnifiedStep::id() is recomputed from the mutable step_id string, but PipelineDag::build caches IDs once in index and later APIs (get, error reporting, execution_order) assume that identity is stable. Because execute_with passes &mut UnifiedStep to user/bridge callbacks, any callback that normalizes or rewrites step_id will desynchronize the cached index from the step’s current ID, causing lookups by current ID to fail and failures to be attributed to a different ID than the one validated in the DAG.

Useful? React with 👍 / 👎.

@AdaWorldAPI AdaWorldAPI merged commit b5cbd19 into main Apr 30, 2026
1 of 5 checks passed
AdaWorldAPI pushed a commit that referenced this pull request Apr 30, 2026
- Fix 4× `#[deprecated(since = "next")]` invalid semver in
  context_chain.rs — drop `since` field (G3 refactor artifact)
- Fix `actor.role <= u8::MAX` tautological comparison in
  lance_membrane.rs:768 — replace with meaningful `< 32` guard
- Document Wave-1 LOC audit in EPIPHANIES.md: recovery (#275-#283)
  = +8,728; Wave 1 (#300-#306) = +3,156; combined = +11,807;
  zero LOC lost from G1 rebase

cargo fmt --check: clean
cargo clippy (4 crates): warnings only, 0 errors

https://claude.ai/code/session_01NYGrxVopyszZYgLBxe4hgj
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.

2 participants