---
title: Re-plan the Scott Fish Bowl 2026 scoring format task
type: task
status: completed
description: >-
  The SFB16 plan was written against the pre-registry codebase: its index rebuild, SCORING_COLUMNS
  append, calculate-points line anchors and spec filename are all stale, and its bonuses jsonb needs
  a stated resolution against config_digest.
base_uri: user:task/league/replan-scott-fish-bowl-2026-scoring-format.md
created_at: '2026-08-05T02:09:09.912Z'
entity_id: 2a9b92b9-c8ee-46c8-b8a5-ac7149179140
finished_at: '2026-08-05T17:19:53.515Z'
owner_identity_uri: █████████████████████████
priority: Medium
public_read: true
relations:
  - blocks [[████████████████████-█████-████-████-████-███████-█████████]]
  - relates [[█████████████████████-███████-███-███-███████-███████████████]]
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-08-05T17:19:53.515Z'
visibility_analyzed_at: '2026-11-05T23:50:10.225Z'
---

# Re-plan the Scott Fish Bowl 2026 scoring format task

[[████████████████████-█████-████-████-████-███████-█████████]] was written against a codebase that no longer exists. Its blocker and shared-surface observations are already recorded, so notification is done — what remains is real re-planning.

## Why its plan cannot be implemented as written

Four of its steps are stale or actively wrong now that the scoring registry and `config_digest` have landed:

- **Its Phase 1 rebuilds `league_scoring_formats_config_unique` to add three columns.** That constraint is dropped. The tuple reached 44 columns against a Postgres `max_index_keys` of 32, so a full-tuple index is no longer buildable at all. Dedup is `config_digest`, and the rebuild step must be deleted rather than adjusted.
- **Its `SCORING_COLUMNS` append is now a registry entry.** `libs-shared/scoring-columns.mjs` is the single source; `find-or-create-format.mjs` derives from it.
- **Its `calculate-points.mjs` line anchors are destroyed.** That function is now one loop over the registry; the hardcoded blocks its anchors pointed at are gone.
- **Its `test/calculate-points.spec.mjs` must not be created.** The dedicated spec exists as `test/libs-shared.calculate-points.spec.mjs`, matching the repo's flat `test/<area>.<name>.spec.mjs` convention. SFB16 extends it.

## The one genuinely new design question

Its `bonuses jsonb` rule list has to be folded into the `config_digest` expression, or two formats differing only in bonuses collide onto one id — silently, with no error anywhere.

`jsonb_out` is immutable so a generated column can reference it, but that is not sufficient: **jsonb key order is normalized while ARRAY order is not**, so two equal rule sets in different order produce different digests and mint duplicate format rows. This needs SFB16's deterministic array canonicalizer, and the canonicalizer has to run before the value is stored rather than inside the digest expression.

The `bonuses jsonb` shape itself remains right for open-ended rules and is complementary to typed columns for the closed kicking and DST sets. Nothing in the registry work replaces it.

## Scope

Re-planning only. Do not implement SFB16 from the re-planned document in the same pass — the re-plan should be reviewed first.

## Plan

- [x] **Delete the index-rebuild step from the SFB16 plan**
  - `league_scoring_formats_config_unique` no longer exists. Replace with a note that `config_digest` absorbs new columns automatically at the cost of a table rewrite.

- [x] **Convert its `SCORING_COLUMNS` append into registry entries**
  - One `libs-shared/scoring-columns.mjs` entry per new column, carrying `section`, `label`, `sql_type`, `default_value` and `input_type` so the settings UI and the API field lists derive for free.

- [x] **Re-anchor its `calculate-points.mjs` changes**
  - The hardcoded blocks its line numbers pointed at are gone. Express the change against the registry loop instead, and note whether any SFB rule is a genuine special case that must stay an explicit named branch.

- [x] **Point its spec work at the existing file**
  - `test/libs-shared.calculate-points.spec.mjs`. Do not create `test/calculate-points.spec.mjs`.

- [x] **Resolve `bonuses jsonb` against `config_digest`**
  - State how the rule list enters the digest expression, and where the deterministic array canonicalization runs. Two equal rule sets in different order must produce one digest.

- [x] **Check the position-aware receiving gap**
  - Carried over and still open: position-aware receiving is not enabled on the role-union path, so SFB-style formats diverge between the two scoring paths. SFB16 is the format class that exposes it.

## Verify

- The re-planned task names no dropped constraint and no stale line anchor.
- Every scoring column it adds is expressed as a registry entry.
- The digest/bonuses interaction has a stated resolution and a test that would catch a canonicalization regression.
