docs(specs): correct four S17 claims the merged code falsifies
Why
S17's Phase 4 delete contract states facts the merged code contradicts. This corrects eleven statements across seven sections, in three groups.
The attachment inventory stated a count that keeps going stale (2 statements)
The spec said eight tables carry the composite FK to blob_storage_attachments. The real number went seven, eight, nine, then ten in four days, most recently when container_remote_blobs landed, so correcting the digit would have shipped stale a third time. A check built from a list in the spec frees an attachment a cached remote row still uses.
The inventory now states the rule instead: every table carrying the FK participates in the check, internal/datastore/migrations/structure.sql is the source of truth for that set, and the guard arms derive from that inventory so a table added without one fails a test. Phase 4's own five delete targets stay enumerated, because that set is closed and this spec owns it. The code side widens the same check in chore(datastore): count every table that refere... (!1450 - merged) • Hayley Swimelar • 19.3. Its branch predates container_remote_blobs, so the predicate covers nine of the ten tables at head, and the structure.sql-derived pinning test it adds is what will demand the tenth on rebase.
A rejected request does cost one database round trip (3 statements)
The subset selector and the Bounded blast radius bullet said the body is validated before the namespace lookup, and the Input validation bullet said the same of name, the enums, and description. slugMiddleware in internal/managementapi/resolve.go wraps all 18 routes registered in internal/managementapi/handler.go and runs NamespaceStore.FindBySlug, one SELECT on namespaces, before next.ServeHTTP. Every planned bulk route registers behind the same wrapper, so the lookup precedes the body decode. All three now scope the claim to the access a handler controls.
The 1,000-tag cap binds per manifest, not per image (6 statements)
countTagsForManifest counts container_tags by container_manifest_id, and the persister checks it on any pointer change and skips only an idempotent re-tag. internal/config/container.go maps ADR-004's 25,000 versions per package onto manifests per image, so an image at those limits holds far more than 1,000 tags and one 1,000-entry batch cannot clear it. The per-image reading reached the subset-selector cap sentence, both tag-mutation sentences, the Resolutions reason, the acceptance criterion, and the Error Cases row. GitLab Duo caught the acceptance criterion, and verifying it turned up the Error Cases row.
The selector and duplicate-key text patched in chore(managementapi): add the bulk delete selec... (!1444 - merged) • Hayley Swimelar • 19.3 is untouched.
Test plan
Docs-only, no behavior change and no scenario catalog effect. markdownlint-cli2 reports 0 errors on the spec, and Vale reports 0 errors with 146 warnings, a finding set byte-identical to the same file on main.
Related to #583