Vulnerabilities product storage

The storage representation was source-verified at d8b3b63f6cb689b0146e4c109ecb78e8b656d421 (2026-10-22). The measured producer is 727dcc45fb9faa361fb6480f082855fcd5fe3b58; its 2026-10-23 initial full-seed receipt records 24,935,978,687 candidate database bytes and M9 at 4.817880213750253 under the unchanged five-times limit. The qualification commands recheck this source contract.

Forward migration 036_product_selector_literal_continuation.sql and its adjacent source tests are qualified. The accepted initial 80-gate comparison also binds the complete native qualification. The complete delta/custody and post-delta proof also passed, including all product storage and its owned sequence exactly once in the unchanged whole-database limit. This isolated proof does not establish fresh-machine installation or ordinary configured OSV refresh.

The Vulnerabilities owner stores each exact product literal once in facts.product_key_dictionary. Current facts and consensus linksets reference its immutable internal product_id. The dictionary preserves the literal without trimming, case folding, parsing or replacing it with a checksum. Product IDs do not enter fact or revision hashes, event payloads, public product values or continuation cursors.

The physical payload homes remain facts.fact_current and consensus.issue_linkset. Their ordinary facts.fact_current_read and consensus.issue_linkset_read views reconstruct the complete original logical fields. They use caller permissions and row security; a caller also needs permission to read the dictionary. The views are read-only. Writers resolve and key-share the exact product before writing a dependent row; replay does not update an existing dictionary row to obtain its ID.

Linkset selector keys use the existing literal-product expressions. A database trigger derives them on every insertion or update, so a caller cannot install inconsistent selector values. Product allocation order never substitutes for literal COLLATE "C" ordering. The all-row vulnerability lookup also includes tombstones; its index obligation is separate from the live corpus-group index.

Within a selector’s current vulnerability group, continuation first seeks a bounded window of later literal products through the exact COLLATE "C" dictionary index and checks their linkset membership. A sparse window falls back to the complete group after the last emitted literal, so the window never truncates the result. The reader refuses a missing, invalid or changed literal index before reading; the existing owner API returns its unavailable response. Null or out-of-range SQL page limits are rejected. No cursor uses allocator order, and no additional stored representation is introduced.

Upgrade and admission

Both owner images must carry the matching code and forward migrations. Stop both old roles before an upgrade; old and new binaries must not serve concurrently. Migration 031_exact_product_storage.sql prepares the dictionary, physical tables, canonical views and durable conversion checkpoint. Migrations 032 through 035 supply the indexed read obligations. Migration 036 replaces the selector function through the ordinary startup migration chain. An older image cannot perform this conversion through a restart.

Startup runs the owner and eventing migrations, then awaits complete outbox conversion before provenance conversion and product conversion. Eventing retention stays registered after those storage cutovers; the early registration contains only its migrations and converter. Retiring the old event source earlier is not a promised net saving or transient-capacity bound. Existing images retain their embedded startup order until a matching forward image is built and admitted. Product conversion copies and independently verifies bounded windows of current facts and linksets. Verification includes every logical field, original timestamp and null value. A changed target invalidates a saved verification cursor. Unexpected incoming references, missing or altered indexes, inconsistent locators and changed read contracts refuse serving admission.

Cutover preserves the exact incoming provenance, binary-fact and identity-map references. It removes the temporary source tables only after complete verification, then restores the ordinary input-witness triggers. It does not rewrite revisions, events or corpus witnesses. Interrupted conversion resumes from its durable checkpoint. No standing old-format table or dual-write path remains after convergence. Startup builds the required deferred indexes before serving; it does not depend on an application-started notification to make admission possible.

The bounded orphan collector preserves referenced or key-share-held products and existing reader snapshots. It runs only after conversion completes and does not rotate corpus input witnesses for dictionary-only maintenance.

Accounting and qualification

Dictionary and identity-sequence bytes count once under the existing current-fact byte envelope. Logical fact rows retain their existing row allowance. Linkset locators and all supporting indexes count under their physical home. During conversion, capacity counts both physical copies and refuses admission; it cannot report only a smaller partially filled target. Whole-database bytes already include these homes and receive no second addition.

This representation changes no numeric budget, debt pin, retention window or other database owner. The full-seed footprint numerator includes every physical table, index, TOAST relation and sequence. The target remains five times the original complete seed body; a small storage fixture or a query plan does not establish that target.

Re-verify ProductStorageTests in the owner Persistence project, the real fact-writer, projector, selector and corpus tests in Facts, and the mirror, custody and endpoint tests. Use tools/scripts/test-targeted-xunit.ps1 with -BuildProjectReferences and retain actual run counts. The complete paired full-seed and delta receipts remain the acceptance boundary; source or isolated conversion tests do not establish shared-estate deployment.

For the conversion and bounded reader controls, run from the repository root:

pwsh tools/scripts/test-targeted-xunit.ps1 -Project src/Vulnerabilities/__Tests/StellaOps.Vulnerabilities.Persistence.Tests/StellaOps.Vulnerabilities.Persistence.Tests.csproj -Class '*ProductStorageTests*' -BuildProjectReferences -MaxThreads 1
pwsh tools/scripts/test-targeted-xunit.ps1 -Project src/Vulnerabilities/__Tests/StellaOps.Vulnerabilities.Facts.Tests/StellaOps.Vulnerabilities.Facts.Tests.csproj -Class '*ConsensusCorpusPagingTests*' -BuildProjectReferences -MaxThreads 1
pwsh tools/scripts/test-targeted-xunit.ps1 -Project src/Vulnerabilities/__Tests/StellaOps.Vulnerabilities.Facts.Tests/StellaOps.Vulnerabilities.Facts.Tests.csproj -Class '*IssueSelectorPagingTests*' -BuildProjectReferences -MaxThreads 1