Skip to content

Native: remove additional redundant transition reads without stale wait ordering #1077

Open

Description

@rmcdaniel

Current owner and source work — November 9, 2026

General owns this P0 while the Server agent stays Off; all original WIP refs are preserved. The seven #1063/#1099 reductions and the later #1068/#1075 work are already released in Native 3.3.2. This issue owns the additional cuts, with no duplicate census or wait-ranking shortcut.

Draft PR #1115 now implements one independent cut: remove the successful activity completion's redundant row reread while preserving its full history snapshot. Head 4613b8adf31f67f593080c5988e81b41a6f7a993, base 5bde7ed3, tree a9c6abef; four files. One normal source train is running; it is the first compile/test execution. Controls compare the full snapshot to a stored-row read, retain prior-attempt/progress/legacy-nullable fields and stale repeats, check cross-Store heartbeat serialization and count the removed SELECT. No local build, runtime experiment, merge or speedup is claimed.

Next: inspect the complete original CI attempt, correct any source/test failures without cancelling live lanes, then qualify this cut through ordinary integration and a published patch. The remaining independent cuts stay open for separate source review and causal controls; the supplied cumulative 0005/0006 references are not applied wholesale. Preserve unconditional chronological wait ranking, backend errors, original IDs, history budgets and rollback state.

Original requirement and evidence

External report topic 23, accepted for P0 intake at the user's direction. The proposal, timings and supplied test results are unverified reference evidence. No patch was applied or executed during intake.

Implementation owner: general. The owning agent drives source, release and applicable published-artifact verification. Existing release, operating and credential gates remain in force.

Problem and proposed scope

The report supplies additional transition cuts: query-poll cache operations in the existing transaction; compact remembered workflow-lease rows with history reconstruction; start payload joined with claim/run data; unused activity wait-clock reads; folding already-delivered history into summary scans; removing activity read-back; reusing probed run status and authored counters; and omitting impossible prior-attempt expiry.

The projection census overlaps the existing P0 server#1063. Keep that item in its existing home and deduplicate any other overlapping cut there; this issue owns the additional source delta and its rollback/order controls.

Acceptance

  • Review and qualify each independent cut against maintained source. Stored rows, answers, fairness, history budgets, published millisecond ties and original IDs must remain equivalent.
  • Preserve backend errors, committed visibility, idempotency and all savepoint cache invalidation. Rebuilt poll history must remain bounded and correlated to the original lease/attempt.
  • Preserve unconditional chronological wait ranking when another projector's converging writes can move positions. The supplied 0005 stage is known wrong in this respect; retain 0006's correction and causal negatives, including current_wait_order_uses_published_millisecond_ties_and_original_ids and wait_positions_are_chronological_and_written_only_when_a_wait_moves.
  • Preserve the actual server cancellation-policy contract instead of weakening it to make the supplied snapshot test pass.
  • Relevant suites must pass on all three databases; retain genuine base/candidate failures and statement-count evidence. The earlier 14.51→12.61-second claim includes the wrong shortcut and is not a valid gain. The corrected result is within the reported session drift.

Reported workload and evidence limits

The report describes 200 workflows, six PHP worker processes, five sequential 20 ms activities or five local activities, last-close wall time, and three or four interleaved A/B rounds. MySQL session-to-session drift is reported as high as 11%. Stock database durability, network placement and the absence of deferred projections are reporter conditions, not independently verified facts. The headline run covers topics 1–24; topic 25 has only a MySQL five-steps A/B. No speedup, production readiness or suite pass is accepted from this intake.

Sanitized reference data

The supplied Server series targets reported base d4c6de8d; the SDK reference targets 96dc061. Maintained heads were independently read as Server c3bbfa5 and SDK c87eae37. These cumulative references are not current-main patches or maintained qualification.

  • 0005-Cut-more-statements-from-each-workflow-and-activity-.patch — 103,089 bytes / SHA-256 3095223e9455381ff492a08b7daf5651ce94aa138bff00cecaf3bc2bdfdc2306.
  • 0006-Rank-a-run-s-waits-on-every-transition-again-fix-the.patch — 8,143 bytes / SHA-256 4d1d1bc2e7e150495a6109f8c4ee0b2d13e65da67a7c8add9c0ef4c46f8fc090.

Bench-only Server0001 is retained as diagnostic context, not a separate production change. Complete sanitized diff references, shared bundle identity and source-review findings are linked in the intake index and the issue comments. Original mail headers, author/date metadata, application commands and runner instructions are omitted.

Activity

  1. added
    kind:featureA public product capability or experience is requested
    priority:P0Immediate release or data-integrity risk
    repo:serverOwned by the standalone server repository
    completion:evidence-requiredClose only after all explicit acceptance and operational evidence is public
    on Nov 6, 2026
  2. rmcdaniel commented on Nov 6, 2026

    @rmcdaniel
    MemberAuthor

    Sanitized external reference for topic 23: 0005-Cut-more-statements-from-each-workflow-and-activity-.patch, part 1/3. Complete sanitized diff: 103,089 bytes / SHA-256 3095223e9455381ff492a08b7daf5651ce94aa138bff00cecaf3bc2bdfdc2306. Ordered parts preserve the complete diff bytes; none was applied or executed. The patch is a cumulative older-source draft, not maintained implementation or qualification. Mail headers/author/date/message/application instructions were removed.

    The supplied 0005 stage includes the known stale wait-ranking shortcut. It is not an acceptable production candidate by itself; retain and independently qualify correction0006 and the negative controls.

    diff --git a/rust/src/runtime/active_summary.rs b/rust/src/runtime/active_summary.rs
    index 7aae3e44..180f34b0 100644
    --- a/rust/src/runtime/active_summary.rs
    +++ b/rust/src/runtime/active_summary.rs
    @@ -64,16 +64,17 @@ where
         for<'r> i64: Decode<'r, DB>,
         usize: ColumnIndex<DB::Row>,
     {
    -    pub(super) async fn projection_census(
    +    /// The run's row, with its census read in the same statement.
    +    pub(super) async fn census_run(
             tx: &mut Transaction<'_, DB>,
             run_id: &str,
    -    ) -> Result<ProjectionCensus> {
    +    ) -> Result<(DB::Row, ProjectionCensus)> {
             // One pass over the run's waits and one over its tasks; each count is
             // the rows of that pass its condition holds for.
    -        let row = Self::query("SELECT w.other_waits,t.repairs,t.failed_tasks,(SELECT COUNT(*) FROM workflow_signal_records WHERE workflow_run_id=$1 AND status='received') AS received_signals,w.external_waits,w.timers,w.activities,w.children,w.updates,(SELECT COUNT(*) FROM workflow_failures WHERE workflow_run_id=$2) AS failures FROM (SELECT COUNT(CASE WHEN status='open' AND kind NOT IN ('condition','signal','timer','activity','child','update') THEN 1 END) AS other_waits,COUNT(CASE WHEN (status='open' OR (status='resolved' AND source_status='timed_out')) AND kind IN ('condition','signal') THEN 1 END) AS external_waits,COUNT(CASE WHEN kind='timer' AND status='open' THEN 1 END) AS timers,COUNT(CASE WHEN kind='activity' AND status='open' THEN 1 END) AS activities,COUNT(CASE WHEN kind='child' THEN 1 END) AS children,COUNT(CASE WHEN kind='update' THEN 1 END) AS updates FROM workflow_run_waits WHERE workflow_run_id=$3) w CROSS JOIN (SELECT COUNT(CASE WHEN task_type='workflow' AND repair_count>0 THEN 1 END) AS repairs,COUNT(CASE WHEN task_type='workflow' AND status='failed' THEN 1 END) AS failed_tasks FROM workflow_tasks WHERE workflow_run_id=$4) t")
    -            .bind(run_id).bind(run_id).bind(run_id).bind(run_id)
    +        let row = Self::query("SELECT r.*,w.other_waits,t.repairs,t.failed_tasks,(SELECT COUNT(*) FROM workflow_signal_records WHERE workflow_run_id=$1 AND status='received') AS received_signals,w.external_waits,w.timers,w.activities,w.children,w.updates,(SELECT COUNT(*) FROM workflow_failures WHERE workflow_run_id=$2) AS failures FROM (SELECT COUNT(CASE WHEN status='open' AND kind NOT IN ('condition','signal','timer','activity','child','update') THEN 1 END) AS other_waits,COUNT(CASE WHEN (status='open' OR (status='resolved' AND source_status='timed_out')) AND kind IN ('condition','signal') THEN 1 END) AS external_waits,COUNT(CASE WHEN kind='timer' AND status='open' THEN 1 END) AS timers,COUNT(CASE WHEN kind='activity' AND status='open' THEN 1 END) AS activities,COUNT(CASE WHEN kind='child' THEN 1 END) AS children,COUNT(CASE WHEN kind='update' THEN 1 END) AS updates FROM workflow_run_waits WHERE workflow_run_id=$3) w CROSS JOIN (SELECT COUNT(CASE WHEN task_type='workflow' AND repair_count>0 THEN 1 END) AS repairs,COUNT(CASE WHEN task_type='workflow' AND status='failed' THEN 1 END) AS failed_tasks FROM workflow_tasks WHERE workflow_run_id=$4) t CROSS JOIN workflow_runs r WHERE r.id=$5")
    +            .bind(run_id).bind(run_id).bind(run_id).bind(run_id).bind(run_id)
                 .fetch_one(&mut **tx).await?;
    -        Ok(ProjectionCensus {
    +        let census = ProjectionCensus {
                 other_waits: DB::number(&row, "other_waits")?,
                 repairs: DB::number(&row, "repairs")?,
                 failed_tasks: DB::number(&row, "failed_tasks")?,
    @@ -84,7 +85,8 @@ where
                 children: DB::number(&row, "children")?,
                 updates: DB::number(&row, "updates")?,
                 failures: DB::number(&row, "failures")?,
    -        })
    +        };
    +        Ok((row, census))
         }
     
         pub(super) async fn active_task_summary(
    diff --git a/rust/src/runtime/activity_deadlines.rs b/rust/src/runtime/activity_deadlines.rs
    index 696c8921..4a46d81b 100644
    --- a/rust/src/runtime/activity_deadlines.rs
    +++ b/rust/src/runtime/activity_deadlines.rs
    @@ -102,21 +102,26 @@ pub(super) fn expired_kind<DB: Backend>(
     /// PHP's `deadlines` map on activity claims and status reads: each deadline
     /// the execution has, in UTC with microseconds, or null when it has none.
     pub(super) fn deadlines_view<DB: Backend>(activity: &DB::Row) -> Result<Value> {
    -    let mut deadlines = serde_json::Map::new();
    -    for (kind, field) in [
    -        ("schedule_to_start", "schedule_deadline_at"),
    -        ("start_to_close", "close_deadline_at"),
    -        ("schedule_to_close", "schedule_to_close_deadline_at"),
    -        ("heartbeat", "heartbeat_deadline_at"),
    -    ] {
    -        if let Some(at) = DB::optional_instant(activity, field)? {
    -            deadlines.insert(
    -                kind.into(),
    -                json!(at.to_rfc3339_opts(chrono::SecondsFormat::Micros, true)),
    -            );
    -        }
    -    }
    -    Ok(if deadlines.is_empty() { Value::Null } else { Value::Object(deadlines) })
    +    Ok(deadlines_of([
    +        DB::optional_instant(activity, "schedule_deadline_at")?,
    +        DB::optional_instant(activity, "close_deadline_at")?,
    +        DB::optional_instant(activity, "schedule_to_close_deadline_at")?,
    +        DB::optional_instant(activity, "heartbeat_deadline_at")?,
    +    ]))
    +}
    +
    +/// `deadlines_view` of an activity's schedule-to-start, start-to-close,
    +/// schedule-to-close and heartbeat deadlines.
    +pub(super) fn deadlines_of(deadlines: [Option<DateTime<Utc>>; 4]) -> Value {
    +    let deadlines: serde_json::Map<String, Value> =
    +        ["schedule_to_start", "start_to_close", "schedule_to_close", "heartbeat"]
    +            .into_iter()
    +            .zip(deadlines)
    +            .filter_map(|(kind, at)| {
    +                Some((kind.into(), json!(at?.to_rfc3339_opts(chrono::SecondsFormat::Micros, true))))
    +            })
    +            .collect();
    +    if deadlines.is_empty() { Value::Null } else { Value::Object(deadlines) }
     }
     
     /// PHP's message for an expired deadline.
    diff --git a/rust/src/runtime/backend.rs b/rust/src/runtime/backend.rs
    index 27550728..5d05d48d 100644
    --- a/rust/src/runtime/backend.rs
    +++ b/rust/src/runtime/backend.rs
    @@ -197,6 +197,9 @@ pub(super) trait Backend: Database {
         fn affected(result: Self::QueryResult) -> u64;
         fn instant(row: &Self::Row, field: &str) -> Result<DateTime<Utc>>;
         fn optional_instant(row: &Self::Row, field: &str) -> Result<Option<DateTime<Utc>>>;
    +    /// A document column's text as the database stores and sends it, the
    +    /// text `SUMMARY_HISTORY_SQL` casts it to.
    +    fn document_text(row: &Self::Row, field: &str) -> Result<String>;
         fn document_row(row: &Self::Row, field: &str) -> Result<Value>;
         fn optional_document_row(row: &Self::Row, field: &str) -> Result<Option<Value>>;
         fn begin(pool: &Pool<Self>) -> impl Future<Output = Result<Transaction<'static, Self>>> + Send;
    @@ -406,6 +409,9 @@ DELETE FROM dw_poll_receipts WHERE (namespace,worker_id,kind,request_id) IN (
                 .map(sqlite_instant)
                 .transpose()
         }
    +    fn document_text(row: &Self::Row, field: &str) -> Result<String> {
    +        Ok(row.try_get::<String, _>(field)?)
    +    }
         fn document_row(row: &Self::Row, field: &str) -> Result<Value> {
             Ok(serde_json::from_str(&row.try_get::<String, _>(field)?)?)
         }
    @@ -532,6 +538,9 @@ impl Backend for Postgres {
                 .try_get::<Option<NaiveDateTime>, _>(field)?
                 .map(|time| time.and_utc()))
         }
    +    fn document_text(row: &Self::Row, field: &str) -> Result<String> {
    +        Ok(row.try_get::<Json<Box<serde_json::value::RawValue>>, _>(field)?.0.get().to_owned())
    +    }
         fn document_row(row: &Self::Row, field: &str) -> Result<Value> {
             Ok(row.try_get::<Json<Value>, _>(field)?.0)
         }
    @@ -788,6 +797,9 @@ impl Backend for MySql {
         fn optional_instant(row: &Self::Row, field: &str) -> Result<Option<DateTime<Utc>>> {
             Ok(row.try_get(field)?)
         }
    +    fn document_text(row: &Self::Row, field: &str) -> Result<String> {
    +        Ok(row.try_get::<Json<Box<serde_json::value::RawValue>>, _>(field)?.0.get().to_owned())
    +    }
         fn document_row(row: &Self::Row, field: &str) -> Result<Value> {
             Ok(row.try_get::<Json<Value>, _>(field)?.0)
         }
    diff --git a/rust/src/runtime/cancellation_scopes.rs b/rust/src/runtime/cancellation_scopes.rs
    index 94e4009e..bc19b527 100644
    --- a/rust/src/runtime/cancellation_scopes.rs
    +++ b/rust/src/runtime/cancellation_scopes.rs
    @@ -2135,12 +2135,17 @@ store_methods! {
             -> Result<Option<&'static str>>
         {
             let events = Self::scope_events(tx, run_id).await?;
    -        let scopes = scopes(&events, run_id)?;
    -        let next = authored + 1;
    -        membership_refusal(&events, &scopes, commands, next)
    +        completion_refusal_in(&events, run_id, commands, authored)
         }
     }
     
    +/// `cancellation_scope_completion_refusal` over a history the transaction
    +/// has already read.
    +pub(super) fn completion_refusal_in(events: &[Event], run_id: &str, commands: &[&Value], authored: i64) -> Result<Option<&'static str>> {
    +    let scopes = scopes(events, run_id)?;
    +    membership_refusal(events, &scopes, commands, authored + 1)
    +}
    +
     /// `ActivityCancellationCompletion::resolved`: the started attempt closed
     /// on its own before any fence, the fence won before any attempt started,
     /// or the original owner acknowledged the stop.
    diff --git a/rust/src/runtime/definition_routing.rs b/rust/src/runtime/definition_routing.rs
    index 2406206e..24da0379 100644
    --- a/rust/src/runtime/definition_routing.rs
    +++ b/rust/src/runtime/definition_routing.rs
    @@ -21,6 +21,10 @@ use sqlx::{ColumnIndex, Decode, Encode, Executor, IntoArguments, Type};
     /// The worker capability that lets a changed definition replay a pinned run.
     pub(super) const ENTRY_VALIDATION_CAPABILITY: &str = "workflow_entry_arguments_validation";
     
    +/// A run's row with its start event's payload (`started_payload`, null when
    +/// it has none), for `definition_context` without another read.
    +pub(super) const RUN_WITH_START: &str = "SELECT r.*,(SELECT payload FROM workflow_history_events WHERE workflow_run_id=r.id AND event_type='WorkflowStarted' ORDER BY sequence LIMIT 1) AS started_payload FROM workflow_runs r WHERE r.id=$1";
    +
     /// PHP's `stringValue`: a trimmed, non-empty string.
     fn trimmed(value: Option<&str>) -> Option<String> {
         value.map(str::trim).filter(|value| !value.is_empty()).map(str::to_owned)
    @@ -52,19 +56,25 @@ where
         for<'r> i64: Decode<'r, DB>,
         usize: ColumnIndex<DB::Row>,
     {
    +    /// `context`, from the run's start payload when the caller read it with
    +    /// the run (`RUN_WITH_START`).
         pub(super) async fn definition_context(
             tx: &mut Transaction<'_, DB>,
             run: &DB::Row,
    +        started: Option<Value>,
             task_queue: Option<&str>,
             required_build_id: Option<String>,
         ) -> Result<DefinitionContext> {
    -        let started = Self::query("SELECT payload FROM workflow_history_events WHERE workflow_run_id=$1 AND event_type='WorkflowStarted' ORDER BY sequence LIMIT 1")
    -            .bind(DB::string(run, "id")?)
    -            .fetch_optional(&mut **tx)
    -            .await?
    -            .map(|row| DB::document_row(&row, "payload"))
    -            .transpose()?
    -            .unwrap_or(Value::Null);
    +        let started = match started {
    +            Some(started) => started,
    +            None => Self::query("SELECT payload FROM workflow_history_events WHERE workflow_run_id=$1 AND event_type='WorkflowStarted' ORDER BY sequence LIMIT 1")
    +                .bind(DB::string(run, "id")?)
    +                .fetch_optional(&mut **tx)
    +                .await?
    +                .map(|row| DB::document_row(&row, "payload"))
    +                .transpose()?
    +                .unwrap_or(Value::Null),
    +        };
             Ok(DefinitionContext {
                 worker_pinned: started["workflow_definition_fingerprint_source"] == "worker",
                 recorded: trimmed(started["workflow_definition_fingerprint"].as_str()),
    @@ -226,7 +236,7 @@ where
                 ),
                 None => (task_queue.map(str::to_owned), None),
             };
    -        let context = Self::definition_context(tx, run, queue.as_deref(), required).await?;
    +        let context = Self::definition_context(tx, run, None, queue.as_deref(), required).await?;
             let (mut matching, mut changed, mut missing, mut unsafe_replacement) = (0, 0, 0, 0);
             let mut examples = Vec::new();
             let peers = self.definition_peers(tx, run, &context).await?;
    diff --git a/rust/src/runtime/namespace_cleanup.rs b/rust/src/runtime/namespace_cleanup.rs
    index e5e9d3be..e82a7436 100644
    --- a/rust/src/runtime/namespace_cleanup.rs
    +++ b/rust/src/runtime/namespace_cleanup.rs
    @@ -272,7 +272,7 @@ store_methods! {
                 }
             }
             // The namespace's histories are gone; no cached read may survive.
    -        tx.scope_history = None;
    +        tx.forget_history();
             for sql in NATIVE_CLEANUP {
                 Self::query(sql).bind(&name).execute(&mut *tx).await?;
             }
    diff --git a/rust/src/runtime/operator_metrics.rs b/rust/src/runtime/operator_metrics.rs
    index 0529e229..aed66c71 100644
    --- a/rust/src/runtime/operator_metrics.rs
    +++ b/rust/src/runtime/operator_metrics.rs
    @@ -1432,7 +1432,7 @@ where
                 return Ok(Error(retention::PAYLOAD_CLEANUP_FAILED.into()));
             };
             // The run's history is gone; no cached read of it may survive.
    -        tx.scope_history = None;
    +        tx.forget_history();
             Self::query("UPDATE workflow_runs SET details_pruned_at=$1 WHERE id=$2 AND details_pruned_at IS NULL")
                 .bind(DB::bind_time(now()))
                 .bind(run_id)
    diff --git a/rust/src/runtime/scope_checkpoint.rs b/rust/src/runtime/scope_checkpoint.rs
    index 2d846445..60773ba7 100644
    --- a/rust/src/runtime/scope_checkpoint.rs
    +++ b/rust/src/runtime/scope_checkpoint.rs
    @@ -283,6 +283,7 @@ store_methods! {
                 return refusal("child_start_turn_not_at_checkpoint");
             }
             let start_turn = std::sync::Mutex::new(None);
    +        let open_activities = std::sync::Mutex::new(None);
             let apply = super::store::Apply {
                 run: &run,
                 task: &task,
    @@ -294,6 +295,7 @@ store_methods! {
                 local,
                 exceptions,
                 start_turn: &start_turn,
    +            open_activities: &open_activities,
             };
             let mut created = Vec::new();
             for (index, command) in commands.iter().enumerate() {
    diff --git a/rust/src/runtime/store.rs b/rust/src/runtime/store.rs
    index 64765366..6b4148de 100644
    --- a/rust/src/runtime/store.rs
    +++ b/rust/src/runtime/store.rs
    @@ -1,4 +1,5 @@
     //! One durable execution state machine for typed database adapters.
    +use super::terminal_summary::SummaryFold;
     use super::transaction::Transaction;
     use super::{Result, backend::Backend, envelope, refuse, side_effect_result, text, wire};
     use axum::http::StatusCode;
    @@ -1265,7 +1266,10 @@ where
     
         /// `StructuralLimits::guardPendingActivities`, `guardPendingChildren` and
         /// `guardPendingTimers` before the bridge writes one more (Workflow #933):
    -    /// a run already holding the limit refuses the whole completion.
    +    /// a run already holding the limit refuses the whole completion. A count
    +    /// the transaction already read, unchanged since, takes the statement's
    +    /// place.
    +    #[allow(clippy::too_many_arguments)]
         async fn guard_pending(
             tx: &mut Transaction<'_, DB>,
             run_id: &str,
    @@ -1273,6 +1277,7 @@ where
             limits: &super::memo_limits::StructuralLimits,
             task_id: &str,
             attempt: &Value,
    +        counted: Option<i64>,
         ) -> Result<()> {
             use super::memo_limits::Pending;
             let (limit, sql) = match kind {
    @@ -1286,7 +1291,10 @@ where
             if limit.is_none() {
                 return Ok(());
             }
    -        let count = Self::scalar(sql).bind(run_id).fetch_one(&mut **tx).await?;
    +        let count = match counted {
    +            Some(count) => count,
    +            None => Self::scalar(sql).bind(run_id).fetch_one(&mut **tx).await?,
    +        };
             match super::memo_limits::Exceeded::pending(kind, limit, count) {
                 Some(exceeded) => Err(exceeded.worker_refusal(task_id, attempt)),
                 None => Ok(()),
    @@ -1310,51 +1318,105 @@ where
         pub(super) async fn authored_sequence(
             tx: &mut Transaction<'_, DB>,
             run_id: &str,
    +    ) -> Result<i64> {
    +        let run = Self::authored_counters(tx, run_id, false).await?;
    +        let wait = Self::query("SELECT payload FROM workflow_history_events WHERE workflow_run_id=$1 AND event_type IN ('SignalWaitOpened','ConditionWaitOpened','VersionMarkerRecorded','MemoUpserted','SideEffectRecorded','SearchAttributesUpserted') ORDER BY sequence DESC LIMIT 1")
    +            .bind(run_id).fetch_optional(&mut **tx).await?
    +            .map(|row| DB::document_row(&row, "payload"))
    +            .transpose()?;
    +        let mut scopes = Vec::new();
    +        for row in Self::query("SELECT event_type,payload FROM workflow_history_events WHERE workflow_run_id=$1 AND event_type IN ('CancellationScopeOpened','CancellationScopeDelivered')")
    +            .bind(run_id).fetch_all(&mut **tx).await?
    +        {
    +            scopes.push((DB::string(&row, "event_type")?, DB::document_row(&row, "payload")?));
    +        }
    +        let started = if DB::number(&run, "run_number")? > 1 {
    +            Self::query("SELECT payload FROM workflow_history_events WHERE workflow_run_id=$1 AND event_type='WorkflowStarted' ORDER BY sequence LIMIT 1")
    +                .bind(run_id).fetch_optional(&mut **tx).await?
    +                .map(|row| DB::document_row(&row, "payload"))
    +                .transpose()?
    +        } else {
    +            None
    +        };
    +        Self::authored_from(&run, wait.as_ref(), scopes.iter().map(|(kind, payload)| (kind.as_str(), payload)), started.as_ref())
    +    }
    +
    +    /// `authored_sequence` for a transaction that has read the run's whole
    +    /// history as the scope engine reads it: the wait, scope and start
    +    /// events come from that read instead of three more statements. With
    +    /// `open_activities`, the same statement also counts the run's pending
    +    /// and running activities.
    +    pub(super) async fn authored_sequence_in(
    +        tx: &mut Transaction<'_, DB>,
    +        run_id: &str,
    +        events: &[super::cancellation_scopes::Event],
    +        open_activities: bool,
    +    ) -> Result<(i64, Option<i64>)> {
    +        let run = Self::authored_counters(tx, run_id, open_activities).await?;
    +        let open = if open_activities { Some(DB::number(&run, "open_activities")?) } else { None };
    +        let wait = events.iter().rev().find(|event| {
    +            matches!(
    +                event.kind.as_str(),
    +                "SignalWaitOpened" | "ConditionWaitOpened" | "VersionMarkerRecorded" | "MemoUpserted" | "SideEffectRecorded" | "SearchAttributesUpserted"
    +            )
    +        });
    +        let scopes = events
    +            .iter()
    +            .filter(|event| matches!(event.kind.as_str(), "CancellationScopeOpened" | "CancellationScopeDelivered"))
    +            .map(|event| (event.kind.as_str(), &*event.payload));
    +        let started = events.iter().find(|event| event.kind == "WorkflowStarted");
    +        Ok((Self::authored_from(&run, wait.map(|event| &*event.payload), scopes, started.map(|event| &*event.payload))?, open))
    +    }
    +
    +    /// The run's row and each authored table's largest sequence, in one
    +    /// statement; a table with no rows for the run counts as 0. With
    +    /// `open_activities`, also the count `guard_pending` reads for activities.
    +    async fn authored_counters(tx: &mut Transaction<'_, DB>, run_id: &str, open_activities: bool) -> Result<DB::Row> {
    +        if open_activities {
    +            return Ok(Self::query("SELECT r.cancellation_delivery_sequence,r.run_number,(SELECT sequence FROM activity_executions WHERE workflow_run_id=$1 ORDER BY sequence DESC LIMIT 1) AS activities,(SELECT sequence FROM workflow_run_timers WHERE workflow_run_id=$2 ORDER BY sequence DESC LIMIT 1) AS timers,(SELECT sequence FROM workflow_child_calls WHERE parent_workflow_run_id=$3 ORDER BY sequence DESC LIMIT 1) AS children,(SELECT COUNT(*) FROM activity_executions WHERE workflow_run_id=$4 AND status IN ('pending','running')) AS open_activities FROM workflow_runs r WHERE r.id=$5")
    +                .bind(run_id).bind(run_id).bind(run_id).bind(run_id).bind(run_id)
    +                .fetch_one(&mut **tx)
    +                .await?);
    +        }
    +        Ok(Self::query("SELECT r.cancellation_delivery_sequence,r.run_number,(SELECT sequence FROM activity_executions WHERE workflow_run_id=$1 ORDER BY sequence DESC LIMIT 1) AS activities,(SELECT sequence FROM workflow_run_timers WHERE workflow_run_id=$2 ORDER BY sequence DESC LIMIT 1) AS timers,(SELECT sequence FROM workflow_child_calls WHERE parent_workflow_run_id=$3 ORDER BY sequence DESC LIMIT 1) AS children FROM workflow_runs r WHERE r.id=$4")
    +            .bind(run_id).bind(run_id).bind(run_id).bind(run_id)
    +            .fetch_one(&mut **tx)
    +            .await?)
    +    }
    +
    +    /// The authored sequence from the run's counters, its latest wait or
    +    /// marker event, its scope events and its start event.
    +    fn authored_from<'a>(
    +        run: &DB::Row,
    +        wait: Option<&Value>,
    +        scopes: impl Iterator<Item = (&'a str, &'a Value)>,
    +        started: Option<&Value>,
         ) -> Result<i64> {
             // Public control command order and deterministic authored call order
             // are different counters in PHP. Signals must never shift a replayed
             // activity/timer/wait's authored position.
    -        // The run's row and each authored table's largest sequence, in one
    -        // statement; a table with no rows for the run counts as 0.
    -        let run = Self::query("SELECT r.cancellation_delivery_sequence,r.run_number,(SELECT sequence FROM activity_executions WHERE workflow_run_id=$1 ORDER BY sequence DESC LIMIT 1) AS activities,(SELECT sequence FROM workflow_run_timers WHERE workflow_run_id=$2 ORDER BY sequence DESC LIMIT 1) AS timers,(SELECT sequence FROM workflow_child_calls WHERE parent_workflow_run_id=$3 ORDER BY sequence DESC LIMIT 1) AS children FROM workflow_runs r WHERE r.id=$4")
    -            .bind(run_id).bind(run_id).bind(run_id).bind(run_id)
    -            .fetch_one(&mut **tx)
    -            .await?;
    -        let activities = DB::optional_number(&run, "activities")?.unwrap_or(0);
    -        let timers = DB::optional_number(&run, "timers")?.unwrap_or(0);
    -        let children = DB::optional_number(&run, "children")?.unwrap_or(0);
    -        let wait = Self::query("SELECT payload FROM workflow_history_events WHERE workflow_run_id=$1 AND event_type IN ('SignalWaitOpened','ConditionWaitOpened','VersionMarkerRecorded','MemoUpserted','SideEffectRecorded','SearchAttributesUpserted') ORDER BY sequence DESC LIMIT 1")
    -            .bind(run_id).fetch_optional(&mut **tx).await?;
    -        let wait_sequence = wait
    -            .map(|row| DB::document_row(&row, "payload"))
    -            .transpose()?
    -            .and_then(|value| value["sequence"].as_i64())
    -            .unwrap_or(0);
    +        let activities = DB::optional_number(run, "activities")?.unwrap_or(0);
    +        let timers = DB::optional_number(run, "timers")?.unwrap_or(0);
    +        let children = DB::optional_number(run, "children")?.unwrap_or(0);
    +        let wait_sequence = wait.and_then(|payload| payload["sequence"].as_i64()).unwrap_or(0);
             // `nextDurableCommandSequence`: a scope opening consumes its sequence,
             // and a delivered scope boundary its whole span, as a delivered run
             // request does.
             let mut scoped = 0;
    -        for row in Self::query("SELECT event_type,payload FROM workflow_history_events WHERE workflow_run_id=$1 AND event_type IN ('CancellationScopeOpened','CancellationScopeDelivered')")
    -            .bind(run_id).fetch_all(&mut **tx).await?
    -        {
    -            let payload = DB::document_row(&row, "payload")?;
    +        for (kind, payload) in scopes {
                 if let Some(sequence) = payload["sequence"].as_i64() {
    -                let span = payload["sequence_span"].as_i64().filter(|span| *span > 1 && DB::string(&row, "event_type").is_ok_and(|kind| kind == "CancellationScopeDelivered"));
    +                let span = payload["sequence_span"].as_i64().filter(|span| *span > 1 && kind == "CancellationScopeDelivered");
                     scoped = scoped.max(span.and_then(|span| sequence.checked_add(span - 1)).unwrap_or(sequence));
                 }
             }
             // A redriven run replays the activities before its resume step from
             // reused completions, which have no execution rows.
             let mut reused = 0;
    -        if DB::number(&run, "run_number")? > 1 {
    -            let started = Self::query("SELECT payload FROM workflow_history_events WHERE workflow_run_id=$1 AND event_type='WorkflowStarted' ORDER BY sequence LIMIT 1")
    -                .bind(run_id).fetch_optional(&mut **tx).await?;
    -            if let Some(started) = started {
    -                let payload = DB::document_row(&started, "payload")?;
    -                if payload["recovery_kind"] == "redrive" {
    -                    reused = payload["resume_step_sequence"].as_i64().unwrap_or(1) - 1;
    -                }
    -            }
    +        if DB::number(run, "run_number")? > 1
    +            && let Some(started) = started
    +            && started["recovery_kind"] == "redrive"
    +        {
    +            reused = started["resume_step_sequence"].as_i64().unwrap_or(1) - 1;
             }
             Ok(activities
                 .max(timers)
    @@ -1362,7 +1424,7 @@ where
                 .max(children)
                 .max(scoped)
                 .max(reused)
    -            .max(DB::optional_number(&run, "cancellation_delivery_sequence")?.unwrap_or(0)))
    +            .max(DB::optional_number(run, "cancellation_delivery_sequence")?.unwrap_or(0)))
         }
     
         /// `pendingSignalWaitIdForOpenSignalWait`: a new signal wait takes the
    @@ -1507,35 +1569,39 @@ where
     
         /// `PendingMessageTask::createForRun`: the run's next update or signal
         /// task, when it is open, has no open workflow task and holds a message
    -    /// that can advance it. The created task's id.
    +    /// that can advance it. The run's status as the probe read it, and the
    +    /// created task's id.
         pub(super) async fn next_message_task(
             tx: &mut Transaction<'_, DB>,
             run_id: &str,
             queue: &str,
             attempted: Option<&str>,
             include_received: bool,
    -    ) -> Result<Option<String>> {
    -        let status = Self::query("SELECT status FROM workflow_runs WHERE id=$1")
    -            .bind(run_id)
    +    ) -> Result<(String, Option<String>)> {
    +        // The run's status, its open workflow tasks, and whether it holds an
    +        // accepted update or a received signal, in one statement. Most runs
    +        // have neither message, and then nothing more is read.
    +        let probe = Self::query("SELECT r.status,(SELECT COUNT(*) FROM workflow_tasks WHERE workflow_run_id=$1 AND task_type='workflow' AND status IN ('ready','leased')) AS open_tasks,(SELECT COUNT(*) FROM (SELECT 1 FROM workflow_updates WHERE workflow_run_id=$2 AND status='accepted' LIMIT 1) AS accepted) AS updates,(SELECT COUNT(*) FROM (SELECT 1 FROM workflow_signal_records WHERE workflow_run_id=$3 AND status='received' AND closed_at IS NULL LIMIT 1) AS received) AS signals FROM workflow_runs r WHERE r.id=$4")
    +            .bind(run_id).bind(run_id).bind(run_id).bind(run_id)
                 .fetch_one(&mut **tx)
                 .await?;
    -        if !matches!(
    -            DB::string(&status, "status")?.as_str(),
    -            "pending" | "running" | "waiting"
    -        ) {
    -            return Ok(None);
    +        let status = DB::string(&probe, "status")?;
    +        if !matches!(status.as_str(), "pending" | "running" | "waiting") {
    +            return Ok((status, None));
             }
    -        let open = Self::scalar("SELECT COUNT(*) FROM workflow_tasks WHERE workflow_run_id=$1 AND task_type='workflow' AND status IN ('ready','leased')")
    -            .bind(run_id).fetch_one(&mut **tx).await?;
    -        if open != 0 {
    -            return Ok(None);
    +        if DB::number(&probe, "open_tasks")? != 0 {
    +            return Ok((status, None));
             }
             // `nextReadyUpdate`: the earliest accepted update, unless an earlier
             // signal is still unapplied. That signal then goes first.
    -        let update = Self::query("SELECT id,workflow_command_id,command_sequence FROM workflow_updates WHERE workflow_run_id=$1 AND status='accepted' ORDER BY CASE WHEN command_sequence IS NULL THEN 1 ELSE 0 END,command_sequence,accepted_at,created_at,id LIMIT 1")
    -            .bind(run_id)
    -            .fetch_optional(&mut **tx)
    -            .await?;
    +        let update = if DB::number(&probe, "updates")? > 0 {
    +            Self::query("SELECT id,workflow_command_id,command_sequence FROM workflow_updates WHERE workflow_run_id=$1 AND status='accepted' ORDER BY CASE WHEN command_sequence IS NULL THEN 1 ELSE 0 END,command_sequence,accepted_at,created_at,id LIMIT 1")
    +                .bind(run_id)
    +                .fetch_optional(&mut **tx)
    +                .await?
    +        } else {
    +            None
    +        };
             if let Some(update) = update
                 && Self::blocking_signal(
                     tx,
    @@ -1554,10 +1620,15 @@ where
                     &DB::string(&update, "workflow_command_id")?,
                 )
                 .await?;
    -            return Ok(Some(task_id));
    +            return Ok((status, Some(task_id)));
    +        }
    +        // Nothing has changed since the probe: with no received signal there
    +        // is none to deliver.
    +        if DB::number(&probe, "signals")? == 0 {
    +            return Ok((status, None));
             }
             let Some(signal) = Self::next_eligible_signal(tx, run_id, attempted, include_received).await? else {
    -            return Ok(None);
    +            return Ok((status, None));
             };
             {
                 let name = DB::string(&signal, "signal_name")?;
    @@ -1574,7 +1645,7 @@ where
                     payload["signal_wait_id"] = json!(signal_wait_id);
                 }
                 let task_id = Self::create_task(tx, run_id, queue, "workflow", payload).await?;
    -            Ok(Some(task_id))
    +            Ok((status, Some(task_id)))
             }
         }
     
    @@ -2451,7 +2522,9 @@ where
                         continue;
                     }
                 }
    -            let run = Self::query("SELECT * FROM workflow_runs WHERE id=$1")
    +            // A workflow task's run comes with its start, which names the
    +            // definition the run may be pinned to.
    +            let run = Self::query(if kind == "workflow" { super::definition_routing::RUN_WITH_START } else { "SELECT * FROM workflow_runs WHERE id=$1" })
                     .bind(&run_id)
                     .fetch_one(&mut **tx)
                     .await?;
    @@ -2476,7 +2549,8 @@ where
                     let required = DB::optional_string(&task, "compatibility")?
                         .map(|build| build.trim().to_owned())
                         .filter(|build| !build.is_empty());
    -                let context = Self::definition_context(tx, &run, Some(queue), required).await?;
    +                let started = DB::optional_document_row(&run, "started_payload")?.unwrap_or(Value::Null);
    +                let context = Self::definition_context(tx, &run, Some(started), Some(queue), required).await?;
                     if !self.definition_allows(tx, &run, &worker, &context).await?.0 {
                         continue;
                     }
    @@ -2617,28 +2691,38 @@ where
                     "workflow_type": DB::string(&run,"workflow_type")?, "namespace": self.namespace.as_ref(), "payload_codec": "avro",
                     "queue": queue, "task_queue": queue, "lease_owner": worker_id, "lease_expires_at": super::cooperative::timestamp(expires)});
                 if let Some(activity) = activity {
    -                let activity_id = DB::string(&activity, "id")?;
    -                let policy =
    -                    DB::optional_document_row(&activity, "retry_policy")?.unwrap_or(Value::Null);
    +                let mut activity = ActivityRecord::read::<DB>(&activity)?;
    +                let activity_id = activity.id.clone();
    +                let policy = activity.retry_policy.clone().unwrap_or(Value::Null);
                     super::activity_deadlines::validate_policy(&policy)?;
                     let attempt_id = id();
                     let started_at = now();
    -                Self::query("UPDATE activity_attempts SET status='expired',lease_expires_at=NULL,closed_at=$1 WHERE activity_execution_id=$2 AND status='running'")
    -                    .bind(DB::bind_time(started_at)).bind(&activity_id).execute(&mut **tx).await?;
    +                let close_deadline = super::activity_deadlines::deadline(&policy, "start_to_close_timeout", started_at)?;
    +                let heartbeat_deadline = super::activity_deadlines::deadline(&policy, "heartbeat_timeout", started_at)?;
    +                // An activity this transaction scheduled has no attempt yet.
    +                if !tx.unattempted_activities.remove(&activity_id) {
    +                    Self::query("UPDATE activity_attempts SET status='expired',lease_expires_at=NULL,closed_at=$1 WHERE activity_execution_id=$2 AND status='running'")
    +                        .bind(DB::bind_time(started_at)).bind(&activity_id).execute(&mut **tx).await?;
    +                }
                     Self::query("INSERT INTO activity_attempts(id,activity_execution_id,workflow_run_id,workflow_task_id,attempt_number,status,lease_owner,lease_expires_at,started_at) VALUES ($1,$2,$3,$4,$5,'running',$6,$7,$8)")
                         .bind(&attempt_id).bind(&activity_id).bind(&run_id).bind(&task_id).bind(attempt)
                         .bind(worker_id).bind(DB::bind_time(expires)).bind(DB::bind_time(started_at)).execute(&mut **tx).await?;
                     Self::query("UPDATE activity_executions SET status='running',attempt_count=$1,current_attempt_id=$2,started_at=$3,last_heartbeat_at=NULL,close_deadline_at=$4,heartbeat_deadline_at=$5 WHERE id=$6")
                         .bind(attempt).bind(&attempt_id).bind(DB::bind_time(started_at))
    -                    .bind(DB::optional_time(super::activity_deadlines::deadline(&policy, "start_to_close_timeout", started_at)?))
    -                    .bind(DB::optional_time(super::activity_deadlines::deadline(&policy, "heartbeat_timeout", started_at)?)).bind(&activity_id)
    +                    .bind(DB::optional_time(close_deadline))
    +                    .bind(DB::optional_time(heartbeat_deadline)).bind(&activity_id)
                         .execute(&mut **tx)
                         .await?;
    -                let activity = Self::query("SELECT * FROM activity_executions WHERE id=$1")
    -                    .bind(&activity_id)
    -                    .fetch_one(&mut **tx)
    -                    .await?;
    -                let mut payload = Self::activity_payload(&activity)?;
    +                // The row as the UPDATE leaves it: the times are whole
    +                // milliseconds, which every backend stores exactly.
    +                activity.status = "running".into();
    +                activity.attempt_count = attempt;
    +                activity.current_attempt_id = Some(attempt_id.clone());
    +                activity.started_at = Some(started_at);
    +                activity.last_heartbeat_at = None;
    +                activity.deadlines[1] = close_deadline;
    +                activity.deadlines[3] = heartbeat_deadline;
    +                let mut payload = activity.payload()?;
                     payload["activity_attempt_id"] = json!(attempt_id);
                     payload["attempt_number"] = json!(attempt);
                     payload["task"] = json!({"id":task_id,"type":"activity","status":"leased",
    @@ -2665,13 +2749,12 @@ where
                         claim["worker_session"] = view;
                     }
                     // A standalone activity started without input has none.
    -                if let Some(arguments) = DB::optional_string(&activity, "arguments")? {
    -                    claim["arguments"] = wire(&arguments);
    +                if let Some(arguments) = &activity.arguments {
    +                    claim["arguments"] = wire(arguments);
                     }
    -                claim["retry_policy"] =
    -                    DB::optional_document_row(&activity, "retry_policy")?.unwrap_or(Value::Null);
    +                claim["retry_policy"] = policy;
                     // PHP omits the field when the activity has no deadline.
    -                let deadlines = super::activity_deadlines::deadlines_view::<DB>(&activity)?;
    +                let deadlines = super::activity_deadlines::deadlines_of(activity.deadlines);
                     if !deadlines.is_null() {
                         claim["deadlines"] = deadlines;
                     }
    @@ -2740,10 +2823,14 @@ where
                     // The run was read in this transaction, which holds the
                     // transition lock, and nothing since has written its history.
                     let last_sequence = DB::number(&run, "last_history_sequence")?;
    +                // The rows the task delivers are the ones the run's summary
    +                // scan reads, so a whole history delivered is that scan.
    +                let mut fold = SummaryFold::default();
                     let (mut events, next) = match page {
    -                    Some(size) => Self::history_page_known(&mut **tx, &run_id, 0, size, Some(last_sequence)).await?,
    -                    None => (Self::whole_history(&mut **tx, &run_id, last_sequence).await?, None),
    +                    Some(size) => Self::history_page_known(&mut **tx, &run_id, 0, size, Some(last_sequence), Some(&mut fold)).await?,
    +                    None => (Self::whole_history(&mut **tx, &run_id, last_sequence, Some(&mut fold)).await?, None),
                     };
    +                fold.keep(tx, &run_id, last_sequence);
                     Self::with_signal_sequences(&mut **tx, &run_id, &mut events).await?;
                     let budget = Self::workflow_history_budget(tx, &run_id, last_sequence).await?;
                     budget.write_task(&mut claim);
    @@ -3373,8 +3460,16 @@ where
             // prepared scope admits no new member, and an undelivered preparation
             // reserves its boundary for every command.
             let non_terminal: Vec<&Value> = commands.iter().filter(|command| !is_terminal(command)).collect();
    -        let mut sequence = Self::authored_sequence(&mut tx, &run_id).await?;
    -        if let Some(reason) = Self::cancellation_scope_completion_refusal(&mut tx, &run_id, &non_terminal, sequence).await? {
    +        let events = Self::scope_events(&mut tx, &run_id).await?;
    +        // The guard of a first command that schedules an activity reads its
    +        // count with the run's counters.
    +        let counts_open = limits.pending_activities.is_some()
    +            && commands.iter().find(|command| !is_terminal(command)).is_some_and(|command| command["type"] == "schedule_activity");
    +        let (mut sequence, open_activities) = Self::authored_sequence_in(&mut tx, &run_id, &events, counts_open).await?;
    +        let refusal = super::cancellation_scopes::completion_refusal_in(&events, &run_id, &non_terminal, sequence)?;
    +        // The transaction's history cache then grows in place, unshared.
    +        drop(events);
    +        if let Some(reason) = refusal {
                 return Err(super::RuntimeError::Protocol {
                     status: StatusCode::CONFLICT,
                     response: super::cancellation_scopes::enveloped(json!({"task_id": task_id,
    @@ -3389,6 +3484,7 @@ where
             // Published parseCommands applies every other command first, whatever
             // the terminal command's position in the batch.
             let start_turn = std::sync::Mutex::new(None);
    +        let open_activities = std::sync::Mutex::new(open_activities);
             let apply = Apply {
                 run: &run,
                 task: &task,
    @@ -3400,6 +3496,7 @@ where
                 local: &local,
                 exceptions: &exceptions,
                 start_turn: &start_turn,
    +            open_activities: &open_activities,
             };
             let order = (0..commands.len())
                 .filter(|index| !is_terminal(&commands[*index]))
    @@ -3502,24 +3599,31 @@ where
             // `createForRun($run, workflowSignalIdForTask($task), false)`.
             let attempted = task_payload["workflow_signal_id"].as_str().or_else(|| task_payload["resume_source_id"].as_str())
                 .filter(|signal| !signal.is_empty());
    -        if let Some(next) = Self::next_message_task(&mut tx, &run_id, &DB::string(&run, "queue")?, attempted, false).await? {
    +        let (probed, next) = Self::next_message_task(&mut tx, &run_id, &DB::string(&run, "queue")?, attempted, false).await?;
    +        // The probe read the run as the commands left it; only a task created
    +        // since can have changed its status.
    +        let mut current = Some(probed).filter(|_| next.is_none());
    +        if let Some(next) = next {
                 created.push(next);
             }
             let start_turn = start_turn.into_inner().unwrap_or_else(std::sync::PoisonError::into_inner);
    -        if let Some(turn) = start_turn
    -            && let Some(next) = Self::child_start_turn(&mut tx, &run_id, task_id, &DB::string(&run, "queue")?, turn).await?
    -        {
    -            created.push(next);
    +        if let Some(turn) = start_turn {
    +            current = None;
    +            if let Some(next) = Self::child_start_turn(&mut tx, &run_id, task_id, &DB::string(&run, "queue")?, turn).await? {
    +                created.push(next);
    +            }
             }
             Self::record_completion(&mut tx, task_id, &json!({COMPLETION_RECEIPT: {"request": body, "created_task_ids": created}})).await?;
             // PHP's executor leaves every run a completed task did not close
             // waiting, even when the task recorded only markers or side effects.
             // A run already waiting or closed is read and left as it is.
    -        let current = Self::query("SELECT status FROM workflow_runs WHERE id=$1")
    -            .bind(&run_id)
    -            .fetch_one(&mut *tx)
    -            .await?;
    -        let mut run_status = DB::string(&current, "status")?;
    +        let mut run_status = match current {
    +            Some(status) => status,
    +            None => DB::string(
    +                &Self::query("SELECT status FROM workflow_runs WHERE id=$1").bind(&run_id).fetch_one(&mut *tx).await?,
    +                "status",
    +            )?,
    +        };
             if matches!(run_status.as_str(), "pending" | "running") {
                 let waiting = Self::query("UPDATE workflow_runs SET status='waiting' WHERE id=$1 AND status IN ('pending','running')")
                     .bind(&run_id)
    @@ -3535,7 +3639,11 @@ where
                     DB::string(&current, "status")?
                 };
             }
    -        if let Some(worker) = &worker {
    +        // This task's completion or failure event is recorded only with the
    +        // run's closing status.
    +        if let Some(worker) = &worker
    +            && matches!(run_status.as_str(), "completed" | "failed")
    +        {
                 Self::attribute_worker_terminal(&mut tx, &run_id, task_id, worker).await?;
             }
             // `MessageStreamService::recordCompletion`: only a continued run
    @@ -3606,6 +3714,8 @@ where
                 Err(_) => {
                     sqlx::raw_sql("ROLLBACK TO SAVEPOINT activity_poll").execute(&mut **tx).await?;
                     tx.run_tags.clear();
    +                // The claim may have appended history the rollback took back.
    +                tx.forget_history();
                     Ok(None)
                 }
             }
    @@ -3644,7 +3754,8 @@ where
             sequence_ref: &mut i64,
             created: &mut Vec<String>,
         ) -> Result<()> {
    -        let Apply { run, task, task_id, run_id, attempt, limits, external, local, exceptions, start_turn } = *apply;
    +        let Apply { run, task, task_id, run_id, attempt, limits, external, local, exceptions, start_turn, open_activities } = *apply;
    +        let open_activities = open_activities.lock().unwrap_or_else(std::sync::PoisonError::into_inner).take();
             let mut sequence = *sequence_ref;
             if matches!(
                 command["type"].as_str(),
    @@ -3657,7 +3768,7 @@ where
                 sequence += 1;
                 Self::apply_update(tx, &run, &task, command, sequence).await?;
             } else if command["type"] == "start_child_workflow" {
    -            Self::guard_pending(tx, &run_id, super::memo_limits::Pending::Children, &limits, task_id, attempt).await?;
    +            Self::guard_pending(tx, &run_id, super::memo_limits::Pending::Children, &limits, task_id, attempt, None).await?;
                 sequence += 1;
                 let started = Self::start_child(tx, &run, task_id, command, sequence).await?;
                 created.push(started.task);
    @@ -3699,7 +3810,7 @@ where
                 self.upsert_search_attributes(tx, &run, &task, command, index, sequence)
                     .await?;
             } else if command["type"] == "schedule_activity" {
    -            Self::guard_pending(tx, &run_id, super::memo_limits::Pending::Activities, &limits, task_id, attempt).await?;
    +            Self::guard_pending(tx, &run_id, super::memo_limits::Pending::Activities, &limits, task_id, attempt, open_activities).await?;
                 sequence += 1;
                 let metadata = super::selections::metadata(command, "activity")?;
                 super::selections::check_sequence(&metadata, sequence)?;
    @@ -3754,11 +3865,30 @@ where
                     .bind(DB::optional_document(&metadata["parallel_group_path"]))
                     .bind(DB::optional_time(schedule_deadline)).bind(DB::optional_time(lifecycle_deadline))
                     .bind(connection.as_deref()).bind(activity_options.as_ref().map(DB::document)).execute(&mut **tx).await?;
    -            let activity = Self::query("SELECT * FROM activity_executions WHERE id=$1")
    -                .bind(&activity_id)
    -                .fetch_one(&mut **tx)
    -                .await?;
    -            let mut payload = Self::activity_payload(&activity)?;
    +            tx.unattempted_activities.insert(activity_id.clone());
    +            // The row as the INSERT writes it. Its documents read back as
    +            // the values bound, MySQL's reordered keys aside, which the
    +            // history's own MySQL column reorders again.
    +            let activity = ActivityRecord {
    +                id: activity_id.clone(),
    +                activity_type: activity_type.to_owned(),
    +                activity_class: activity_type.to_owned(),
    +                sequence,
    +                arguments: Some(arguments),
    +                retry_policy: policy,
    +                status: "pending".into(),
    +                attempt_count: 0,
    +                closed_at: None,
    +                current_attempt_id: None,
    +                exception: None,
    +                queue: queue.clone(),
    +                parallel_group_path: Some(metadata["parallel_group_path"].clone()).filter(|path| !path.is_null()),
    +                activity_options,
    +                started_at: None,
    +                last_heartbeat_at: None,
    +                deadlines: [schedule_deadline, None, lifecycle_deadline, None],
    +            };
    +            let mut payload = activity.payload()?;
                 payload["task"] = Self::task_snapshot(&task)?;
                 let recorded_at = now();
                 Self::append_at(
    @@ -3783,16 +3913,19 @@ where
                 )
                 .await?;
                 created.push(activity_task.clone());
    -            Self::create_activity_wait(
    +            Self::open_activity_wait(
                     tx,
    +                &DB::string(&run, "workflow_instance_id")?,
                     &run_id,
    -                &activity_id,
    +                &activity.id,
    +                activity.sequence,
    +                &activity.activity_type,
                     &activity_task,
  3. rmcdaniel commented on Nov 6, 2026

    @rmcdaniel
    MemberAuthor

    Sanitized external reference for topic 23: 0005-Cut-more-statements-from-each-workflow-and-activity-.patch, part 2/3. Complete sanitized diff: 103,089 bytes / SHA-256 3095223e9455381ff492a08b7daf5651ce94aa138bff00cecaf3bc2bdfdc2306. Ordered parts preserve the complete diff bytes; none was applied or executed. The patch is a cumulative older-source draft, not maintained implementation or qualification. Mail headers/author/date/message/application instructions were removed.

    The supplied 0005 stage includes the known stale wait-ranking shortcut. It is not an acceptable production candidate by itself; retain and independently qualify correction0006 and the negative controls.

                     recorded_at,
                 )
                 .await?;
             } else if command["type"] == "start_timer" {
    -            Self::guard_pending(tx, &run_id, super::memo_limits::Pending::Timers, &limits, task_id, attempt).await?;
    +            Self::guard_pending(tx, &run_id, super::memo_limits::Pending::Timers, &limits, task_id, attempt, None).await?;
                 sequence += 1;
                 let metadata = super::selections::metadata(command, "timer")?;
                 super::selections::check_sequence(&metadata, sequence)?;
    @@ -4134,17 +4267,17 @@ where
                 self.claim_external_in(&mut tx, &prepared.reference, &prepared.uri)
                     .await?;
             }
    +        let closed_at = now();
             Self::query("UPDATE activity_executions SET status='completed',result=$1,exception=NULL,closed_at=$2 WHERE id=$3")
    -            .bind(&result).bind(DB::bind_time(now())).bind(&activity_id).execute(&mut *tx).await?;
    -        let activity = Self::query("SELECT * FROM activity_executions WHERE id=$1")
    -            .bind(&activity_id)
    -            .fetch_one(&mut *tx)
    -            .await?;
    -        let mut payload = Self::activity_payload(&activity)?;
    -        let metadata = super::selections::activity_metadata(DB::optional_document_row(
    -            &activity,
    -            "parallel_group_path",
    -        )?)?;
    +            .bind(&result).bind(DB::bind_time(closed_at)).bind(&activity_id).execute(&mut *tx).await?;
    +        // The row as the UPDATE leaves it: its time is whole milliseconds,
    +        // which every backend stores exactly.
    +        let mut activity = ActivityRecord::read::<DB>(&activity)?;
    +        activity.status = "completed".into();
    +        activity.exception = None;
    +        activity.closed_at = Some(closed_at);
    +        let mut payload = activity.payload()?;
    +        let metadata = super::selections::activity_metadata(activity.parallel_group_path.clone())?;
             payload["activity_attempt_id"] = json!(attempt_id);
             payload["attempt_number"] = json!(DB::number(&attempt, "attempt_number")?);
             payload["result"] = wire(&result);
    @@ -4243,6 +4376,8 @@ where
                 Err(_) => {
                     sqlx::raw_sql("ROLLBACK TO SAVEPOINT workflow_poll").execute(&mut **tx).await?;
                     tx.run_tags.clear();
    +                // The claim may have appended history the rollback took back.
    +                tx.forget_history();
                     Ok(None)
                 }
             }
    @@ -4972,7 +5107,7 @@ where
             // charged before the update (Workflow #930).
             tx.charge_bytes(super::history_meter::php_json_len(&payload).saturating_sub(recorded))?;
             // The only in-place history change: a cached read would miss it.
    -        tx.scope_history = None;
    +        tx.forget_history();
             Self::query("UPDATE workflow_history_events SET payload=$1 WHERE id=$2")
                 .bind(DB::document(&payload))
                 .bind(DB::string(&event, "id")?)
    @@ -5029,64 +5164,18 @@ where
         }
     
         pub(super) fn activity_payload(activity: &DB::Row) -> Result<Value> {
    -        let mut payload = json!({"activity_execution_id": DB::string(activity,"id")?, "activity_type": DB::string(activity,"activity_type")?,
    -        "activity_class": DB::string(activity,"activity_class")?, "sequence": DB::number(activity,"sequence")?,
    -        "activity": {"id":DB::string(activity,"id")?,"idempotency_key":DB::string(activity,"id")?,
    -            "sequence":DB::number(activity,"sequence")?,"type":DB::string(activity,"activity_type")?,
    -            "class":DB::string(activity,"activity_class")?,"local_activity":false,
    -            "activity_type": DB::string(activity,"activity_type")?, "arguments": DB::optional_string(activity,"arguments")?.map(|arguments| wire(&arguments)),
    -            "retry_policy":DB::optional_document_row(activity,"retry_policy")?,
    -            "status":DB::string(activity,"status")?,"attempt_count":DB::number(activity,"attempt_count")?,
    -            "closed_at":DB::optional_instant(activity,"closed_at")?,
    -            "attempt_id":DB::optional_string(activity,"current_attempt_id")?,
    -            "exception":DB::optional_string(activity,"exception")?.map(|value|wire(&value)),
    -            "payload_codec": "avro", "queue": DB::string(activity,"queue")?}});
    -        let metadata = super::selections::activity_metadata(DB::optional_document_row(
    -            activity,
    -            "parallel_group_path",
    -        )?)?;
    -        super::selections::merge(&mut payload, &metadata);
    -        // `ActivitySnapshot::fromExecution` names the options' cancellation
    -        // policy and scope when set.
    -        if let Some(options) = DB::optional_document_row(activity, "activity_options")? {
    -            for field in ["cancellation_policy", "cancellation_scope_id"] {
    -                if let Some(value) = options[field].as_str() {
    -                    payload["activity"][field] = json!(value);
    -                }
    -            }
    -            // `ActivitySnapshot::fromExecution`: an abandoned activity names
    -            // the lifetime it keeps after the run stops waiting.
    -            if options["cancellation_policy"] == "abandon"
    -                && let Some(deadline) = DB::optional_instant(activity, "schedule_to_close_deadline_at")?
    -            {
    -                payload["activity"]["schedule_to_close_deadline_at"] = json!(deadline.format("%Y-%m-%dT%H:%M:%S%.6fZ").to_string());
    -            }
    -        }
    -        // Released history readers retain the first claim and heartbeat across
    -        // a retry even while the next attempt has not produced new progress.
    -        for field in ["started_at", "last_heartbeat_at"] {
    -            if let Some(value) = DB::optional_instant(activity, field)? {
    -                payload["activity"][field] = json!(value);
    -            }
    -        }
    -        if payload["activity"]["arguments"].is_null() {
    -            payload["activity"].as_object_mut().unwrap().remove("arguments");
    -        }
    -        if payload["activity"]["retry_policy"].is_null() {
    -            // A legacy absent snapshot stays absent in its durable history.
    -            // The activity claim may still advertise a nullable policy.
    -            payload["activity"]
    -                .as_object_mut()
    -                .unwrap()
    -                .remove("retry_policy");
    -        }
    -        Ok(payload)
    +        ActivityRecord::read::<DB>(activity)?.payload()
         }
     
         fn event(row: DB::Row) -> Result<Value> {
    -        let event_type = DB::string(&row, "event_type")?;
    -        let payload =
    -            super::history_presentation::payload(DB::document_row(&row, "payload")?, &event_type);
    +        let payload = DB::document_row(&row, "payload")?;
    +        Self::event_with(&row, payload)
    +    }
    +
    +    /// `event` with the row's stored payload, parsed.
    +    fn event_with(row: &DB::Row, payload: Value) -> Result<Value> {
    +        let event_type = DB::string(row, "event_type")?;
    +        let payload = super::history_presentation::payload(payload, &event_type);
             Ok(
                 json!({"id": DB::string(&row,"id")?, "sequence": DB::number(&row,"sequence")?, "event_type": DB::string(&row,"event_type")?,
             "payload": payload,
    @@ -5147,12 +5236,18 @@ where
             Ok(())
         }
     
    -    /// A run's whole history, whose last sequence is `last_sequence`.
    -    async fn whole_history(connection: &mut DB::Connection, run_id: &str, last_sequence: i64) -> Result<Vec<Value>> {
    +    /// A run's whole history, whose last sequence is `last_sequence`, and
    +    /// each event recorded in `fold`.
    +    pub(super) async fn whole_history(
    +        connection: &mut DB::Connection,
    +        run_id: &str,
    +        last_sequence: i64,
    +        mut fold: Option<&mut SummaryFold>,
    +    ) -> Result<Vec<Value>> {
             let mut events = Vec::new();
             let mut after = 0;
             loop {
    -            let (page, next) = Self::history_page_known(&mut *connection, run_id, after, 1000, Some(last_sequence)).await?;
    +            let (page, next) = Self::history_page_known(&mut *connection, run_id, after, 1000, Some(last_sequence), fold.as_deref_mut()).await?;
                 if let Some(last) = page.last() {
                     after = last["sequence"].as_i64().unwrap_or(after);
                 }
    @@ -5169,18 +5264,20 @@ where
             after_sequence: i64,
             page_size: i64,
         ) -> Result<(Vec<Value>, Option<String>)> {
    -        Self::history_page_known(connection, run_id, after_sequence, page_size, None).await
    +        Self::history_page_known(connection, run_id, after_sequence, page_size, None, None).await
         }
     
         /// `history_page_connection` for a caller that has read the run's
         /// `last_history_sequence` in this transaction, or `None` to read it
    -    /// after the page.
    +    /// after the page. `fold` records each event, from the payload text the
    +    /// summary scan reads.
         async fn history_page_known(
             connection: &mut DB::Connection,
             run_id: &str,
             after_sequence: i64,
             page_size: i64,
             last_sequence: Option<i64>,
    +        fold: Option<&mut SummaryFold>,
         ) -> Result<(Vec<Value>, Option<String>)> {
             use base64::{Engine, engine::general_purpose::STANDARD};
             // Bound serialized history bytes before hydrating JSON. SQLite executes
    @@ -5212,10 +5309,19 @@ where
                 ));
             }
             let next = (last < total).then(|| STANDARD.encode(last.to_string()));
    -        Ok((
    -            rows.into_iter().map(Self::event).collect::<Result<_>>()?,
    -            next,
    -        ))
    +        let events = match fold {
    +            None => rows.into_iter().map(Self::event).collect::<Result<_>>()?,
    +            Some(fold) => rows
    +                .iter()
    +                .map(|row| {
    +                    let raw = DB::document_text(row, "payload")?;
    +                    let payload = serde_json::from_str(&raw)?;
    +                    fold.record(&DB::string(row, "id")?, DB::number(row, "sequence")?, &DB::string(row, "event_type")?, &raw, &payload);
    +                    Self::event_with(row, payload)
    +                })
    +                .collect::<Result<_>>()?,
    +        };
    +        Ok((events, next))
         }
     }
     
    @@ -5519,6 +5625,10 @@ pub(super) struct Apply<'a, R> {
         /// The first child this batch started with `await: "started"`: the
         /// parent's start turn (Server #966).
         pub(super) start_turn: &'a std::sync::Mutex<Option<Value>>,
    +    /// The run's pending and running activities, as read before the batch's
    +    /// first command; that command takes it, so no later one reads a count
    +    /// a command may have changed.
    +    pub(super) open_activities: &'a std::sync::Mutex<Option<i64>>,
     }
     
     impl<R> Clone for Apply<'_, R> {
    @@ -5754,3 +5864,110 @@ pub(super) struct StartMetadata<'a> {
         pub(super) memo: Option<&'a super::memos::PreparedMemo>,
         pub(super) search_attributes: Option<&'a super::search_attribute_upserts::StartSearchAttributes>,
     }
    +
    +/// The activity execution columns its history payload and claim read, as a
    +/// row holds them, or as a statement of this transaction has just written
    +/// them, so the transaction need not read its own write back.
    +pub(super) struct ActivityRecord {
    +    pub(super) id: String,
    +    pub(super) activity_type: String,
    +    pub(super) activity_class: String,
    +    pub(super) sequence: i64,
    +    pub(super) arguments: Option<String>,
    +    pub(super) retry_policy: Option<Value>,
    +    pub(super) status: String,
    +    pub(super) attempt_count: i64,
    +    pub(super) closed_at: Option<DateTime<Utc>>,
    +    pub(super) current_attempt_id: Option<String>,
    +    pub(super) exception: Option<String>,
    +    pub(super) queue: String,
    +    pub(super) parallel_group_path: Option<Value>,
    +    pub(super) activity_options: Option<Value>,
    +    pub(super) started_at: Option<DateTime<Utc>>,
    +    pub(super) last_heartbeat_at: Option<DateTime<Utc>>,
    +    /// The schedule-to-start, start-to-close, schedule-to-close and
    +    /// heartbeat deadlines (`activity_deadlines::deadlines_of`).
    +    pub(super) deadlines: [Option<DateTime<Utc>>; 4],
    +}
    +
    +impl ActivityRecord {
    +    pub(super) fn read<DB: Backend>(activity: &DB::Row) -> Result<Self> {
    +        Ok(Self {
    +            id: DB::string(activity, "id")?,
    +            activity_type: DB::string(activity, "activity_type")?,
    +            activity_class: DB::string(activity, "activity_class")?,
    +            sequence: DB::number(activity, "sequence")?,
    +            arguments: DB::optional_string(activity, "arguments")?,
    +            retry_policy: DB::optional_document_row(activity, "retry_policy")?,
    +            status: DB::string(activity, "status")?,
    +            attempt_count: DB::number(activity, "attempt_count")?,
    +            closed_at: DB::optional_instant(activity, "closed_at")?,
    +            current_attempt_id: DB::optional_string(activity, "current_attempt_id")?,
    +            exception: DB::optional_string(activity, "exception")?,
    +            queue: DB::string(activity, "queue")?,
    +            parallel_group_path: DB::optional_document_row(activity, "parallel_group_path")?,
    +            activity_options: DB::optional_document_row(activity, "activity_options")?,
    +            started_at: DB::optional_instant(activity, "started_at")?,
    +            last_heartbeat_at: DB::optional_instant(activity, "last_heartbeat_at")?,
    +            deadlines: [
    +                DB::optional_instant(activity, "schedule_deadline_at")?,
    +                DB::optional_instant(activity, "close_deadline_at")?,
    +                DB::optional_instant(activity, "schedule_to_close_deadline_at")?,
    +                DB::optional_instant(activity, "heartbeat_deadline_at")?,
    +            ],
    +        })
    +    }
    +
    +    /// The activity's history payload (`Store::activity_payload`).
    +    pub(super) fn payload(&self) -> Result<Value> {
    +        let mut payload = json!({"activity_execution_id": self.id, "activity_type": self.activity_type,
    +        "activity_class": self.activity_class, "sequence": self.sequence,
    +        "activity": {"id":self.id,"idempotency_key":self.id,
    +            "sequence":self.sequence,"type":self.activity_type,
    +            "class":self.activity_class,"local_activity":false,
    +            "activity_type": self.activity_type, "arguments": self.arguments.as_deref().map(wire),
    +            "retry_policy":self.retry_policy,
    +            "status":self.status,"attempt_count":self.attempt_count,
    +            "closed_at":self.closed_at,
    +            "attempt_id":self.current_attempt_id,
    +            "exception":self.exception.as_deref().map(wire),
    +            "payload_codec": "avro", "queue": self.queue}});
    +        let metadata = super::selections::activity_metadata(self.parallel_group_path.clone())?;
    +        super::selections::merge(&mut payload, &metadata);
    +        // `ActivitySnapshot::fromExecution` names the options' cancellation
    +        // policy and scope when set.
    +        if let Some(options) = &self.activity_options {
    +            for field in ["cancellation_policy", "cancellation_scope_id"] {
    +                if let Some(value) = options[field].as_str() {
    +                    payload["activity"][field] = json!(value);
    +                }
    +            }
    +            // `ActivitySnapshot::fromExecution`: an abandoned activity names
    +            // the lifetime it keeps after the run stops waiting.
    +            if options["cancellation_policy"] == "abandon"
    +                && let Some(deadline) = self.deadlines[2]
    +            {
    +                payload["activity"]["schedule_to_close_deadline_at"] = json!(deadline.format("%Y-%m-%dT%H:%M:%S%.6fZ").to_string());
    +            }
    +        }
    +        // Released history readers retain the first claim and heartbeat across
    +        // a retry even while the next attempt has not produced new progress.
    +        for (field, value) in [("started_at", self.started_at), ("last_heartbeat_at", self.last_heartbeat_at)] {
    +            if let Some(value) = value {
    +                payload["activity"][field] = json!(value);
    +            }
    +        }
    +        if payload["activity"]["arguments"].is_null() {
    +            payload["activity"].as_object_mut().unwrap().remove("arguments");
    +        }
    +        if payload["activity"]["retry_policy"].is_null() {
    +            // A legacy absent snapshot stays absent in its durable history.
    +            // The activity claim may still advertise a nullable policy.
    +            payload["activity"]
    +                .as_object_mut()
    +                .unwrap()
    +                .remove("retry_policy");
    +        }
    +        Ok(payload)
    +    }
    +}
    diff --git a/rust/src/runtime/terminal_summary.rs b/rust/src/runtime/terminal_summary.rs
    index ea9d2a31..6a3032e0 100644
    --- a/rust/src/runtime/terminal_summary.rs
    +++ b/rust/src/runtime/terminal_summary.rs
    @@ -50,10 +50,7 @@ where
             tx: &mut Transaction<'_, DB>,
             run_id: &str,
         ) -> Result<()> {
    -        let run = Self::query("SELECT * FROM workflow_runs WHERE id=$1")
    -            .bind(run_id)
    -            .fetch_one(&mut **tx)
    -            .await?;
    +        let (run, census) = Self::census_run(tx, run_id).await?;
             let status = DB::string(&run, "status")?;
             let closed = terminal(&status);
             // Retention and v1 projections need their separately qualified
    @@ -85,7 +82,6 @@ where
                 updates,
                 ..
             } = history;
    -        let census = Self::projection_census(tx, run_id).await?;
             let active = if closed {
                 None
             } else {
    @@ -199,18 +195,24 @@ where
                 }
             }
             // Missing/incomplete summaries use the same bounded canonical scan.
    -        // This calculation does not persist or repair any projection.
    -        let history = Self::summary_history(tx, run_id, true, last)
    +        // This calculation does not persist or repair any projection. It
    +        // scans as an open run's projection does, and the transaction keeps
    +        // the scan, so the run's projection at commit continues it instead
    +        // of reading the history again.
    +        let history = Self::scan_summary_history(tx, run_id, false, last)
                 .await?
    +            .filter(|history| history.count == last && history.count != 0)
                 .ok_or_else(|| {
                     super::refuse(
                         axum::http::StatusCode::CONFLICT,
                         "history_budget_unavailable",
                     )
                 })?;
    -        Ok(tx
    +        let budget = tx
                 .history_budget
    -            .summarize(history.count, history.bytes, history.fan_out))
    +            .summarize(history.count, history.bytes, history.fan_out);
    +        tx.summary_scan = Some(SummaryScan { run_id: run_id.to_owned(), open: true, history });
    +        Ok(budget)
         }
     
         async fn summary_history(
    @@ -219,9 +221,45 @@ where
             closed: bool,
             last: i64,
         ) -> Result<Option<SummaryHistory>> {
    -        let mut history = SummaryHistory {
    -            active_history_supported: true,
    -            ..SummaryHistory::default()
    +        let Some(mut history) = Self::scan_summary_history(tx, run_id, closed, last).await? else {
    +            return Ok(None);
    +        };
    +        history.active_history_supported &= history.selections.complete();
    +        history.active_history_supported &= history.barriers.complete();
    +        Ok((history.count == last && history.count != 0).then_some(history))
    +    }
    +
    +    /// `summary_history`'s pass over the events, before it judges the
    +    /// open selections and barriers: from the scan this transaction kept
    +    /// for the run, when there is one that covers what `closed` records,
    +    /// or else from the first event. `None` when a sequence is missing.
    +    async fn scan_summary_history(
    +        tx: &mut Transaction<'_, DB>,
    +        run_id: &str,
    +        closed: bool,
    +        last: i64,
    +    ) -> Result<Option<SummaryHistory>> {
    +        let kept = match tx.summary_scan.take() {
    +            Some(scan) if scan.run_id != run_id => {
    +                tx.summary_scan = Some(scan);
    +                None
    +            }
    +            scan => scan.filter(|scan| (closed || scan.open) && scan.history.count <= last),
    +        };
    +        // A kept scan read to a short page, so the history ended there. A
    +        // later event takes a sequence past it, so a kept scan that has
    +        // reached `last` has read every event.
    +        if let Some(scan) = kept.as_ref()
    +            && scan.history.count == last
    +        {
    +            return Ok(kept.map(|scan| scan.history));
    +        }
    +        let mut history = match kept {
    +            Some(scan) => scan.history,
    +            None => SummaryHistory {
    +                active_history_supported: true,
    +                ..SummaryHistory::default()
    +            },
             };
             loop {
                 // Bound hydrated history to sixteen events. The transaction already
    @@ -248,9 +286,56 @@ where
                     break;
                 }
             }
    -        history.active_history_supported &= history.selections.complete();
    -        history.active_history_supported &= history.barriers.complete();
    -        Ok((history.count == last && history.count != 0).then_some(history))
    +        Ok(Some(history))
    +    }
    +}
    +
    +/// A summary scan of one run's history that a transaction keeps for the
    +/// run's projection. It lives in the transaction, which holds the transition
    +/// lock, so the history only grows by appended sequences; the in-place
    +/// update and the deletes forget it (`Transaction::forget_history`). `open`
    +/// is whether it recorded what an open run's projection records.
    +pub(super) struct SummaryScan {
    +    run_id: String,
    +    open: bool,
    +    history: SummaryHistory,
    +}
    +
    +/// A summary scan built from history rows the transaction read from the
    +/// first event for another purpose (a workflow task's history), so the
    +/// run's budget and projection do not read them again.
    +pub(super) struct SummaryFold(Option<SummaryHistory>);
    +
    +impl Default for SummaryFold {
    +    fn default() -> Self {
    +        Self(Some(SummaryHistory {
    +            active_history_supported: true,
    +            ..SummaryHistory::default()
    +        }))
    +    }
    +}
    +
    +impl SummaryFold {
    +    /// Records the next event as an open run's scan does. An event out of
    +    /// sequence, or one the scan refuses, abandons the fold, so the scan
    +    /// reads and judges the history itself.
    +    pub(super) fn record(&mut self, id: &str, sequence: i64, kind: &str, raw: &str, payload: &Value) {
    +        let Some(history) = self.0.as_mut() else {
    +            return;
    +        };
    +        if sequence == history.count + 1 && history.record_parsed(id, kind, raw, payload, false).is_ok() {
    +            history.count = sequence;
    +        } else {
    +            self.0 = None;
    +        }
    +    }
    +
    +    /// Keeps the fold as the transaction's scan when it recorded the run's
    +    /// whole history, through `last`.
    +    pub(super) fn keep<DB: Backend>(self, tx: &mut Transaction<'_, DB>, run_id: &str, last: i64) {
    +        if let Some(history) = self.0.filter(|history| history.count == last) {
    +            tx.summary_scan = Some(SummaryScan { run_id: run_id.to_owned(), open: true, history });
    +        }
         }
     }
     
    @@ -274,11 +359,15 @@ struct SummaryHistory {
     
     impl SummaryHistory {
         fn record(&mut self, id: &str, kind: &str, raw: &str, closed: bool) -> Result<()> {
    -        let payload: Value = serde_json::from_str(raw)?;
    -        self.bytes += history_budget::parsed_event_size(kind, raw, &payload)? as i64;
    +        self.record_parsed(id, kind, raw, &serde_json::from_str(raw)?, closed)
    +    }
    +
    +    /// `record` for an event whose payload text the caller has parsed.
    +    fn record_parsed(&mut self, id: &str, kind: &str, raw: &str, payload: &Value, closed: bool) -> Result<()> {
    +        self.bytes += history_budget::parsed_event_size(kind, raw, payload)? as i64;
             if !closed {
    -            self.active_history_supported &= self.barriers.apply(kind, &payload);
    -            self.active_history_supported &= self.selections.apply(id, kind, &payload);
    +            self.active_history_supported &= self.barriers.apply(kind, payload);
    +            self.active_history_supported &= self.selections.apply(id, kind, payload);
                 let supported = if kind == "SelectionResolved" {
                     true
                 } else if matches!(kind, "TimerScheduled" | "TimerFired" | "TimerCancelled") {
    @@ -286,26 +375,26 @@ impl SummaryHistory {
                         payload["timer_kind"].as_str(),
                         Some("signal_timeout" | "condition_timeout")
                     ) {
    -                    self.external_waits.apply(kind, &payload)
    +                    self.external_waits.apply(kind, payload)
                     } else {
    -                    self.timers.apply(kind, &payload)
    +                    self.timers.apply(kind, payload)
                     }
                 } else if kind.starts_with("Activity") {
    -                self.activities.apply(kind, &payload)
    +                self.activities.apply(kind, payload)
                 } else if kind.starts_with("Child") {
    -                self.children.apply(kind, &payload)
    +                self.children.apply(kind, payload)
                 } else if kind.starts_with("Update") {
    -                self.updates.apply(kind, &payload)
    +                self.updates.apply(kind, payload)
                 } else {
    -                self.external_waits.apply(kind, &payload)
    +                self.external_waits.apply(kind, payload)
                 };
                 self.active_history_supported &= supported;
             }
             if kind == "WorkflowStarted" && self.started.is_none() {
                 self.started = Some(payload.clone());
             }
    -        self.record_fan_out(&payload);
    -        self.record_failure(kind, &payload);
    +        self.record_fan_out(payload);
    +        self.record_failure(kind, payload);
             Ok(())
         }
     
    diff --git a/rust/src/runtime/transaction.rs b/rust/src/runtime/transaction.rs
    index 5ad98151..8d7f1466 100644
    --- a/rust/src/runtime/transaction.rs
    +++ b/rust/src/runtime/transaction.rs
    @@ -23,11 +23,18 @@ pub(super) struct Transaction<'a, DB: Backend> {
         /// has committed or rolled back (`Store::begin`).
         pub(super) queued: Option<tokio::sync::OwnedMutexGuard<()>>,
         summaries: BTreeSet<String>,
    +    /// The runs whose waits this transaction opened or reopened. Ordinals
    +    /// rank a run's waits by when each opened, and only all of a run's waits
    +    /// are ever deleted, so no other change moves one (`Store::order_waits`).
    +    wait_order: BTreeSet<String>,
         pub(super) history_budget: super::history_budget::Config,
         /// A workflow task completion's history meter (Workflow #930).
         pub(super) meter: Option<super::history_meter::Meter>,
         /// The run history the cancellation scope engine has read (`scope_events`).
         pub(super) scope_history: Option<super::cancellation_scopes::HistoryCache>,
    +    /// The summary scan of a run's history this transaction has kept
    +    /// (`Store::workflow_history_budget`), for the run's projection.
    +    pub(super) summary_scan: Option<super::terminal_summary::SummaryScan>,
         /// Times the transition lock while it is held (`DW_BENCH_TRACE`).
         pub(super) lock_trace: Option<super::bench_trace::LockTrace>,
         /// The binding key of a workflow poll request this transaction has
    @@ -38,6 +45,9 @@ pub(super) struct Transaction<'a, DB: Backend> {
         pub(super) run_tags: HashMap<String, RunTags>,
         /// Local activity rows not yet written (`Store::write_local_activities`).
         pub(super) local_writes: super::local_activities::Writes,
    +    /// The activities this transaction scheduled and has not yet started an
    +    /// attempt of: their ids are new, so they have no attempt to expire.
    +    pub(super) unattempted_activities: BTreeSet<String>,
     }
     
     impl<'a, DB: Backend> Transaction<'a, DB> {
    @@ -49,13 +59,16 @@ impl<'a, DB: Backend> Transaction<'a, DB> {
                 inner,
                 queued: None,
                 summaries: BTreeSet::new(),
    +            wait_order: BTreeSet::new(),
                 history_budget,
                 meter: None,
                 scope_history: None,
    +            summary_scan: None,
                 lock_trace: None,
                 replayed_poll: None,
                 run_tags: HashMap::new(),
                 local_writes: Default::default(),
    +            unattempted_activities: BTreeSet::new(),
             }
         }
     
    @@ -63,6 +76,17 @@ impl<'a, DB: Backend> Transaction<'a, DB> {
             self.summaries.insert(run_id.to_owned());
         }
     
    +    pub(super) fn wait_order_changed(&mut self, run_id: &str) {
    +        self.wait_order.insert(run_id.to_owned());
    +    }
    +
    +    /// Forget every history read this transaction keeps: its rows were
    +    /// changed in place or deleted, so a later read starts over.
    +    pub(super) fn forget_history(&mut self) {
    +        self.scope_history = None;
    +        self.summary_scan = None;
    +    }
    +
         /// Rows the next statement will write, charged to a metered completion.
         pub(super) fn charge_rows(&mut self, rows: usize) -> Result<()> {
             match self.meter.as_mut() {
    @@ -129,7 +153,9 @@ where
             let written = std::time::Instant::now();
             super::bench_trace::phase("local writes", written - started);
             for run_id in std::mem::take(&mut self.summaries) {
    -            Store::<DB>::order_waits(&mut self, &run_id).await?;
    +            if self.wait_order.contains(&run_id) {
    +                Store::<DB>::order_waits(&mut self, &run_id).await?;
    +            }
                 Store::<DB>::project_run_summary(&mut self, &run_id).await?;
             }
             let projected = std::time::Instant::now();
    diff --git a/rust/src/runtime/wait_projection.rs b/rust/src/runtime/wait_projection.rs
    index 0627a4c1..fb617ef1 100644
    --- a/rust/src/runtime/wait_projection.rs
    +++ b/rust/src/runtime/wait_projection.rs
    @@ -20,7 +20,8 @@ where
     {
         /// Store each wait's chronological ordinal over all of the run's retained
         /// waits, as Workflow #941's projector does, writing only waits that moved.
    -    /// Resolving a wait moves nothing, so most transitions write no positions.
    +    /// Resolving a wait moves nothing, so a transition that opened no wait
    +    /// does not rank them (`Transaction::wait_order_changed`).
         pub(super) async fn order_waits(tx: &mut Transaction<'_, DB>, run: &str) -> Result<()> {
             let mut moved = std::collections::HashSet::new();
             loop {
    @@ -74,6 +75,7 @@ where
                     .bind(projection_id(run, &wait)).bind(wait).bind(sequence - 1).bind(format!("Waiting for update {name}."))
                     .bind(DB::bind_time(at)).bind(name).bind(update).bind(super::text(payload, "workflow_command_id")?)
                     .bind(sequence).bind(DB::bind_time(at)).bind(DB::bind_time(at)).bind(run).execute(&mut **tx).await?;
    +            tx.wait_order_changed(run);
             } else {
                 let failed = payload["failure_id"]
                     .as_str()
    @@ -122,6 +124,7 @@ where
                     .bind(DB::bind_time(at)).bind(super::text(payload,"child_workflow_instance_id")?).bind(label)
                     .bind(super::text(payload,"child_workflow_run_id")?).bind(DB::bind_time(at)).bind(DB::bind_time(at)).bind(run_id)
                     .execute(&mut **tx).await?;
    +            tx.wait_order_changed(run_id);
             } else {
                 // `ChildRunHistory::waitSnapshotForSequence`: a cancelled or
                 // terminated child ends the wait as cancelled.
    @@ -147,21 +150,47 @@ where
             activity_id: &str,
             task_id: &str,
             at: DateTime<Utc>,
    +    ) -> Result<()> {
    +        let activity = Self::query("SELECT a.id,a.workflow_run_id,a.sequence,a.activity_type,r.workflow_instance_id FROM activity_executions a JOIN workflow_runs r ON r.id=a.workflow_run_id WHERE a.id=$1 AND a.workflow_run_id=$2")
    +            .bind(activity_id).bind(run_id).fetch_one(&mut **tx).await?;
    +        let activity_type = DB::string(&activity, "activity_type")?;
    +        Self::open_activity_wait(
    +            tx,
    +            &DB::string(&activity, "workflow_instance_id")?,
    +            &DB::string(&activity, "workflow_run_id")?,
    +            &DB::string(&activity, "id")?,
    +            DB::number(&activity, "sequence")?,
    +            &activity_type,
    +            task_id,
    +            at,
    +        )
    +        .await
    +    }
    +
    +    /// `create_activity_wait` for an activity, of a run in the given
    +    /// instance, whose row the transaction has already read or written.
    +    #[allow(clippy::too_many_arguments)]
    +    pub(super) async fn open_activity_wait(
    +        tx: &mut Transaction<'_, DB>,
    +        instance_id: &str,
    +        run_id: &str,
    +        activity_id: &str,
    +        sequence: i64,
    +        activity_type: &str,
    +        task_id: &str,
    +        at: DateTime<Utc>,
         ) -> Result<()> {
             // Only the authored ActivityScheduled transition creates this row.
             // Reads and later events never repair an absent projection implicitly.
    -        let activity = Self::query("SELECT a.sequence,a.activity_type,r.workflow_instance_id FROM activity_executions a JOIN workflow_runs r ON r.id=a.workflow_run_id WHERE a.id=$1 AND a.workflow_run_id=$2")
    -            .bind(activity_id).bind(run_id).fetch_one(&mut **tx).await?;
             let wait_id = format!("activity:{activity_id}");
             let projection_id = projection_id(run_id, &wait_id);
    -        let sequence = DB::number(&activity, "sequence")?;
    -        let activity_type = DB::string(&activity, "activity_type")?;
             Self::query("INSERT INTO workflow_run_waits(id,workflow_run_id,workflow_instance_id,wait_id,position,kind,sequence,status,source_status,summary,opened_at,target_type,task_backed,external_only,resume_source_kind,resume_source_id,task_id,task_type,task_status,history_authority,created_at,updated_at) VALUES ($1,$2,$3,$4,$5,'activity',$6,'open','pending',$7,$8,$9,TRUE,FALSE,'activity_execution',$10,$11,'activity','ready','typed_history',$12,$13)")
    -            .bind(projection_id).bind(run_id).bind(DB::string(&activity, "workflow_instance_id")?)
    +            .bind(projection_id).bind(run_id).bind(instance_id)
                 .bind(wait_id).bind(sequence - 1).bind(sequence)
                 .bind(format!("Waiting for activity {activity_type}."))
                 .bind(DB::bind_time(at)).bind(activity_type).bind(activity_id).bind(task_id)
                 .bind(DB::bind_time(at)).bind(DB::bind_time(at)).execute(&mut **tx).await?;
    +        tx.wait_order_changed(run_id);
             Ok(())
         }
     
    @@ -197,6 +226,7 @@ where
             } else if event_type == "ActivityStarted" {
                 Self::query("UPDATE workflow_run_waits SET source_status='running',task_status='leased',opened_at=$1,updated_at=$2 WHERE workflow_run_id=$3 AND wait_id=$4 AND status='open'")
                     .bind(DB::bind_time(at)).bind(DB::bind_time(at)).bind(run_id).bind(wait_id).execute(&mut **tx).await?;
    +            tx.wait_order_changed(run_id);
             } else {
                 let activity_type = super::text(payload, "activity_type")?;
                 let (status, source_status, summary) = match event_type {
    @@ -242,6 +272,7 @@ where
                 .bind(DB::bind_time(at)).bind(DB::bind_time(fire_at)).bind(timer_id).bind(task_id)
                 .bind(DB::bind_time(at)).bind(DB::bind_time(at)).bind(run_id)
                 .execute(&mut **tx).await?;
    +        tx.wait_order_changed(run_id);
             Ok(())
         }
     
    @@ -299,6 +330,7 @@ where
                     .bind(summary)
                     .bind(DB::bind_time(at)).bind(key).bind(DB::bind_time(at)).bind(DB::bind_time(at))
                     .bind(run_id).execute(&mut **tx).await?;
    +            tx.wait_order_changed(run_id);
             } else if event_type == "ConditionWaitSatisfied" {
                 // Receipt alone is not satisfaction. Only the worker's committed
                 // application event resolves this occurrence, without read repair.
    @@ -362,6 +394,7 @@ where
                     .bind(summary)
                     .bind(DB::bind_time(at)).bind(name).bind(DB::bind_time(at)).bind(DB::bind_time(at))
                     .bind(run_id).execute(&mut **tx).await?;
    +            tx.wait_order_changed(run_id);
             } else if payload["timer_kind"] == "signal_timeout"
                 && matches!(
                     event_type,
    diff --git a/rust/src/runtime/workflow_poll_requests.rs b/rust/src/runtime/workflow_poll_requests.rs
    index 7e1b538a..17891d78 100644
    --- a/rust/src/runtime/workflow_poll_requests.rs
    +++ b/rust/src/runtime/workflow_poll_requests.rs
    @@ -18,12 +18,16 @@ use super::transaction::Transaction;
     use super::{Result, RuntimeError, store::now};
     use axum::http::StatusCode;
     use chrono::{DateTime, Utc};
    -use serde_json::{Value, json};
    +use serde_json::{Map, Value, json};
     use sha2::{Digest, Sha256};
     
     // Distinct from the query poll prefix, so each sweep sees only its own rows.
     const PREFIX: &str = "dw-native-workflow-poll:";
     
    +// A remembered workflow lease keeps where its history ended in place of the
    +// events (`remembered`), and a replay reads them again (`with_history`).
    +const HISTORY: &str = "remembered_history";
    +
     // The task row a cached workflow lease is delivered from.
     const WORKFLOW_LEASE: &str = "SELECT status,queue,lease_owner,lease_expires_at,attempt_count FROM workflow_tasks WHERE id=$1 AND task_type='workflow'";
     
    @@ -93,6 +97,39 @@ fn task_kind(response: &Value) -> &str {
         response["task"]["task_kind"].as_str().unwrap_or("workflow")
     }
     
    +/// An answer as it is remembered. A workflow lease's history events are
    +/// the run's events through the last one delivered, which never change
    +/// while the lease can be delivered, so it keeps that sequence and the
    +/// signal sequences the events showed instead of the events.
    +fn remembered(response: &Value) -> Value {
    +    let (Some(answer), Some(events)) = (response.as_object(), response["task"]["history_events"].as_array()) else {
    +        return response.clone();
    +    };
    +    let through = events.last().and_then(|event| event["sequence"].as_i64()).unwrap_or(0);
    +    let signal_sequences: Map<String, Value> = events
    +        .iter()
    +        .filter(|event| event["event_type"] == "SignalReceived")
    +        .filter_map(|event| {
    +            let sequence = event["payload"].get("workflow_sequence").filter(|sequence| !sequence.is_null())?;
    +            Some((event["payload"]["signal_id"].as_str()?.to_owned(), sequence.clone()))
    +        })
    +        .collect();
    +    let mut kept = Map::new();
    +    for (key, value) in answer {
    +        let value = match value.as_object().filter(|_| key == "task") {
    +            Some(task) => {
    +                let task = task.iter().map(|(field, value)| (field.clone(), if field == "history_events" { Value::Null } else { value.clone() }));
    +                Value::Object(task.collect())
    +            }
    +            None => value.clone(),
    +        };
    +        kept.insert(key.clone(), value);
    +    }
    +    let run_id = response["task"]["run_id"].clone();
    +    kept.insert(HISTORY.into(), json!({"run_id": run_id, "through": through, "signal_sequences": signal_sequences}));
    +    Value::Object(kept)
    +}
    +
     /// The 409 for an ID already bound to another task-kind set.
     fn kinds_conflict(request: &WorkflowPollRequest, bound: Value) -> RuntimeError {
         RuntimeError::Protocol {
    @@ -137,7 +174,9 @@ store_methods! {
                     .collect();
                 format!("{PREFIX}{digest}")
             };
    -        let keys = WorkflowPollKeys { binding: key("binding"), result: key("result") };
    +        // A remembered lease is not the answer it delivered, so it has its
    +        // own key: a server that keeps whole answers never reads one.
    +        let keys = WorkflowPollKeys { binding: key("binding"), result: key("remembered-result") };
             Ok(WorkflowPollRequest { keys, pending_ttl: pending_ttl(default_wait), kinds: normalized(kinds), id })
         }
     
    @@ -206,9 +245,10 @@ store_methods! {
     
         /// The cached answer if its task can still be delivered; a stale answer
         /// is deleted. A workflow lease shows the task's current lease, as PHP
    -    /// rebuilds it; a validation lease replays as it was delivered. An
    -    /// answer of a kind this request did not ask for is not replayed.
    -    /// `result` is the answer `bind_workflow_poll` read.
    +    /// rebuilds it, and the history it delivered, read again; a validation
    +    /// lease replays as it was delivered. An answer of a kind this request
    +    /// did not ask for is not replayed. `result` is the answer
    +    /// `bind_workflow_poll` read.
         pub(super) async fn deliverable_workflow_poll(
             &self,
             tx: &mut Transaction<'_, DB>,
    @@ -233,6 +273,7 @@ store_methods! {
                 Self::workflow_lease_deliverable(tx, &mut result["task"], worker_id, queue).await?
             };
             if deliverable {
    +            Self::with_history(&mut **tx, &mut result).await?;
                 return Ok(Some(result));
             }
             Self::query(DB::QUERY_CACHE_DELETE).bind(&request.keys.result).execute(&mut **tx).await?;
    @@ -280,6 +321,30 @@ store_methods! {
             Ok(false)
         }
     
    +    /// A remembered lease's history events, read again: the run's events
    +    /// through the sequence the delivered history ended at, showing the
    +    /// signal sequences they showed then (`remembered`).
    +    async fn with_history(connection: &mut DB::Connection, answer: &mut Value) -> Result<()> {
    +        let Some(kept) = answer.as_object_mut().and_then(|answer| answer.shift_remove(HISTORY)) else {
    +            return Ok(());
    +        };
    +        let (Some(run_id), Some(through)) = (kept["run_id"].as_str(), kept["through"].as_i64()) else {
    +            return Err(RuntimeError::Refused { status: StatusCode::INTERNAL_SERVER_ERROR, reason: "remembered_workflow_lease_unreadable" });
    +        };
    +        let mut events = if through > 0 { Self::whole_history(connection, run_id, through, None).await? } else { Vec::new() };
    +        events.retain(|event| event["sequence"].as_i64().is_some_and(|sequence| sequence <= through));
    +        for event in events.iter_mut().filter(|event| event["event_type"] == "SignalReceived") {
    +            let sequence = event["payload"]["signal_id"].as_str().and_then(|id| kept["signal_sequences"].get(id)).cloned();
    +            if let (Some(sequence), Some(payload)) = (sequence, event["payload"].as_object_mut())
    +                && payload.get("workflow_sequence").is_none_or(Value::is_null)
    +            {
    +                payload.insert("workflow_sequence".into(), sequence);
    +            }
    +        }
    +        answer["task"]["history_events"] = Value::Array(events);
    +        Ok(())
    +    }
    +
         /// The broker's `cachedTaskStillDeliverable`: the validation task is in
         /// this namespace and queue, leased to this worker with a live lease, at
         /// the delivered attempt.
    @@ -307,13 +372,14 @@ store_methods! {
         /// Remember a poll's answer under its request ID, and rewrite the
         /// binding with PHP's result expiry. A workflow lease's answer is kept
         /// while its task can be delivered (the row decides), as PHP's lease
    -    /// binding replays for the whole live lease.
    +    /// binding replays for the whole live lease. A workflow lease is kept
    +    /// without its history events (`remembered`).
         pub(super) async fn remember_workflow_poll(tx: &mut Transaction<'_, DB>, request: &WorkflowPollRequest, response: &Value) -> Result<()> {
             let ttl = result_ttl(response, request.pending_ttl);
             let kept = if response["task"].is_object() && task_kind(response) == "workflow" { 3600 } else { ttl };
             Self::query(DB::QUERY_CACHE_PUT_PAIR)
                 .bind(&request.keys.binding).bind(json!({"task_kinds": request.kinds}).to_string()).bind(epoch() + ttl)
    -            .bind(&request.keys.result).bind(response.to_string()).bind(epoch() + kept)
    +            .bind(&request.keys.result).bind(remembered(response).to_string()).bind(epoch() + kept)
                 .execute(&mut **tx)
                 .await?;
             Ok(())
    @@ -348,7 +414,11 @@ store_methods! {
                 return Ok(None);
             };
             let row = Self::query(WORKFLOW_LEASE).bind(&task_id).fetch_optional(&self.pool).await?;
    -        Ok(Self::lease_row_deliverable(row, &mut result["task"], worker_id, queue)?.then_some(result))
    +        if !Self::lease_row_deliverable(row, &mut result["task"], worker_id, queue)? {
    +            return Ok(None);
    +        }
    +        Self::with_history(&mut *self.pool.acquire().await?, &mut result).await?;
    +        Ok(Some(result))
         }
     
         /// A cache row read without the lock, if it has not expired.
    diff --git a/rust/tests/execution/activity_results.rs b/rust/tests/execution/activity_results.rs
    index 86a50a27..096ff29b 100644
    --- a/rust/tests/execution/activity_results.rs
    +++ b/rust/tests/execution/activity_results.rs
    @@ -404,3 +404,115 @@ async fn activity_poll_answers_carry_the_server_clock() {
         assert_eq!(empty.1["poll_status"], "empty", "{}", empty.1);
         assert!(micros(&empty.1["server_time"]), "{}", empty.1);
     }
    +
    +/// An activity's row, each column as its text or null.
    +async fn activity_row(database: &TestDatabase, id: &str) -> Value {
    +    let cast = if matches!(database, TestDatabase::MySql { .. }) { "CHAR" } else { "TEXT" };
    +    let query = [
    +        "status", "attempt_count", "current_attempt_id", "queue", "retry_policy", "started_at", "closed_at",
    +        "last_heartbeat_at", "schedule_deadline_at", "close_deadline_at", "schedule_to_close_deadline_at",
    +        "heartbeat_deadline_at",
    +    ]
    +    .map(|column| format!("SELECT '{column}',CAST({column} AS {cast}) FROM activity_executions WHERE id='{id}'"))
    +    .join(" UNION ALL ");
    +    macro_rules! read {
    +        ($pool:expr) => {{
    +            let pool = $pool;
    +            let rows: Vec<(String, Option<String>)> =
    +                sqlx::query_as(sqlx::AssertSqlSafe(query)).fetch_all(&pool).await.unwrap();
    +            pool.close().await;
    +            rows
    +        }};
    +    }
    +    let rows = match database {
    +        TestDatabase::Sqlite(dir) => read!(
    +            sqlx::SqlitePool::connect(&format!("sqlite://{}", dir.path().join("runtime.sqlite").display())).await.unwrap()
    +        ),
    +        TestDatabase::Postgres { options, .. } => {
    +            read!(sqlx::postgres::PgPoolOptions::new().connect_with(options.as_ref().clone()).await.unwrap())
    +        }
    +        TestDatabase::MySql { options, .. } => {
    +            read!(sqlx::mysql::MySqlPoolOptions::new().connect_with(options.as_ref().clone()).await.unwrap())
    +        }
    +    };
    +    Value::Object(rows.into_iter().map(|(column, value)| (column, json!(value))).collect())
    +}
    +
    +/// A time as a history snapshot or a row's text holds it.
    +fn snapshot_time(value: &Value) -> Option<chrono::NaiveDateTime> {
    +    let text = value.as_str()?;
    +    Some(
    +        chrono::DateTime::parse_from_rfc3339(text)
    +            .map(|at| at.naive_utc())
    +            .or_else(|_| chrono::NaiveDateTime::parse_from_str(text, "%Y-%m-%d %H:%M:%S%.f"))
    +            .unwrap(),
    +    )
    +}
    +
    +/// An activity's history snapshot agrees with its row.
    +fn assert_snapshot_is_row(snapshot: &Value, row: &Value) {
    +    assert_eq!(snapshot["status"], row["status"], "{snapshot} {row}");
    +    assert_eq!(snapshot["attempt_count"].to_string(), row["attempt_count"].as_str().unwrap(), "{snapshot} {row}");
    +    assert_eq!(snapshot["attempt_id"], row["current_attempt_id"], "{snapshot} {row}");
    +    assert_eq!(snapshot["queue"], row["queue"], "{snapshot} {row}");
    +    let policy: Value = serde_json::from_str(row["retry_policy"].as_str().unwrap()).unwrap();
    +    assert_eq!(snapshot["retry_policy"], policy, "{snapshot} {row}");
    +    for field in ["started_at", "closed_at", "last_heartbeat_at"] {
    +        assert_eq!(snapshot_time(&snapshot[field]), snapshot_time(&row[field]), "{field}: {snapshot} {row}");
    +    }
    +    assert_eq!(
    +        snapshot_time(&snapshot["schedule_to_close_deadline_at"]),
  4. rmcdaniel commented on Nov 6, 2026

    @rmcdaniel
    MemberAuthor

    Sanitized external reference for topic 23: 0005-Cut-more-statements-from-each-workflow-and-activity-.patch, part 3/3. Complete sanitized diff: 103,089 bytes / SHA-256 3095223e9455381ff492a08b7daf5651ce94aa138bff00cecaf3bc2bdfdc2306. Ordered parts preserve the complete diff bytes; none was applied or executed. The patch is a cumulative older-source draft, not maintained implementation or qualification. Mail headers/author/date/message/application instructions were removed.

    The supplied 0005 stage includes the known stale wait-ranking shortcut. It is not an acceptable production candidate by itself; retain and independently qualify correction0006 and the negative controls.

    +        snapshot_time(&row["schedule_to_close_deadline_at"]),
    +        "{snapshot} {row}"
    +    );
    +}
    +
    +// Scheduling, claiming and completing an activity each write its row and
    +// record its snapshot without reading the row back: each snapshot is the row
    +// as its transition left it.
    +#[tokio::test]
    +async fn an_activity_snapshot_is_its_row_as_each_transition_left_it() {
    +    let database = TestDatabase::new().await;
    +    let runtime = database.open().await.unwrap();
    +    let app = router(runtime.clone());
    +    register(&app, "snapshot-worker", json!(["echo"]), json!(["snapshot-activity"])).await;
    +    let started = start(&app, "activity-snapshot").await;
    +    let workflow = poll(&app, "snapshot-worker", "workflow").await;
    +    let done = finish_task(
    +        &app,
    +        &workflow,
    +        json!([{"type":"schedule_activity","activity_type":"snapshot-activity",
    +            "arguments":envelope(Payload::String("界".into())),
    +            "retry_policy":{"max_attempts":3,"backoff_seconds":[1,2]},"cancellation_policy":"abandon",
    +            "start_to_close_timeout":120,"schedule_to_start_timeout":60,"schedule_to_close_timeout":600,"heartbeat_timeout":30}]),
    +    )
    +    .await;
    +    assert_eq!(done.0, StatusCode::OK, "{}", done.1);
    +    let snapshot = |history: &Value, event_type: &str| {
    +        history.as_array().unwrap().iter().find(|event| event["event_type"] == event_type).unwrap()["payload"]["activity"].clone()
    +    };
    +    let history = run_history(&app, &started["workflow_id"], &started["run_id"]).await;
    +    let scheduled = snapshot(&history, "ActivityScheduled");
    +    assert!(scheduled["schedule_to_close_deadline_at"].is_string(), "{scheduled}");
    +    let id = scheduled["id"].as_str().unwrap().to_owned();
    +    assert_snapshot_is_row(&scheduled, &activity_row(&database, &id).await);
    +    let task = poll(&app, "snapshot-worker", "activity").await;
    +    let row = activity_row(&database, &id).await;
    +    let history = run_history(&app, &started["workflow_id"], &started["run_id"]).await;
    +    assert_snapshot_is_row(&snapshot(&history, "ActivityStarted"), &row);
    +    for (kind, column) in [
    +        ("schedule_to_start", "schedule_deadline_at"),
    +        ("start_to_close", "close_deadline_at"),
    +        ("schedule_to_close", "schedule_to_close_deadline_at"),
    +        ("heartbeat", "heartbeat_deadline_at"),
    +    ] {
    +        assert_eq!(snapshot_time(&task["deadlines"][kind]), snapshot_time(&row[column]), "{kind}: {task}");
    +    }
    +    finish_activity(&app, "default", &task, envelope(Payload::Null)).await;
    +    let history = run_history(&app, &started["workflow_id"], &started["run_id"]).await;
    +    let completed = snapshot(&history, "ActivityCompleted");
    +    assert!(completed["closed_at"].is_string(), "{completed}");
    +    assert_snapshot_is_row(&completed, &activity_row(&database, &id).await);
    +    runtime.close().await;
    +    database.remove().await;
    +}
    diff --git a/rust/tests/execution/history_budget.rs b/rust/tests/execution/history_budget.rs
    index 0abb681c..0cd9e09b 100644
    --- a/rust/tests/execution/history_budget.rs
    +++ b/rust/tests/execution/history_budget.rs
    @@ -364,3 +364,33 @@ async fn history_budget_missing_history_rolls_back_original_claim_and_receipt()
         runtime.close().await;
         database.remove().await;
     }
    +
    +// A workflow task delivers the run's whole history, which is the summary scan
    +// its projection keeps: the summary it writes is the one a scan writes.
    +#[tokio::test]
    +async fn a_delivered_history_projects_the_summary_a_scan_does() {
    +    let database = TestDatabase::new().await;
    +    let runtime = database.open().await.unwrap();
    +    let app = router(runtime.clone());
    +    register(&app, "fold-worker", json!(["echo"]), json!([])).await;
    +    let started = start(&app, "summary-fold").await;
    +    // Text the summary measures from its encoding: an escape, characters
    +    // wider than a byte, and a fan-out group.
    +    database
    +        .replace_history_payload(json!({"note":"界\u{1}é","parallel_group_id":"group","parallel_group_size":3}))
    +        .await;
    +    let task = poll(&app, "fold-worker", "workflow").await;
    +    let delivered = stored(&database, &started).await;
    +    assert_eq!(delivered["history_fan_out"], 3, "{delivered}");
    +    let (status, body) = request(
    +        &app,
    +        "POST",
    +        &format!("/api/worker/workflow-tasks/{}/heartbeat", task["task_id"].as_str().unwrap()),
    +        json!({"lease_owner":task["lease_owner"],"workflow_task_attempt":task["workflow_task_attempt"]}),
    +    )
    +    .await;
    +    assert_eq!(status, StatusCode::OK, "{body}");
    +    assert_eq!(stored(&database, &started).await, delivered);
    +    runtime.close().await;
    +    database.remove().await;
    +}
    diff --git a/rust/tests/execution/workflow_poll_requests.rs b/rust/tests/execution/workflow_poll_requests.rs
    index 83ad4953..b7cf3d32 100644
    --- a/rust/tests/execution/workflow_poll_requests.rs
    +++ b/rust/tests/execution/workflow_poll_requests.rs
    @@ -48,6 +48,37 @@ async fn poll_request_ids_replay_like_php() {
         database.remove().await;
     }
     
    +// A replayed lease is the task it delivered, with its current lease. The
    +// history is the one it delivered, though the run's has grown since: a
    +// signal accepted while the task is leased is not in it.
    +#[tokio::test]
    +async fn a_replayed_lease_delivers_the_history_it_delivered() {
    +    let database = TestDatabase::new().await;
    +    let runtime = database.open().await.unwrap();
    +    let app = router(runtime.clone());
    +    let definition = json!({"worker_id":"replay-worker","task_queue":"test","runtime":"php",
    +        "supported_workflow_types":["echo"],"supported_activity_types":[],
    +        "workflow_command_contracts":{"echo":{"signals":["payload"],"signal_contracts":[{"name":"payload","parameters":[]}]}}});
    +    let (status, body) = request(&app, "POST", "/api/worker/register", definition).await;
    +    assert_eq!(status, StatusCode::CREATED, "{body}");
    +    let started = start(&app, "replay-history").await;
    +    let leased = poll_id(&app, "history-id").await;
    +    assert_eq!(leased["poll_status"], "leased", "{leased}");
    +    let signal = format!("/api/workflows/replay-history/runs/{}/signal/payload", started["run_id"].as_str().unwrap());
    +    let (status, body) = request(&app, "POST", &signal, json!({"input":envelope(Payload::Array(vec![]))})).await;
    +    assert_eq!(status, StatusCode::ACCEPTED, "{body}");
    +    let history = run_history(&app, &json!("replay-history"), &started["run_id"]).await;
    +    let delivered = leased["task"]["history_events"].as_array().unwrap().len();
    +    assert!(history.as_array().unwrap().len() > delivered, "{history}");
    +
    +    let replay = poll_id(&app, "history-id").await;
    +    let mut expected = leased["task"].clone();
    +    expected["lease_expires_at"] = replay["task"]["lease_expires_at"].clone();
    +    assert_eq!(replay["task"], expected);
    +    runtime.close().await;
    +    database.remove().await;
    +}
    +
     // A duplicate with the same ID can lease after a held poll's last probe and
     // before that poll records its no-task answer. The held poll then returns the
     // duplicate's lease, and a retry whose original response was lost recovers
  5. rmcdaniel commented on Nov 6, 2026

    @rmcdaniel
    MemberAuthor

    Sanitized external reference for topic 23: 0006-Rank-a-run-s-waits-on-every-transition-again-fix-the.patch, part 1/1. Complete sanitized diff: 8,143 bytes / SHA-256 4d1d1bc2e7e150495a6109f8c4ee0b2d13e65da67a7c8add9c0ef4c46f8fc090. Ordered parts preserve the complete diff bytes; none was applied or executed. The patch is a cumulative older-source draft, not maintained implementation or qualification. Mail headers/author/date/message/application instructions were removed.

    The supplied 0005 stage includes the known stale wait-ranking shortcut. It is not an acceptable production candidate by itself; retain and independently qualify correction0006 and the negative controls.

    diff --git a/rust/src/runtime/transaction.rs b/rust/src/runtime/transaction.rs
    index 8d7f1466..6ed9ebb6 100644
    --- a/rust/src/runtime/transaction.rs
    +++ b/rust/src/runtime/transaction.rs
    @@ -23,10 +23,6 @@ pub(super) struct Transaction<'a, DB: Backend> {
         /// has committed or rolled back (`Store::begin`).
         pub(super) queued: Option<tokio::sync::OwnedMutexGuard<()>>,
         summaries: BTreeSet<String>,
    -    /// The runs whose waits this transaction opened or reopened. Ordinals
    -    /// rank a run's waits by when each opened, and only all of a run's waits
    -    /// are ever deleted, so no other change moves one (`Store::order_waits`).
    -    wait_order: BTreeSet<String>,
         pub(super) history_budget: super::history_budget::Config,
         /// A workflow task completion's history meter (Workflow #930).
         pub(super) meter: Option<super::history_meter::Meter>,
    @@ -59,7 +55,6 @@ impl<'a, DB: Backend> Transaction<'a, DB> {
                 inner,
                 queued: None,
                 summaries: BTreeSet::new(),
    -            wait_order: BTreeSet::new(),
                 history_budget,
                 meter: None,
                 scope_history: None,
    @@ -76,10 +71,6 @@ impl<'a, DB: Backend> Transaction<'a, DB> {
             self.summaries.insert(run_id.to_owned());
         }
     
    -    pub(super) fn wait_order_changed(&mut self, run_id: &str) {
    -        self.wait_order.insert(run_id.to_owned());
    -    }
    -
         /// Forget every history read this transaction keeps: its rows were
         /// changed in place or deleted, so a later read starts over.
         pub(super) fn forget_history(&mut self) {
    @@ -153,9 +144,7 @@ where
             let written = std::time::Instant::now();
             super::bench_trace::phase("local writes", written - started);
             for run_id in std::mem::take(&mut self.summaries) {
    -            if self.wait_order.contains(&run_id) {
    -                Store::<DB>::order_waits(&mut self, &run_id).await?;
    -            }
    +            Store::<DB>::order_waits(&mut self, &run_id).await?;
                 Store::<DB>::project_run_summary(&mut self, &run_id).await?;
             }
             let projected = std::time::Instant::now();
    diff --git a/rust/src/runtime/wait_projection.rs b/rust/src/runtime/wait_projection.rs
    index fb617ef1..de82afef 100644
    --- a/rust/src/runtime/wait_projection.rs
    +++ b/rust/src/runtime/wait_projection.rs
    @@ -20,8 +20,7 @@ where
     {
         /// Store each wait's chronological ordinal over all of the run's retained
         /// waits, as Workflow #941's projector does, writing only waits that moved.
    -    /// Resolving a wait moves nothing, so a transition that opened no wait
    -    /// does not rank them (`Transaction::wait_order_changed`).
    +    /// Resolving a wait moves nothing, so most transitions write no positions.
         pub(super) async fn order_waits(tx: &mut Transaction<'_, DB>, run: &str) -> Result<()> {
             let mut moved = std::collections::HashSet::new();
             loop {
    @@ -75,7 +74,6 @@ where
                     .bind(projection_id(run, &wait)).bind(wait).bind(sequence - 1).bind(format!("Waiting for update {name}."))
                     .bind(DB::bind_time(at)).bind(name).bind(update).bind(super::text(payload, "workflow_command_id")?)
                     .bind(sequence).bind(DB::bind_time(at)).bind(DB::bind_time(at)).bind(run).execute(&mut **tx).await?;
    -            tx.wait_order_changed(run);
             } else {
                 let failed = payload["failure_id"]
                     .as_str()
    @@ -124,7 +122,6 @@ where
                     .bind(DB::bind_time(at)).bind(super::text(payload,"child_workflow_instance_id")?).bind(label)
                     .bind(super::text(payload,"child_workflow_run_id")?).bind(DB::bind_time(at)).bind(DB::bind_time(at)).bind(run_id)
                     .execute(&mut **tx).await?;
    -            tx.wait_order_changed(run_id);
             } else {
                 // `ChildRunHistory::waitSnapshotForSequence`: a cancelled or
                 // terminated child ends the wait as cancelled.
    @@ -190,7 +187,6 @@ where
                 .bind(format!("Waiting for activity {activity_type}."))
                 .bind(DB::bind_time(at)).bind(activity_type).bind(activity_id).bind(task_id)
                 .bind(DB::bind_time(at)).bind(DB::bind_time(at)).execute(&mut **tx).await?;
    -        tx.wait_order_changed(run_id);
             Ok(())
         }
     
    @@ -226,7 +222,6 @@ where
             } else if event_type == "ActivityStarted" {
                 Self::query("UPDATE workflow_run_waits SET source_status='running',task_status='leased',opened_at=$1,updated_at=$2 WHERE workflow_run_id=$3 AND wait_id=$4 AND status='open'")
                     .bind(DB::bind_time(at)).bind(DB::bind_time(at)).bind(run_id).bind(wait_id).execute(&mut **tx).await?;
    -            tx.wait_order_changed(run_id);
             } else {
                 let activity_type = super::text(payload, "activity_type")?;
                 let (status, source_status, summary) = match event_type {
    @@ -272,7 +267,6 @@ where
                 .bind(DB::bind_time(at)).bind(DB::bind_time(fire_at)).bind(timer_id).bind(task_id)
                 .bind(DB::bind_time(at)).bind(DB::bind_time(at)).bind(run_id)
                 .execute(&mut **tx).await?;
    -        tx.wait_order_changed(run_id);
             Ok(())
         }
     
    @@ -330,7 +324,6 @@ where
                     .bind(summary)
                     .bind(DB::bind_time(at)).bind(key).bind(DB::bind_time(at)).bind(DB::bind_time(at))
                     .bind(run_id).execute(&mut **tx).await?;
    -            tx.wait_order_changed(run_id);
             } else if event_type == "ConditionWaitSatisfied" {
                 // Receipt alone is not satisfaction. Only the worker's committed
                 // application event resolves this occurrence, without read repair.
    @@ -394,7 +387,6 @@ where
                     .bind(summary)
                     .bind(DB::bind_time(at)).bind(name).bind(DB::bind_time(at)).bind(DB::bind_time(at))
                     .bind(run_id).execute(&mut **tx).await?;
    -            tx.wait_order_changed(run_id);
             } else if payload["timer_kind"] == "signal_timeout"
                 && matches!(
                     event_type,
    diff --git a/rust/tests/execution/activity_results.rs b/rust/tests/execution/activity_results.rs
    index 096ff29b..02182d36 100644
    --- a/rust/tests/execution/activity_results.rs
    +++ b/rust/tests/execution/activity_results.rs
    @@ -460,11 +460,9 @@ fn assert_snapshot_is_row(snapshot: &Value, row: &Value) {
         for field in ["started_at", "closed_at", "last_heartbeat_at"] {
             assert_eq!(snapshot_time(&snapshot[field]), snapshot_time(&row[field]), "{field}: {snapshot} {row}");
         }
    -    assert_eq!(
    -        snapshot_time(&snapshot["schedule_to_close_deadline_at"]),
    -        snapshot_time(&row["schedule_to_close_deadline_at"]),
    -        "{snapshot} {row}"
    -    );
    +    // `ActivitySnapshot::fromExecution` names the lifetime only of an
    +    // abandoned activity, a policy native scheduling refuses.
    +    assert!(snapshot.get("schedule_to_close_deadline_at").is_none(), "{snapshot}");
     }
     
     // Scheduling, claiming and completing an activity each write its row and
    @@ -483,7 +481,7 @@ async fn an_activity_snapshot_is_its_row_as_each_transition_left_it() {
             &workflow,
             json!([{"type":"schedule_activity","activity_type":"snapshot-activity",
                 "arguments":envelope(Payload::String("界".into())),
    -            "retry_policy":{"max_attempts":3,"backoff_seconds":[1,2]},"cancellation_policy":"abandon",
    +            "retry_policy":{"max_attempts":3,"backoff_seconds":[1,2]},
                 "start_to_close_timeout":120,"schedule_to_start_timeout":60,"schedule_to_close_timeout":600,"heartbeat_timeout":30}]),
         )
         .await;
    @@ -493,7 +491,6 @@ async fn an_activity_snapshot_is_its_row_as_each_transition_left_it() {
         };
         let history = run_history(&app, &started["workflow_id"], &started["run_id"]).await;
         let scheduled = snapshot(&history, "ActivityScheduled");
    -    assert!(scheduled["schedule_to_close_deadline_at"].is_string(), "{scheduled}");
         let id = scheduled["id"].as_str().unwrap().to_owned();
         assert_snapshot_is_row(&scheduled, &activity_row(&database, &id).await);
         let task = poll(&app, "snapshot-worker", "activity").await;
  6. rmcdaniel commented on Nov 6, 2026

    @rmcdaniel
    MemberAuthor

    Intake is filed as P0, owner server. Shared index, allocation and static review findings.

    Complete sanitized diff reference(s): 0005 part 1/3, 0005 part 2/3, 0005 part 3/3, 0006 part 1/1.

    Shared Lockbox bundle: lab4/general/dw-performance-intake/2026-11-06/sanitized-tricks-20-25.zip (101,306 bytes; SHA-256 3e5b593ed89536451be1c8d74abf2ee6e4782cd3f7a75fe5fc9a645d648a52da). Its roundtrip and member hashes are verified. The supplied patches and benchmark results remain external reference data; no patch was applied or executed during intake. Existing issue acceptance and subsequent owner review remain in force.

  7. rmcdaniel commented on Nov 7, 2026

    @rmcdaniel
    MemberAuthor

    Security static reconciliation for the sanitized 0006 reference, reviewed as data only:

    The activity-snapshot test change is a legitimate correction, not a maintained-policy weakening.

    • Maintained Server source at c3bbfa5f explicitly refuses schedule_activity.cancellation_policy=abandon with installed_runtime_activity_policy; native has no post-run authority for an abandoned activity.
    • Maintained Workflow source at 57e87238df33369a759099c1fb298a1246749e41, Workflow\V2\Support\ActivitySnapshot::fromExecution, emits schedule_to_close_deadline_at only when the activity cancellation policy is exactly abandon.
    • Therefore the 0005 test's abandoned activity was outside the admitted native policy, and its assertion that the snapshot must contain that deadline was not a valid maintained-baseline requirement. 0006 correctly removes the unsupported policy and expects the field to be absent.

    Keep the independent production refusal for abandon, and retain meaningful row/snapshot equality for fields that are part of admitted policies. Do not add native abandon admission merely to preserve the external reference test. This conclusion does not accept the cumulative 0005 source, its benchmark claims, or its known stale wait-ranking shortcut; the full 0006 wait-order correction and backend controls remain required.

    No supplied patch was applied, executed, compiled or benchmarked.

  8. rmcdaniel commented on Nov 8, 2026

    @rmcdaniel
    MemberAuthor

    Operator nudge: this benchmax ticket (P0) has had no linked PR and no activity for about 27 h. Please pick it up next, after the current work on #1057. Suggested order for the stalled server tickets: #1063, #1068, #1075, #1077, #1078, #1079, then #1081 (P2); this one is 4 of 7. Open a draft PR as soon as work starts so the tracker shows it. If it is blocked, add the status:blocked label with a one-line reason.

  9. rmcdaniel commented on Nov 9, 2026

    @rmcdaniel
    MemberAuthor

    General started the bounded #1077 source slice in draft #1115. The old successful-completion read is retained only as the test reference. Production uses the already-fenced activity row plus the exact written status/exception/close time; ordinary snapshot callers keep their read/decode order. Source 4613b8a/tree a9c6abef on actual main 5bde7ed; delta 14,980 B / 1d15e651838d783ed2c7c2da1686a5f61465364bb9d71728f2abcf56cb5503e0. All four database families use the same new named control via the existing source suites. First compile/test is hosted; no run, suite or benchmark PASS is claimed yet. Other cuts, wait-order controls and original WIP refs stay intact.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:generalcompletion:evidence-requiredClose only after all explicit acceptance and operational evidence is publickind:featureA public product capability or experience is requestedpriority:P0Immediate release or data-integrity riskrepo:serverOwned by the standalone server repository

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions