Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
# bitpacker-hardening — Zod surface, OTel, property tests, benchmarks
**Packages:** `@suaveplan/bitpacker`, `@suaveplan/bitpacker-strategies`, `@suaveplan/bitpacker-validation`, `@suaveplan/bitpacker-game`, `@suaveplan/bitpacker-streaming`, `@suaveplan/bitpacker-compressed`, `@suaveplan/bitpacker-network`, `@suaveplan/bitpacker-schema`, `@suaveplan/bitpacker-delta`
**Status:** PROPOSED
**Type:** hardening (no behaviour changes; no public-surface breaks)
**Carved from:** `bitpacker-full-port` triage on 2026-05-03
**Scope decisions baked in (2026-05-03):**
- Feature-flags integration is **DROPPED** from this change. Every policy choice in the bitpacker family is already JSON-configurable (compression algorithm name + options object, network simulation profile, named adapter registration, registry / storage adapter selection). Feature-flags would only add value for runtime per-user / per-tenant rollout, which is not a legitimate concern for deterministic codecs. If a future risky primitive arrives, gate it at adapter-registration time on the consumer's bootstrap, not via runtime flags.
## Why
The bitpacker family is functionally complete and at 100/100/100/100 coverage across every package. Six hardening items from the original `bitpacker-full-port` proposal remain genuinely undone — they would have been done as part of the full port but the modular work prioritised behaviour over instrumentation. Each is small in isolation; bundled together they amount to one focused hardening pass that makes the family enterprise-ready by genesis policy.
None of these items change any wire format, public API shape, or behaviour. Every item is additive instrumentation or test infrastructure.
## What changes
### 1. Zod schemas for every public type
Every public input/output type across the family gets a Zod schema. Today only `bitpacker-crypto`, `bitpacker-delta`, and `bitpacker-schema` have them; `bitpacker` core, `bitpacker-strategies`, `bitpacker-validation`, `bitpacker-game`, `bitpacker-streaming`, `bitpacker-compressed`, and `bitpacker-network` do not.
- ADDED: per-package `*.schema.ts` modules co-located with the implementation file (`schema-name.ts` → `schema-name.schema.ts` + `schema-name.schema.md`).
- ADDED: `validate*` helpers around boundaries that today accept `unknown` from external callers (`pack(data: unknown, schema: BitPackerSchema)`, registry `register(adapter)`, `unpack` input bytes).
- ENFORCED: input validation runs only at package boundaries, not internal recursion (per CLAUDE.md "validate at boundaries only").
### 2. Register Zod schemas with `@suaveplan/schema-registry`
Once the Zod schemas exist, register the canonical wire-format schemas with `@suaveplan/schema-registry` so cross-version consumers can negotiate.
- ADDED: per-package `register-schemas.ts` entry that contributes its public schemas to the registry under namespace `"bitpacker"` with the package name as the schema name and `version: 1`.
- ADDED: `tests/registered.test.ts` per package asserting the schemas are present after `import "@suaveplan/<pkg>/register-schemas"`.
### 3. OTel instrumentation across core
Only `bitpacker-crypto` has OTel spans today. The bitpacker core, strategy chain, and the four runtime packages (`-streaming`, `-compressed`, `-network`, `-game`) get spans + counters + histograms via `@suaveplan/opentelemetry`.
- ADDED: per-package OTel module (`<pkg>/src/otel/instruments.ts`) declaring `Counter`, `Histogram`, and span names.
- ADDED: hot-path emission at pack/unpack boundaries (one span per top-level call; per-chunk events nested inside).
- ASSERTED: emission is verified with a real `MeterProvider` from `@suaveplan/testing` — never `vi.mock`.
### 4. `fast-check` property tests
The family today verifies behaviour with concrete examples. Property tests close the gap on round-trip soundness for every schema kind.
- ADDED: `fast-check` as a `devDependency` on every bitpacker package.
- ADDED: per-package `tests/property/*.property.test.ts` files exercising round-trip pack/unpack against generated input.
- ADDED: arbitrary generators for each schema kind in `@suaveplan/testing/bitpacker-arbitraries` so tests across packages share the same generator surface.
### 5. `mitata` benchmark suite + CI regression gate
Add steady-state benchmarks for every public encoder/decoder, with baselines committed to the repo.
- ADDED: `mitata` as a `devDependency` on every bitpacker package.
- ADDED: per-package `bench/*.bench.ts` files with `bench(name, fn)` per public operation.
- ADDED: `scripts/bench/bitpacker-baselines.json` baseline file.
- ADDED: CI gate (Turbo task `bench:check`) that fails if any benchmark regresses by more than 10% from baseline.
### 6. `@suaveplan/feature-flags` integration — DROPPED
Resolved 2026-05-03 (see scope decisions at top of proposal). Every policy choice in the bitpacker family is already configurable via plain JSON option bundles or named-adapter registration; runtime per-user / per-tenant rollout is not a legitimate concern for deterministic codecs. If a future risky primitive arrives, gate it at adapter-registration time at the consumer's bootstrap rather than via runtime flag evaluation. No code change in this section.
## Out of scope
- 47 source files in the bitpacker family lack a sibling `.md` (bitpacker 30, bitpacker-validation 9, bitpacker-game 7, bitpacker-crypto 1). Documentation pass tracked as a separate `bitpacker-doc-pass` change.
- Wire-format-version forward-compat semantics for `_version` strategy. Today the strategy enforces version match; "tolerate newer version with default-ed missing fields" is a feature decision, not hardening. Tracked separately as a future `bitpacker-version-tolerance` change.
- Tree-shake assertion test on the rewritten `index.ts`. Vite already verifies tree-shaking via dts-only outputs; an explicit assertion adds little value.
## Source mapping
This change derives entirely from the unchecked items in `openspec/changes/bitpacker-full-port/tasks.md` Phase 3 (the "Enterprise Hardening" section). No upstream source lines — every item is an above-baseline genesis-policy addition.
| Requirement | Source | Notes |
|---|---|---|
| Zod schemas for every public type | `bitpacker-full-port/tasks.md` Phase 3 #2 | Above-baseline; genesis policy |
| Register with schema-registry | `bitpacker-full-port/tasks.md` Phase 3 #3 | Above-baseline; genesis policy |
| OTel across core | `bitpacker-full-port/tasks.md` Phase 3 #5 | Above-baseline; genesis policy |
| fast-check property tests | `bitpacker-full-port/tasks.md` Phase 3 #8 | Above-baseline; genesis policy |
| mitata benchmark suite + gate | `bitpacker-full-port/tasks.md` Phase 3 #9-10 | Above-baseline; genesis policy |
| feature-flags integration | `bitpacker-full-port/tasks.md` Phase 3 #11 | DROPPED — JSON-configurable boundaries cover every legitimate need |
## Verification approach
- Per-package `tests/registered.test.ts` for the schema-registry items.
- Per-package OTel emission tests using a real `MeterProvider` from `@suaveplan/testing`.
- Per-package `tests/property/*.property.test.ts` files using `fast-check`.
- Per-package `bench/*.bench.ts` files with `mitata` baselines committed.
- `bunx turbo bench:check --filter=...@suaveplan/bitpacker*` as the CI gate.
- The 100/100/100/100 coverage gate continues to apply at package level.
## Migration impact
None — additive only. No public surface changes; no behaviour changes; no consumer is affected. Every change here is instrumentation or test infrastructure.
## Estimated scope
- ~30-40 new files (one Zod schema module + one OTel module + one property-test file + one bench file per package)
- ~2-3 new dependencies per package (`fast-check`, `mitata`, possibly `@suaveplan/opentelemetry` if not already wired)
- Zero source-file modifications outside of the new modules
- One new Turbo task (`bench:check`)
- One CI workflow update
## Migration
Phase 8 introduces a single behaviour-affecting addition to `@suaveplan/bitpacker-strategies`:
### `_version` strategy now accepts optional `_tolerance` and `_allowedVersions` fields
**Old behaviour:** `{ _version: number; _schema: BitPackerSchema }` — `unpack` throws `BitpackerError(INVALID_SCHEMA)` on any wire-version mismatch.
**New behaviour:** Same default. New optional fields:
- `_tolerance?: "strict" | "forward"` — defaults to `"strict"`. Set to `"forward"` to accept alternate wire-versions on `unpack`.
- `_allowedVersions?: readonly number[]` — when paired with `_tolerance: "forward"`, restricts acceptance to the listed versions; otherwise any version `[0, 65535]` is accepted.
**Search command for downstream consumers:**
```bash
# Find all _version-strategy usages that may benefit from forward tolerance:
grep -rn "_version" packages/ apps/ --include="*.ts" | grep -v "/dist/\|/coverage/\|node_modules" | grep -E "_version:\s*[0-9]"
```
**Recommended replacement:** No replacement is needed for existing strict-mode consumers — the change is purely additive and the default behaviour is unchanged. Consumers that need to read newer-version payloads from a server they don't control can opt in by adding `_tolerance: "forward"` and (optionally) `_allowedVersions`.
**Failure-mode change:** When `_tolerance: "forward"` is set with `_allowedVersions`, the error message on rejection now includes the `allowedVersions` list in the error context. Consumers asserting on the old error message text (which mentioned only expected/received) must update their assertions.