---
title: Retire the bare-container JSDoc backlog in league
type: task
status: completed
description: >-
  Work the 1,135 bare-container JSDoc occurrences across 184 league files down toward zero, lowering
  each file's allowance in the ratchet baseline as it goes.
base_uri: user:task/league/retire-bare-container-jsdoc.md
created_at: '2026-08-19T21:49:33.650Z'
entity_id: 7f8a3b4c-f4be-469a-b770-2c82582b9794
finished_at: '2026-08-19T22:54:08.014Z'
observations:
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: Low
public_read: true
relations:
  - succeeds [[user:task/league/raise-league-testing-floor-stage-3.md]]
started_at: '2026-08-19T21:50:38.752Z'
tags:
  - user:tag/league-xo-football.md
  - ███████████████-██████-███████████
updated_at: '2026-08-19T23:49:37.304Z'
visibility_analyzed_at: '2026-11-05T23:50:14.069Z'
---

## Why

The `local/no-bare-container-jsdoc` rule landed at league `32f05d60f` as a ratchet against 1,135 pre-existing occurrences across 184 files. The ratchet holds the line — no NEW bare tag can enter — but it does not shrink the backlog on its own, and every one of those tags is a coverage hole that reads as documentation.

A bare `@param {Object}` is not vague, it is wrong: capital `Object` is the TypeScript `Object` interface, so a tag of that shape guarantees an error at every property access through it. `{Array}` / `{Map}` / `{Set}` / `{Promise}` are generics missing their arguments, so everything read through them is `any`. This was the single dominant blocker across all twelve producers adopted in Stage 3.

Lowercase `{object}` with `@param x.y` sub-tags is CORRECT and is deliberately not flagged. Do not "fix" it.

## Plan

- [x] Work files down in batches, lowering each file's allowance as it goes. Order by whatever is cheapest; there is no required sequence.
- [x] Regenerate the baseline after each batch (`node eslint-rules/generate-bare-container-jsdoc-baseline.mjs`) and confirm `yarn check:jsdoc-baseline` passes.
- [x] Keep `yarn lint`, `yarn check:types` and the suite green on every commit.

## How to work it

Find the worst offenders:

```
python3 -c "
import json
d=json.load(open('eslint-rules/bare-container-jsdoc-baseline.json'))
for f,n in sorted(d['allowances'].items(), key=lambda kv: -kv[1])[:20]: print(n, f)"
```

The four replacements, in order of how often they apply:

- `{Object}` as a params bag with `@param x.y` sub-tags beneath it -> lowercase `{object}`. This is the majority and is a one-character change.
- `{Object}` with no sub-tags, used as a real value -> a named `@typedef`, a `Record<string, T>`, or the generated row type from `db/schema-types.d.ts` if it is a database row.
- `{Array}` / `{Map}` / `{Set}` / `{Promise}` -> supply the arguments: `T[]`, `Map<K, V>`, `Set<T>`, `Promise<T>`.
- `{Function}` -> the signature `(a: A, b: B) => R`, or a named `@callback`.

Very often the real shape is already written in the PROSE beside the tag. Read it and move it into the type — `merge-player-projections.mjs` carried seven bare tags whose exact shapes were all described in the descriptions next to them.

## Boundaries

- Do NOT add `// @ts-check` to a file as part of this work. Adoption is a separate decision with its own gate (`db/gates/ts-check-adoption.json`) and its own diagnostic cost.
- Do NOT touch `private/` — it is a submodule CI never checks out, and the rule deliberately does not apply there.
- If a tag's real type is genuinely unclear, leave it and move on. A guessed annotation is worse than the bare one it replaces, because it looks settled.
- Do NOT deploy.

## Key locations

- Rule: `eslint-rules/no-bare-container-jsdoc.mjs`; detector `eslint-rules/bare-container-jsdoc.mjs`
- Baseline: `eslint-rules/bare-container-jsdoc-baseline.json` (generated)
- Gates: `yarn lint`, `yarn check:jsdoc-baseline`, `yarn check:types`
- Predecessor: [[user:task/league/raise-league-testing-floor-stage-3.md]]
