Skip to content

Commit 6a768ff

Browse files
committed
docs(automation): declare the stranded-row carve-out on sys_automation_run's node_id and variables_json
The durable row's own declarations now agree with their writer: on the one terminal-row class that carries a consumed suspension, node_id is the PAUSED node (the Runs surface titles and highlights the row with it), and variables_json is either the restorable snapshot or the store's drop notice, which is not one. Same carve-out pattern node_type's description already carried. The changeset names the visible Runs-surface change. Source-only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
1 parent 7308e03 commit 6a768ff

2 files changed

Lines changed: 24 additions & 5 deletions

File tree

.changeset/stranded-run-status-stamp.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,13 @@ repairs to how `restoreConsumedSuspension` finds a stranded run's snapshot:
3737
object store read that column back as the snapshot's node, so a restore
3838
from the row (after a restart, or on another replica) re-armed the run at
3939
the failed node and the next resume skipped it while reporting the run
40-
completed. The throwing node stays where the Runs surface reads it: the
41-
row's step log and `error`.
40+
completed. The throwing node stays in the row's step log and `error`.
41+
Visible on the Runs surface: `sys_automation_run`'s row title and highlight
42+
set are built from `node_id` (`titleFormat '{flow_name} · {node_id}'`), so a
43+
stranded run's row now names the PAUSED node — the one an operator can
44+
re-arm — where it named the node that threw; ordinary completed / failed
45+
rows are unchanged. The `node_id` and `variables_json` field descriptions
46+
carry this carve-out, the way `node_type`'s already did.
4247
- The verb reads the durable row and its own per-process journal as two
4348
witnesses of one strand instead of trusting either alone. The hot copy is
4449
preferred when both describe the same pause (it is the verbatim object the

packages/services/service-automation/src/sys-automation-run.object.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,20 @@ export const SysAutomationRun = ObjectSchema.create({
113113

114114
flow_version: Field.number({ label: 'Flow Version', required: false, group: 'Identity' }),
115115

116+
// [#13937] The stranded-class carve-out in this description mirrors
117+
// `node_type`'s below, for the same reason: the restore verb re-arms the
118+
// pause from this column (`ObjectStoreSuspendedRunStore` rebuilds the
119+
// consumed snapshot's node from it — there is no other column), so on that
120+
// one terminal-row class the writer (`AutomationEngine.recordLog`) puts
121+
// the PAUSED node here, not the last step. This column is also what the
122+
// Runs surface titles and highlights a row with (`titleFormat` /
123+
// `highlightFields` above), so the carve-out is visible there too, by
124+
// design: the row names the pause an operator can re-arm.
116125
node_id: Field.text({
117126
label: 'Node',
118127
required: false,
119128
maxLength: 255,
120-
description: 'For a suspended run, the node it is paused at (resume continues from its out-edges); for a terminal run, the last node reached.',
129+
description: 'For a suspended run, the node it is paused at (resume continues from its out-edges); for a terminal run, the last node reached — except the one class of terminal row that carries a consumed suspension (a run whose resume consumed its pause and then failed downstream, answered `status: \'stranded\'`), which keeps the PAUSED node so a restore re-arms the pause rather than the node that threw. On that row the last node reached is the final entry of steps_json and the failure is in error; the Runs surface titles the row with this column, so a stranded run reads as its pause node there.',
121130
group: 'State',
122131
}),
123132

@@ -235,11 +244,16 @@ export const SysAutomationRun = ObjectSchema.create({
235244

236245
// [#13909] The presence-discriminator named in this description lives in
237246
// ObjectStoreSuspendedRunStore.deserializeConsumedSuspension — one writer,
238-
// one reader, this column is the key for both.
247+
// one reader, this column is the key for both. [#13937] The same column
248+
// carries the store's drop notice when the snapshot was over its row
249+
// budget (`$consumedSuspensionDropped`, read back as
250+
// `RunRecord.consumedSuspensionDropped`), so "present" now has two shapes
251+
// and only one of them is a restorable snapshot — stated in the
252+
// description rather than left to the reader of the column.
239253
variables_json: Field.textarea({
240254
label: 'Variables',
241255
required: false,
242-
description: 'JSON snapshot of the flow variable map at suspend time. On a terminal row its PRESENCE is the discriminator: nothing but the consumed-suspension path writes it there, so variables_json present on a completed/failed row ⇔ the row carries a restorable suspension — the store\'s deserializer keys off exactly this.',
256+
description: 'JSON snapshot of the flow variable map at suspend time. On a terminal row its PRESENCE is the discriminator: nothing but the consumed-suspension path writes it there, so variables_json present on a completed/failed row ⇔ the row\'s run had a pause that its resume consumed before a downstream node failed — the store\'s deserializer keys off exactly this. Two shapes on such a row: the snapshot itself (a restorable suspension), or a one-key notice `{"$consumedSuspensionDropped": …}` recording that the snapshot existed and was NOT persisted (over the store\'s row budget) — the notice is not a restorable snapshot; such a run can be restored only by the process that stranded it, while it runs.',
243257
group: 'State',
244258
}),
245259

0 commit comments

Comments
 (0)