Skip to content

Commit 73d219d

Browse files
committed
Refine read-path helper to augment status_details with inferred fields
1 parent 347754e commit 73d219d

1 file changed

Lines changed: 28 additions & 12 deletions

File tree

docs/exec-id-plan.md

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,33 @@ dependency in the read path.
5858
1. **Drop the duroxide dependency** — read solely from `df.nodes` (one
5959
`SELECT` of the instance's rows). Remove `list_executions` / the fabricated
6060
`execution_id` cross join.
61-
2. For each node, **override** the returned `status` (and `status_details`) to
62-
the *effective* value at read time, derived from ancestors:
63-
- **Leaf→root climb:** find the first ancestor whose `execution_id` is not a
64-
prefix of the node's. Ancestor `failed``skipped`; `running`/newer
65-
execution ⇒ `pending`; `completed` but branch/iteration not taken ⇒
66-
`skipped`; RACE loser ⇒ `cancelled`.
67-
- Use the IF decision and RACE winner recorded in `status_details`
68-
(Step 6) to know which branch/iteration is "taken".
69-
3. Children are reachable via `left_node`/`right_node`; the tree is immutable
61+
2. For each node, **leave `status` and the stored `execution_id` untouched**
62+
and instead **add inferred fields to the returned `status_details`**:
63+
- `inferred_status` — the *effective* state at read time (`skipped`,
64+
`cancelled`, `pending`, or the node's own physical status when no
65+
ancestor overrides it).
66+
- `inferred_status_from_ancestor_id` — the node whose state *determined*
67+
`inferred_status` (the first ancestor whose `execution_id` is not a
68+
prefix of this node's). Makes the derivation explainable per row.
69+
- We deliberately do NOT add `inferred_status_execution_id`: it is just the
70+
`execution_id` of `inferred_status_from_ancestor_id`'s row, so a reader
71+
can join to that node if needed.
72+
- Rationale: the read path **must not compete with the write path**. By
73+
augmenting (not overwriting) `status_details`, the helper never races the
74+
orchestration's fenced writes. (If we later materialize this at the DB
75+
level it would compete — defer that decision.)
76+
3. **Derivation (leaf→root climb):** find the first ancestor whose
77+
`execution_id` is not a prefix of the node's; that ancestor's state sets
78+
`inferred_status`. Ancestor `failed``skipped`; `running`/newer execution
79+
`pending`; `completed` but branch/iteration not taken ⇒ `skipped`; RACE
80+
loser ⇒ `cancelled`. Use the IF decision and RACE winner recorded in
81+
`status_details` (Step 6) to know which branch/iteration is "taken".
82+
4. Children are reachable via `left_node`/`right_node`; the tree is immutable
7083
after `df.start()`, so no parent column is required (optional safe
7184
denormalization only).
72-
- Output columns: surface `status_details` alongside `status`; the implicit
73-
states appear only here, never in the base table.
85+
- Output columns: `status` and `status_details` keep their stored values; the
86+
inferred fields live inside the returned `status_details` JSONB only, never in
87+
the base table.
7488

7589
## Step 4 — Document the `execution_id`
7690

@@ -88,7 +102,9 @@ dependency in the read path.
88102
execution) drawn dashed, with a clear note that the dashed ones exist only in
89103
`df.instance_nodes` output.
90104
- Document the inference helper used by `instance_nodes` (the ancestor-climb /
91-
derivation function): inputs, the prefix rule, and each case.
105+
derivation function): inputs, the prefix rule, each case, and that it emits
106+
`inferred_status` + `inferred_status_from_ancestor_id` into `status_details`
107+
rather than mutating the stored `status`/`execution_id`.
92108

93109
## Step 6 — RACE winner recording (small behavioral add)
94110

0 commit comments

Comments
 (0)