Skip to content

Refuses unresolved providers at start_child_at - #273

Merged
johnnyt merged 1 commit into
mainfrom
sp-8ktk-start-child-refusal
Oct 13, 2026
Merged

johnnyt merged 1 commit into
mainfrom
sp-8ktk-start-child-refusal

Conversation

@johnnyt

@johnnyt johnnyt commented Oct 13, 2026

Copy link
Copy Markdown
Member

What

StatifierPersistence.Driver.start_child_at/6 refuses a driver whose function_providers: list does not resolve, at its entry, answering {:refused, {:function_providers, reason}} within its existing {:refused, reason} return shape. That start_child_at/6 refuses within its existing shape was ruled by the operator, 2026-10-13; resolve_and_answer_parent/3 is unchanged.

Before this, a driver whose list did not resolve could create and drive a fan-out child through this door. The child's automatic answer then reached answer_parent/3, which refuses such a list before it settles anything, and on the automatic path that refusal is discarded, so the invocation never settled and nothing told the host.

How

  • The door runs the same check the driver's other doors use (check_function_providers/2 in StatifierPersistence.Driver, over the driver's own list), first: before the parent's record is read, before any child is created and before anything is written. A thin wrapper (check_start_providers/1) turns that check's {:error, reason} into this door's {:refused, reason}; the reason term is the same.
  • The typespec names the member, {:refused, {:function_providers, Executions.function_providers_reason()} | term()}; the reason was already inside the open term(), so no caller's contract narrows or widens. The function's docs and Driver.new/3's option docs name the refusal, and the README's function_providers: paragraph says the fan-out door answers in its own shape.
  • The refusal emits no [:statifier_persistence, :child, :refused], as the door's :execution_not_found emits none: that event's reason is ADR-0008 decision 4's refusal set (docs/telemetry.md), and widening it is not this change.
  • ADR-0004's 2026-10-13 Amendment (still proposed) gains the door as a row in its door table, plus a dated paragraph under the table; git diff origin/main -- docs/adr/ removes no line.
  • changelog.d/sp-8ktk.md: one Added line, the host-facing sentence (only a driver with a list that does not resolve sees a change).

Tests

Two new tests in test/statifier_persistence/function_providers_test.exs, under "Driver.start_child_at/6":

  • "a list that does not resolve is refused before any child is created or written": the refusal for an invalid provider, and for a list that is not a list on a parent id that does not exist (the check runs ahead of the parent's read, so it answers the list's refusal, not :execution_not_found); no serialization exclusion asked for, the parent's record unchanged, no child execution at either index.
  • "a list that resolves starts the child as before".

The existing fan-out test files are unchanged.

Sabotage, each run against the new tests, the copy restored byte-equal after each:

  • dropped the entry check from start_child_at/6: the refusal test went red on its assertion (the door answered :ok and created the child);
  • made the wrapper refuse every list: the resolvable-list test went red on its assertion.

Gate

Full mix quality green locally on this tree (format, compile with warnings as errors, credo, docs, doc links, dependencies, dialyzer, tests at 96.0% coverage), against a local Postgres server on 5432.

Provenance

  • The README sentence and the Driver.new/3 option docs are not named in the bead; both listed the doors that refuse and would otherwise have read as complete without this one.
  • Not emitting the child-refused telemetry event for this refusal is an engineering choice taken in this change (2026-10-13); the ADR-0004 dated paragraph records it.

Driver.start_child_at/6 now checks the driver's function_providers:
list first, before the parent's record is read, any child is created
or anything is written, and answers {:refused, {:function_providers,
reason}} within its existing {:refused, reason} shape. Without it, a
driver whose list does not resolve could start a fan-out child whose
automatic answer the parent's door then refused, and that refusal is
discarded on the automatic path, so the invocation never settled.

The check is the one the driver's other doors use. The typespec names
the reason, the docs and README say so, ADR-0004's proposed Amendment
gains the door in its table by a dated edit, and a changelog fragment
carries the host-facing line. Ruled by the operator, 2026-10-13.

Refs: sp-8ktk
@johnnyt
johnnyt merged commit 9e625d1 into main Oct 13, 2026
1 check passed
@johnnyt
johnnyt deleted the sp-8ktk-start-child-refusal branch October 13, 2026 14:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant