Skip to content

fix(workflow-executor): drop dead-branch steps from context (PRD-433)#1623

Draft
hercemer42 wants to merge 3 commits into
feat/prd-214-server-step-mapperfrom
fix/prd-433-revise-step-run-context
Draft

fix(workflow-executor): drop dead-branch steps from context (PRD-433)#1623
hercemer42 wants to merge 3 commits into
feat/prd-214-server-step-mapperfrom
fix/prd-433-revise-step-run-context

Conversation

@hercemer42
Copy link
Copy Markdown

@hercemer42 hercemer42 commented Jun 3, 2026

fixes PRD-433

Problem

Revising an earlier workflow step did not discard the steps that originally ran after it. The orchestrator marks them correctly (revised on the pivot card, cancelled on the downstream branch, live clones re-appended with originalStepIndex), but the executor read the run history branch-blind in two places:

  1. toPreviousSteps filtered only on done && stepIndex < current → dead-branch entries polluted the AI context for every step type.
  2. The record pool (getAvailableRecordRefs) was built from the raw local RunStore → records loaded by superseded steps were still offered as sources (revising "Load store" proposed loading from the dead branch's owner instead of reloading the store), while live clones (new stepIndex) couldn't be matched to their stored records at all.

Fix (executor-only — design agreed with Alban & Enki)

  • previousSteps now mirrors the orchestrator's own live-path filter: !revised && !cancelled.
  • Each previous step carries lineageStepIndexes (own index first, then earlier same-step generations, rebuilt by the mapper from the full history — the dead entries carry the intermediate generations). BaseStepExecutor.resolveLineageExecution walks them freshest-first; the record pool and the previous-steps AI summary both use it.
  • originalStepIndex declared in ServerStepHistory (already on the wire), lineageStepIndexes added to the strict Step schema as optional (additive, legacy producers stay valid).

Why lineage groups and not stepName: LinkTo loops are a product feature — the same step name can legitimately appear twice on the live path, so instance identity (stepIndex lineage) is the only safe key. Why latest-in-group and not root-match: a double revision with a re-execution in between leaves executions at several indexes of the same root; the root one is stale.

Tests

TDD — 17 new tests written red-first across mapper, base, and the four record executors:

  • cancelled downstream excluded from previousSteps and pool
  • revised anchor excluded while its live clone resolves to the original's stored record
  • entry-point revision (pool collapses to baseRecordRef)
  • double revision resolves the freshest generation, stale absent from the select-record enum
  • LinkTo loop: two live instances of the same stepName keep their own records
  • dead-branch fixtures no longer mappable don't fail the run (filter precedes mapping)

7 legacy executor tests updated to the new pool contract (records enter the pool only when claimed by a live previousStep). Full package suite: 858/858 green; changed files ≥90% covered on all metrics.

🤖 Generated with Claude Code

Note

Drop dead-branch steps from workflow executor context to fix revision-aware execution

  • Filters previousSteps to exclude revised and cancelled history entries, mirroring the server-side live-path logic in run-to-available-step-mapper.ts.
  • Adds lineageStepIndexes to each Step, built by tracing revision chains back to the original step index, so execution data lookups prefer the freshest generation of a step.
  • BaseStepExecutor.resolveLineageExecution walks lineageStepIndexes newest-first to find the best matching StepExecutionData for building previous-step summaries.
  • RecordStepExecutor.getAvailableRecordRefs now derives its candidate record pool from context.previousSteps (live path only) instead of scanning all run-store executions, excluding records produced by dead branches.
  • Behavioral Change: workflows that have been revised will no longer surface execution data or related records from cancelled/revised branches when processing subsequent steps.

Macroscope summarized 4c3cde3. (Automatic summaries will resume when PR exits draft mode or review begins).

@linear-code
Copy link
Copy Markdown

linear-code Bot commented Jun 3, 2026

PRD-433

@qltysh
Copy link
Copy Markdown

qltysh Bot commented Jun 3, 2026

1 new issue

Tool Category Rule Count
qlty Structure Function with many returns (count = 4): getAvailableRecordRefs 1

hercemer42 and others added 2 commits June 3, 2026 15:33
Revising a step forks the run history: the orchestrator stamps the pivot
card revised and the downstream branch cancelled, then re-appends live
clones. The executor read that history branch-blind, so re-executed
steps inherited stale AI context and the record pool offered records
loaded by superseded steps (e.g. revising "Load store" proposed loading
from the dead branch's owner).

previousSteps now mirrors the orchestrator's own live-path filter, and
each step carries lineageStepIndexes so executions persisted in the
RunStore under a clone's original index resolve to the freshest
generation — never to a dead branch, never by stepName (LinkTo loops
make names non-unique on the live path).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The base branch's load-related rework routes BelongsTo loads through
getSingleRelatedData instead of getRelatedData; the revision repro test
asserted the old port call. Also rewords a test comment that referenced
the ticket (review feedback).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@hercemer42 hercemer42 force-pushed the fix/prd-433-revise-step-run-context branch from 4c3cde3 to b608f35 Compare June 3, 2026 13:36
@hercemer42
Copy link
Copy Markdown
Author

[Claude Opus 4.8] — Review resolution on #1623

1. Inline conventions comment (test comment referencing the ticket) — agreed and fixed in b608f35.

2. qlty: "Function with many returns (count = 4): getAvailableRecordRefs" — pushing back, not fixing. The four returns are two guard-style early exits inside a flatMap callback plus the final pool return; the project's own code review principles explicitly prescribe this shape ("Return early — guard clauses at the top, avoid deep nesting"). Collapsing them into a single-exit ternary would also fight TypeScript's type narrowing on the discriminated executionResult union. 20-line function, single responsibility — restructuring would reduce readability to satisfy a counter, with no behavioral benefit. Flagging the conflict between the qlty many-returns rule and the project's return-early convention for a maintainer decision on the rule threshold.

3. CI failure (Linting & Testing workflow-executor) — root cause was not the review findings: the base branch moved under the PR (#1610 reworked the load-related executor; BelongsTo loads now go through getSingleRelatedData instead of getRelatedData). CI tests the merge result, so the revision repro test asserted a port call the merged flow no longer makes. Resolved by rebasing onto the current base and adapting the assertion to the sibling tests' shape (b608f35). Full suite on the rebased branch: 898/898 green.

@qltysh
Copy link
Copy Markdown

qltysh Bot commented Jun 3, 2026

Qlty


Coverage Impact

Unable to calculate total coverage change because base branch coverage was not found.

Modified Files with Diff Coverage (3)

RatingFile% DiffUncovered Line #s
New Coverage rating: A
packages/workflow-executor/src/executors/record-step-executor.ts100.0%
New Coverage rating: A
packages/workflow-executor/src/executors/base-step-executor.ts100.0%
New Coverage rating: A
...workflow-executor/src/adapters/run-to-available-step-mapper.ts100.0%
Total100.0%
🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

…ationale

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@hercemer42 hercemer42 force-pushed the fix/prd-433-revise-step-run-context branch from 86af236 to b8a0d5d Compare June 3, 2026 15:32
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