Files
cgalo5758 b0df28647f Move organizations to a new default as obligations an act owns
A default change saved the new default outside any lock, then moved
every organization of the type inside the request, from a list read
after the save. At size the request ran out of time. A failure part way
left the rest unmoved, with "Details are in the server logs." as the
only answer and committing again as the only re-run (#187).

Migration 00028 gives the default change work it can own and resume:

- core.plan_act_reached_org_types records which types an act reaches.
  core.plan_act_owns_org_type states once that the newest act to reach
  a type owns it, so a newer act takes over what an older one has not
  moved.
- core.plan_act_obligations holds one row per organization the act must
  move. core.plan_act_orgs_without_pool records an organization with no
  default pool, which is logged and counted, not owed.
- core.plan_act_listing_frontier states once which organizations the
  listing has not reached: the writer, the listing-done read, the
  poller's read and the card's count all call it, and PostgreSQL
  inlines it into each.
- Four enclosed writers list, settle, record a failure (failed after
  five attempts) and requeue. core.record_plan_act now writes the
  reached types and refuses a reorder detail that disagrees with the
  ladder. core.grants.act_id names the act a grant came from.

The commit's first transaction runs on the runner under the exclusive
rendezvous, inside a 5 s deadline. It locks the type and refuses a
reserved type, a candidate that is already the default ("Already the
default.") and a ladder with no rank 0. It then saves the default and
records the act. With no disposition given, it rolls back when an
organization holds the outgoing default, and the 422 preview is rebuilt
after the rollback through the capped read. A listing then writes the
obligations in batches of 500, in org type and organization id order.

Each move runs in its own transaction under the shared rendezvous. It
locks the pool, locks the obligation together with its type's owner, and
reads the current default and its rank 0. It then migrates or
grandfathers the organization, settles it as already at the default, or,
under an act with no disposition, leaves it untouched with a Warn line.
A failure is recorded on the obligation alone with its step, in a plain
transaction that takes no pool row. The request tries up to 50
obligations within 2 s. Its lock and transaction timeouts are 500 ms and
2 s, and a move they cut records no attempt. It then starts
PlanActDrainWorkflow for the rest; PollPlanActs starts a drain for any
act still owing work. The toast counts what is not yet reached and what
has no default pool, and leaves out any count it could not read.

The org type's card shows each open act's lines: organizations not yet
reached, and those not moved, each with its reason, with Retry. They
are read in two statements in one read-only repeatable-read
transaction, and "Pending plan changes could not be read. Reference:
{request id}." replaces them when that read fails. Retry is an act of
its own: it requeues the failed obligations on the types the act still
owns, and a retried organization is recorded under the operator who
pressed Retry. It answers a lock wait with the collision sentence and
any other failure with "Failed to retry. Reference: {request id}.".

Along the way:

- The default change's sentences ending "Details are in the server
  logs.", in six places, now end "Reference: {request id}.", logged
  through the request's logger.
- The select's label becomes "New default", so the form library's
  refusal reads "Choose a new default from the list." and its required
  message "Choose a new default.".

The change's specs now carry that label for org-types and form-library.
The design states the frontier function and that the request's cap
counts obligations tried, not settled. docs/database-locks.md,
docs/plan-management.md and the two docs quoting the label follow the
code.

Goldens:

- Behaviour changes in contention, default-already-at, default-migrate,
  default-none-migrate, default-needs-disposition, default-begin-fails,
  default-initiate-fails, default-migrate-restore-fails,
  default-grandfather, default-change-twice, default-current-refused,
  default-save-fails, the four removal-against-default files and the
  four tier-change-during-conferral files.
- The three default form goldens move with the label. routes.golden
  gains the Retry route's probes.
- default-list-fails is retired: no listing runs outside the act any
  more. New: default-listing-fails, default-superseded,
  default-request-cap, default-untouched-outgoing, default-retry,
  default-retry-nothing, default-preview-capped and
  default-gate-true-preview-empty.
- The other 40 plan-act goldens gain only the reached-type,
  without-pool and obligation sections and the grants' act column.

The full suite passes.
2026-10-19 19:54:16 -04:00
..