Initial Commit
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
# Enterprise hardening — `@suaveplan/packer` extensibility cluster
|
||||
|
||||
**Package**: `@suaveplan/packer`
|
||||
**Status**: PROPOSED
|
||||
**Type**: feature
|
||||
**Design**: (inline below — scope is well-bounded by the 5-item brief)
|
||||
**Spec delta**: ./specs/packer/spec.md
|
||||
|
||||
## Why
|
||||
|
||||
Cluster #1 (`enterprise-packer-integrity`, archived 2026-05-19) landed
|
||||
BLAKE3 content-addressable digests, the bundle-level Merkle root,
|
||||
mandatory integrity verification on unpack, full OpenTelemetry
|
||||
instrumentation, and i18n-keyed error messages. Cluster #2
|
||||
(`enterprise-packer-resilience`, archived 2026-05-19) added retry,
|
||||
idempotency, whole-file `AbortSignal`, and Clock / Rng injection.
|
||||
|
||||
This is the third and final cluster, addressing the remaining
|
||||
`enterprise/packer.md` audit gaps (L41, L42, L48, L72, L75):
|
||||
|
||||
1. No `@suaveplan/schema-registry` integration — the Zod option schemas
|
||||
exist in `src/schemas/packer.schemas.ts` but are not registered with
|
||||
the canonical versioned registry, and the chunk-format version byte
|
||||
has no companion registry entry that records `V1` as the shipping
|
||||
format and reserves the path to `V2`.
|
||||
2. No `@suaveplan/feature-flags` rollout surface — the audit calls for
|
||||
"Feature flags via `@suaveplan/feature-flags` for format-version
|
||||
rollouts" (L48); today the package writes V1 unconditionally and has
|
||||
no rollout path for a future V2 wire format.
|
||||
3. No plugin / extension surface — the audit calls for "A plugin /
|
||||
extension interface (where it makes sense) so downstream packages
|
||||
can contribute behaviour without forking" (L72). Today the packer is
|
||||
closed: every transform, integrity strategy, and codec is hard-wired.
|
||||
4. No `fast-check` property tests — the audit calls for "Property-based
|
||||
tests (fast-check) for pack/unpack round-trips and streaming-
|
||||
equivalence-to-batch invariants" (L42).
|
||||
5. No mitata benchmark suite — the audit calls for "Performance —
|
||||
benchmark suite in `src/__benchmarks__`, published numbers in the
|
||||
README, regression gate in CI" (L75).
|
||||
|
||||
After this cluster lands, `@suaveplan/packer` meets every checkbox in
|
||||
the `enterprise/packer.md` §3 + §4 + §6 acceptance criteria.
|
||||
|
||||
## What changes
|
||||
|
||||
- ADDED: `src/schemas/registration.ts` — registers every Zod schema in
|
||||
`src/schemas/packer.schemas.ts` plus a `packer-chunk-format` subject
|
||||
with V1 as the shipping wire-format version under a `BACKWARD`
|
||||
compatibility policy. `getPackerSchemaRegistry()` returns the
|
||||
process-singleton registry; `registerPackerSchemas(registry)`
|
||||
performs the registration against any caller-supplied
|
||||
`SchemaRegistry`.
|
||||
- ADDED: `@suaveplan/schema-registry` as a runtime dependency.
|
||||
- ADDED: `src/flags/flags.ts` — `PackerFeatureFlags` provider
|
||||
interface and `defaultPackerFlags()` no-op provider that returns the
|
||||
V1 default for every flag. `resolvePackerFlags(opts)` adapts a
|
||||
caller-supplied `@suaveplan/feature-flags` `Evaluator` or a raw
|
||||
provider into the internal `PackerFeatureFlags` shape.
|
||||
- ADDED: `flags?: PackerFlagsOptions` on `PackerOptions`. The write
|
||||
path now reads `packer.format.v2` from the resolved provider — when
|
||||
enabled, `Packer.pack` throws
|
||||
`PackerError(PACKER.FORMAT_VERSION_UNSUPPORTED)` with the catalog
|
||||
message `packer.error.FORMAT_VERSION_UNSUPPORTED.v2-not-implemented`
|
||||
(versioning gate — V2 is a future change, not a deferral).
|
||||
- ADDED: `@suaveplan/feature-flags` as a runtime dependency.
|
||||
- ADDED: `src/plugins/plugins.ts` — `PackerPlugin` contract, six
|
||||
lifecycle hooks (`onPackStart`, `onEntry`, `onPackEnd`,
|
||||
`onUnpackStart`, `onEntryDecoded`, `onUnpackEnd`),
|
||||
order-deterministic invocation via
|
||||
`PluginPipeline.runHook(name, ctx)`. Plugin errors surface as
|
||||
`PackerError(PACKER.PLUGIN_FAILED)`.
|
||||
- ADDED: `plugins?: ReadonlyArray<PackerPlugin>` on `PackerOptions`.
|
||||
`Packer.pack` and `Unpacker.unpack` invoke every registered plugin's
|
||||
hooks in declaration order.
|
||||
- ADDED: `PACKER.FORMAT_VERSION_UNSUPPORTED` and `PACKER.PLUGIN_FAILED`
|
||||
error codes in `error-codes.ts`.
|
||||
- ADDED: `src/__property__/round-trip.property.test.ts` and
|
||||
`src/__property__/determinism.property.test.ts` — `fast-check`
|
||||
property-based round-trip, streaming↔batch equivalence, determinism.
|
||||
- ADDED: `src/__benchmarks__/packer.bench.ts` — `mitata` benchmark
|
||||
suite covering whole-file pack/unpack, chunked pack (single + 1000
|
||||
chunks), integrity verify, plugin overhead. `bun run benchmark`
|
||||
script in `package.json` and a Benchmarks section in `README.md`.
|
||||
- ADDED: `mitata` as a devDependency (already present in the root
|
||||
catalog as `catalog:testing`).
|
||||
- MODIFIED: `src/index.ts` — exports `getPackerSchemaRegistry`,
|
||||
`registerPackerSchemas`, `PackerPlugin`, `defaultPackerFlags`,
|
||||
`resolvePackerFlags`, and the plugin / flags / registration types.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- V2 wire format implementation — this cluster lands the registry
|
||||
entry and the feature-flag gate; the V2 codec is a follow-up change
|
||||
that flips the throw into real encoding work. The throw is the spec
|
||||
contract for V2-not-implemented; not a deferral.
|
||||
- Mutation testing — orthogonal to the audit; opens a follow-up.
|
||||
- Codemod for breaking option changes — there are none in this
|
||||
cluster.
|
||||
|
||||
## Source mapping
|
||||
|
||||
This change derives from `../suaveplan/enterprise/packer.md`.
|
||||
|
||||
| Requirement | Source lines | Notes |
|
||||
|---|---|---|
|
||||
| PACKER-26 (`@suaveplan/schema-registry` integration) | L41 | Verbatim — "Zod schemas … `@suaveplan/schema-registry` integration, versioned evolution with explicit format-version bytes". |
|
||||
| PACKER-27 (`@suaveplan/feature-flags` rollouts) | L48, L79 | Verbatim — "Feature flags via `@suaveplan/feature-flags` for format-version rollouts" / "Feature flags for any risky new behaviour". |
|
||||
| PACKER-28 (plugin / extension interface) | L72 | Verbatim — "A plugin / extension interface (where it makes sense) so downstream packages can contribute behaviour without forking". |
|
||||
| PACKER-29 (`fast-check` property tests) | L42 | Verbatim — "Property-based tests (fast-check) for pack/unpack round-trips and streaming-equivalence-to-batch invariants". |
|
||||
| PACKER-30 (mitata benchmark suite) | L75 | Verbatim — "Performance — benchmark suite in `src/__benchmarks__`, published numbers in the README, regression gate in CI". |
|
||||
|
||||
### Requirements with no source line
|
||||
|
||||
None — every requirement maps directly to an audit bullet.
|
||||
|
||||
## Verification approach
|
||||
|
||||
- `src/schemas/registration.test.ts` + `.spec-conformance.test.ts` —
|
||||
pins registry contents, V1 evolution metadata, BACKWARD compatibility
|
||||
on subject reuse.
|
||||
- `src/flags/flags.test.ts` + `.spec-conformance.test.ts` — pins
|
||||
default no-op provider, custom evaluator wiring, V2 throw path.
|
||||
- `src/plugins/plugins.test.ts` + `.spec-conformance.test.ts` — pins
|
||||
hook ordering, error surface, downstream plugin example (noop logger
|
||||
+ double-integrity SHA-256 sidecar).
|
||||
- `src/__property__/round-trip.property.test.ts` — pack→unpack identity
|
||||
+ streaming↔batch equivalence under arbitrary inputs.
|
||||
- `src/__property__/determinism.property.test.ts` — deterministic
|
||||
output under seeded Rng + fixed Clock.
|
||||
- `src/__benchmarks__/packer.bench.ts` — runnable via
|
||||
`bun run benchmark`; baseline numbers published in `README.md`.
|
||||
|
||||
## Migration impact
|
||||
|
||||
Public-surface additions only. No existing signature changes shape —
|
||||
the new options are all optional with platform defaults. Two consumer
|
||||
packages (`@suaveplan/drive-plugin-chunks`, `@suaveplan/drive-webrtc`)
|
||||
are audited and the sweep is part of the gates; no existing call site
|
||||
changes shape.
|
||||
|
||||
## Impact
|
||||
|
||||
### Affected dependents
|
||||
|
||||
Run `bunx turbo ls --filter=...@suaveplan/packer` to enumerate.
|
||||
|
||||
- `@suaveplan/drive-plugin-chunks` — [x] tests run / [x] docs reviewed / [ ] no action needed
|
||||
- `@suaveplan/drive-webrtc` — [x] tests run / [x] docs reviewed / [ ] no action needed
|
||||
|
||||
If `tests run` is unchecked for any dependent, the change MUST NOT be
|
||||
folded into the canonical spec.
|
||||
|
||||
## Migration
|
||||
|
||||
No behavioral change for callers that did not pass the new options.
|
||||
Existing calls keep their previous semantics:
|
||||
|
||||
- **No `plugins` passed**: no hooks fire; behaviour identical to
|
||||
cluster #2.
|
||||
- **No `flags` passed**: `defaultPackerFlags()` is used; every flag
|
||||
returns the V1 default and `Packer.pack` writes the V1 format.
|
||||
- **No `registry` interaction needed**: callers that did not use
|
||||
`@suaveplan/schema-registry` before do not need to start now —
|
||||
registration is a process-singleton side effect available on demand.
|
||||
@@ -0,0 +1,225 @@
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
# Capability Spec Delta: packer (enterprise-packer-extensibility)
|
||||
|
||||
**Package**: `@suaveplan/packer`
|
||||
**Status**: PROPOSED
|
||||
|
||||
> Every requirement below carries a `**Verified by:**` line at authoring time. No exceptions.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: PACKER-26 — `@suaveplan/schema-registry` integration
|
||||
|
||||
The package SHALL register every Zod schema in
|
||||
`src/schemas/packer.schemas.ts` (`PackerOptionsSchema`,
|
||||
`UnpackOptionsSchema`, `ChunkedPackerOptionsSchema`, `ManifestSchema`)
|
||||
plus a `packer-chunk-format` subject capturing the wire-format version
|
||||
byte, into a process-singleton `SchemaRegistry` exposed via
|
||||
`getPackerSchemaRegistry()`. The `packer-chunk-format` subject MUST
|
||||
register V1 (the current wire format) under a `BACKWARD` compatibility
|
||||
policy with evolution metadata `{ formatVersion: "1", encoding:
|
||||
"binary", description: "v1 fixed-width 164-byte header + AEAD payload" }`
|
||||
so a future V2 entry can be added without disturbing V1. Every other
|
||||
schema MUST be registered under its own subject (`PackerOptions`,
|
||||
`UnpackOptions`, `ChunkedPackerOptions`, `Manifest`) carrying the
|
||||
metadata `{ formatVersion: "1" }`. A `registerPackerSchemas(registry)`
|
||||
function MUST allow callers to register the same schemas into any
|
||||
external `SchemaRegistry` instance for cross-package validation.
|
||||
|
||||
#### Scenario: getPackerSchemaRegistry returns a populated singleton
|
||||
|
||||
- **WHEN** `getPackerSchemaRegistry()` is called and then
|
||||
`registry.listSubjects()` is read
|
||||
- **THEN** the list contains exactly `PackerOptions`, `UnpackOptions`,
|
||||
`ChunkedPackerOptions`, `Manifest`, and `packer-chunk-format`
|
||||
|
||||
#### Scenario: packer-chunk-format records V1 with evolution metadata
|
||||
|
||||
- **WHEN** `registry.getLatest("packer-chunk-format")` is read
|
||||
- **THEN** `metadata.formatVersion === "1"`, `metadata.encoding ===
|
||||
"binary"`, and registering an identical V1 candidate succeeds under
|
||||
`BACKWARD` compatibility
|
||||
|
||||
#### Scenario: registerPackerSchemas writes into a caller-supplied registry
|
||||
|
||||
- **WHEN** `registerPackerSchemas(externalRegistry)` is called against
|
||||
a fresh `createRegistry()`
|
||||
- **THEN** `externalRegistry.listSubjects()` contains the same five
|
||||
subjects with `formatVersion: "1"` metadata, and no mutation to the
|
||||
global singleton has occurred
|
||||
|
||||
**Verified by:** `src/schemas/registration.test.ts::"registers every option schema as a subject with formatVersion 1 metadata"` AND `src/schemas/registration.test.ts::"records packer-chunk-format V1 with binary encoding and BACKWARD compatibility"` AND `src/schemas/registration.spec-conformance.test.ts::"getPackerSchemaRegistry returns a process-singleton populated with the canonical subjects"` AND `src/schemas/registration.spec-conformance.test.ts::"registerPackerSchemas writes into a caller-supplied registry without mutating the global singleton"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: PACKER-27 — `@suaveplan/feature-flags` for format-version rollouts
|
||||
|
||||
The package SHALL accept a `flags?: PackerFlagsOptions` bundle on
|
||||
`PackerOptions` and route the wire-format selection through the
|
||||
resolved `PackerFeatureFlags` provider. The exported constant
|
||||
`PACKER_FLAG_KEYS.FORMAT_V2 === "packer.format.v2"` MUST identify the
|
||||
V2 rollout flag. When no provider is supplied, `defaultPackerFlags()`
|
||||
MUST act as a no-op that returns the V1 default (`false`) for every
|
||||
flag. When the resolved provider reports `packer.format.v2 === true`,
|
||||
`Packer.pack` MUST throw
|
||||
`PackerError(PACKER.FORMAT_VERSION_UNSUPPORTED)` with the catalog
|
||||
message `packer.error.FORMAT_VERSION_UNSUPPORTED.v2-not-implemented`;
|
||||
this is a versioning gate — the V2 codec is a future change. A
|
||||
caller-supplied `@suaveplan/feature-flags` `Evaluator` MUST be
|
||||
acceptable as the provider via `resolvePackerFlags({ evaluator,
|
||||
context? })`.
|
||||
|
||||
#### Scenario: default flags resolve V2 to false and pack succeeds in V1
|
||||
|
||||
- **WHEN** `new Packer().pack(data)` is called with no `flags` option
|
||||
- **THEN** the call returns a `PackedData` whose `metadata` carries the
|
||||
V1 shape (no V2-specific fields), and `defaultPackerFlags().get("packer.format.v2") === false`
|
||||
|
||||
#### Scenario: enabling packer.format.v2 throws FORMAT_VERSION_UNSUPPORTED
|
||||
|
||||
- **WHEN** `Packer.pack(data)` is called with a `flags` provider whose
|
||||
`get("packer.format.v2")` returns `true`
|
||||
- **THEN** the call rejects with `PackerError(PACKER.FORMAT_VERSION_UNSUPPORTED)`
|
||||
and `error.code === "PACKER.FORMAT_VERSION_UNSUPPORTED"`
|
||||
|
||||
#### Scenario: a caller-supplied feature-flags Evaluator drives the gate
|
||||
|
||||
- **WHEN** `Packer.pack(data)` is called with `flags: { evaluator,
|
||||
context: { userId: "u-1" } }` where `evaluator` resolves
|
||||
`packer.format.v2` to `false` for `userId: "u-1"`
|
||||
- **THEN** the call returns a V1 bundle and the evaluator's internal
|
||||
counter records one evaluation for `flag_key: "packer.format.v2"`
|
||||
|
||||
**Verified by:** `src/flags/flags.test.ts::"defaultPackerFlags returns false for packer.format.v2"` AND `src/flags/flags.test.ts::"resolvePackerFlags adapts a feature-flags Evaluator into a PackerFeatureFlags provider"` AND `src/flags/flags.spec-conformance.test.ts::"Packer.pack throws PACKER.FORMAT_VERSION_UNSUPPORTED when packer.format.v2 is enabled"` AND `src/flags/flags.spec-conformance.test.ts::"Packer.pack succeeds in V1 when flags option is omitted"` AND `src/flags/flags.spec-conformance.test.ts::"a caller-supplied feature-flags Evaluator drives the format-version gate"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: PACKER-28 — Plugin / extension interface
|
||||
|
||||
The package SHALL expose a `PackerPlugin` contract carrying six
|
||||
lifecycle hooks (`onPackStart(ctx)`, `onEntry(ctx)`, `onPackEnd(ctx)`,
|
||||
`onUnpackStart(ctx)`, `onEntryDecoded(ctx)`, `onUnpackEnd(ctx)`), each
|
||||
optional and each invoked in plugin-declaration order. Plugins are
|
||||
registered via `plugins?: ReadonlyArray<PackerPlugin>` on
|
||||
`PackerOptions`. `Packer.pack` MUST invoke `onPackStart` exactly once
|
||||
before the pipeline, `onEntry` exactly once per entry packed (one for
|
||||
whole-file mode), and `onPackEnd` exactly once after the final result
|
||||
is composed. `Unpacker.unpack` MUST invoke `onUnpackStart` /
|
||||
`onEntryDecoded` / `onUnpackEnd` symmetrically. Any plugin error MUST
|
||||
be caught and re-thrown as
|
||||
`PackerError(PACKER.PLUGIN_FAILED)` with a catalog message
|
||||
`packer.error.PLUGIN_FAILED.<hook>` and the original error carried on
|
||||
`error.cause`.
|
||||
|
||||
#### Scenario: plugin hooks fire in declaration order
|
||||
|
||||
- **WHEN** `Packer.pack(data)` is called with two plugins whose
|
||||
`onPackStart` / `onEntry` / `onPackEnd` push their name onto a
|
||||
shared array
|
||||
- **THEN** the array equals `["A:start", "B:start", "A:entry",
|
||||
"B:entry", "A:end", "B:end"]`
|
||||
|
||||
#### Scenario: an unpack pass fires onUnpackStart / onEntryDecoded / onUnpackEnd
|
||||
|
||||
- **WHEN** `Unpacker.unpack(packed)` is called with a plugin that
|
||||
records each unpack hook
|
||||
- **THEN** the recorded order is `["unpackStart", "entryDecoded",
|
||||
"unpackEnd"]`
|
||||
|
||||
#### Scenario: a plugin error surfaces as PACKER.PLUGIN_FAILED
|
||||
|
||||
- **WHEN** `Packer.pack(data)` is called with a plugin whose
|
||||
`onEntry` throws `new Error("boom")`
|
||||
- **THEN** the call rejects with
|
||||
`PackerError(PACKER.PLUGIN_FAILED)`, `error.cause.message === "boom"`,
|
||||
and `error.message` is the resolved
|
||||
`packer.error.PLUGIN_FAILED.onEntry` catalog string
|
||||
|
||||
#### Scenario: a double-integrity sidecar plugin contributes SHA-256
|
||||
|
||||
- **WHEN** `Packer.pack(data)` is called with a plugin whose `onEntry`
|
||||
computes SHA-256 of the payload and stashes it on the plugin's
|
||||
internal map, then `Unpacker.unpack` is called with the same plugin
|
||||
whose `onEntryDecoded` reads back and asserts equality
|
||||
- **THEN** both calls succeed and the plugin observed identical SHA-256
|
||||
bytes on the way out and the way in
|
||||
|
||||
**Verified by:** `src/plugins/plugins.test.ts::"runs onPackStart / onEntry / onPackEnd in declaration order"` AND `src/plugins/plugins.test.ts::"runs onUnpackStart / onEntryDecoded / onUnpackEnd in declaration order"` AND `src/plugins/plugins.test.ts::"wraps thrown plugin errors as PackerError(PACKER.PLUGIN_FAILED) with cause"` AND `src/plugins/plugins.spec-conformance.test.ts::"a noop-logger plugin observes every lifecycle hook"` AND `src/plugins/plugins.spec-conformance.test.ts::"a double-integrity sidecar plugin contributes SHA-256 alongside BLAKE3"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: PACKER-29 — `fast-check` property-based round-trip + determinism
|
||||
|
||||
The package SHALL include `fast-check` property tests pinning the
|
||||
pack/unpack round-trip identity, streaming↔batch equivalence, and
|
||||
seeded-Rng determinism invariants. Property assertions MUST run over
|
||||
arbitrary `Uint8Array` inputs of length 0..1024 (at least 50 runs per
|
||||
property). Round-trip identity: for every `data`, `unpack(pack(data))`
|
||||
returns bytes byte-equal to `data`. Streaming equivalence: the
|
||||
concatenation of every `PackedChunk.bytes` emitted by
|
||||
`packChunks(asyncIter(data), { ...same fileKey/signKey/baseNonce })`
|
||||
MUST byte-equal the wire bytes of `packChunks(data, { ...same opts })`
|
||||
consumed from a whole-buffer source. Determinism: two `Packer.pack`
|
||||
runs with identical inputs, identical options, the same seeded `Rng`
|
||||
and the same fixed `Clock` MUST produce byte-identical `PackedData`
|
||||
(both `data` and every byte of `metadata.integrity`).
|
||||
|
||||
#### Scenario: round-trip identity over arbitrary Uint8Array inputs
|
||||
|
||||
- **WHEN** `fc.assert(fc.asyncProperty(fc.uint8Array({ maxLength: 1024 }), async (data) => { … pack/unpack … }))` runs
|
||||
- **THEN** every drawn `data` is recovered byte-identical from
|
||||
`unpack(pack(data))`
|
||||
|
||||
#### Scenario: streaming chunked pack matches whole-buffer chunked pack
|
||||
|
||||
- **WHEN** the same input is fed to `packChunks` once as a
|
||||
whole-buffer source and once as a 1-byte-at-a-time async-iterable
|
||||
source, with identical options
|
||||
- **THEN** concatenated wire bytes are byte-identical between the two
|
||||
runs
|
||||
|
||||
#### Scenario: seeded Rng + fixed Clock produces byte-identical bundles
|
||||
|
||||
- **WHEN** two `Packer.pack(data)` calls run with the same `data`, the
|
||||
same seeded `Rng`, the same fixed `Clock`, and identical encryption
|
||||
options
|
||||
- **THEN** both `result.data` and `result.metadata.integrity.entryDigest`
|
||||
bytes are equal
|
||||
|
||||
**Verified by:** `src/__property__/round-trip.property.test.ts::"pack then unpack recovers the original bytes for arbitrary inputs"` AND `src/__property__/round-trip.property.test.ts::"chunked streaming pack matches whole-buffer chunked pack byte-for-byte"` AND `src/__property__/determinism.property.test.ts::"two packs with identical inputs and seeded Rng produce byte-identical bundles"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: PACKER-30 — mitata benchmark suite
|
||||
|
||||
The package SHALL ship a runnable mitata benchmark suite at
|
||||
`src/__benchmarks__/packer.bench.ts` covering: (a) whole-file
|
||||
`Packer.pack` over a fixed 1 KiB payload, (b) whole-file
|
||||
`Unpacker.unpack` over the result of (a), (c) chunked `packChunks`
|
||||
over a single-chunk source, (d) chunked `packChunks` over a
|
||||
1000-chunk source, (e) `computeWholeFileIntegrity` over a fixed 1 KiB
|
||||
payload, and (f) a plugin-overhead comparison
|
||||
(`Packer.pack` with zero plugins vs. with a no-op plugin). The
|
||||
benchmark MUST be runnable via `bun run benchmark` declared in
|
||||
`package.json` and MUST execute without error. Baseline numbers MUST
|
||||
be published in `README.md` under a `## Benchmarks` section with a
|
||||
hardware/runtime context line.
|
||||
|
||||
#### Scenario: bun run benchmark executes the mitata suite
|
||||
|
||||
- **WHEN** `bun run benchmark` is invoked at the package root
|
||||
- **THEN** the suite completes without throwing and emits mitata's
|
||||
standard ops/sec summary for every named case
|
||||
|
||||
#### Scenario: every named case is registered with mitata
|
||||
|
||||
- **WHEN** the benchmark module is statically inspected for `bench(`
|
||||
invocations
|
||||
- **THEN** there are at least six distinct `bench("...")` calls
|
||||
matching the named cases (whole-file pack, whole-file unpack,
|
||||
chunked single, chunked 1000, integrity verify, plugin overhead)
|
||||
|
||||
**Verified by:** `src/benchmarks-conformance/packer.bench.spec-conformance.test.ts::"declares the six named benchmark cases"` AND `src/benchmarks-conformance/packer.bench.spec-conformance.test.ts::"package.json declares a bun run benchmark script"` AND `src/benchmarks-conformance/packer.bench.spec-conformance.test.ts::"README.md publishes a Benchmarks section with a hardware context line"`
|
||||
|
||||
<!-- markdownlint-enable MD024 -->
|
||||
@@ -0,0 +1,93 @@
|
||||
# Tasks — Enterprise hardening (extensibility cluster) for `@suaveplan/packer`
|
||||
|
||||
## 1. Spec authoring
|
||||
|
||||
- [x] 1.1 Read `../suaveplan/enterprise/packer.md` end-to-end
|
||||
- [x] 1.2 Read `openspec/specs/packer/spec.md`
|
||||
- [x] 1.3 Read `openspec/AGENTS.md` Sections 13 and 14
|
||||
- [x] 1.4 Draft `proposal.md` from `openspec/templates/proposal.md`
|
||||
- [x] 1.5 Fill in `## Source mapping` table — every requirement cites a line range
|
||||
- [x] 1.6 Draft `specs/packer/spec.md` from `openspec/templates/spec-delta.md`
|
||||
- [x] 1.7 Every requirement in the spec delta carries a `**Verified by:**` line naming concrete `it(...)` titles
|
||||
|
||||
## 2. Implementation — error codes (E-1 / E-2 / E-3)
|
||||
|
||||
- [x] 2.1 Add `FORMAT_VERSION_UNSUPPORTED` and `PLUGIN_FAILED` codes to `src/error-codes.ts` with metadata.
|
||||
- [x] 2.2 Add catalog keys `packer.error.FORMAT_VERSION_UNSUPPORTED.v2-not-implemented`, `packer.error.PLUGIN_FAILED.<hook>` to `src/i18n/messages.ts`.
|
||||
|
||||
## 3. Implementation — schema registry (E-1)
|
||||
|
||||
- [x] 3.1 Add `@suaveplan/schema-registry` to `packer/package.json` deps (`workspace:^`).
|
||||
- [x] 3.2 New `src/schemas/registration.ts` — `registerPackerSchemas(registry)` + `getPackerSchemaRegistry()`, registering every Zod schema in `packer.schemas.ts` plus a `packer-chunk-format` subject with V1 evolution metadata.
|
||||
- [x] 3.3 Sibling `registration.md` + `registration.types.ts` + `index.ts` barrel.
|
||||
- [x] 3.4 Subpath export `./schemas` in `package.json`.
|
||||
|
||||
## 4. Implementation — feature flags (E-2)
|
||||
|
||||
- [x] 4.1 Add `@suaveplan/feature-flags` to `packer/package.json` deps (`workspace:^`).
|
||||
- [x] 4.2 New `src/flags/flags.ts` — `PackerFeatureFlags` interface, `defaultPackerFlags()` no-op, `resolvePackerFlags(opts)` factory, `PACKER_FLAG_KEYS` constants.
|
||||
- [x] 4.3 Sibling `flags.md` + `flags.types.ts` + `index.ts` barrel.
|
||||
- [x] 4.4 `Packer.pack` reads `packer.format.v2` from the resolved provider; when truthy, throws `PackerError(PACKER.FORMAT_VERSION_UNSUPPORTED)`.
|
||||
- [x] 4.5 `PackerOptions.flags?: PackerFlagsOptions` threaded through `AbstractPacker.resolvedOptions`.
|
||||
- [x] 4.6 Subpath export `./flags` in `package.json`.
|
||||
|
||||
## 5. Implementation — plugin interface (E-3)
|
||||
|
||||
- [x] 5.1 New `src/plugins/plugins.ts` — `PackerPlugin` contract, `PluginPipeline.runHook(name, ctx)`, error mapping to `PACKER.PLUGIN_FAILED`.
|
||||
- [x] 5.2 Sibling `plugins.md` + `plugins.types.ts` + `index.ts` barrel.
|
||||
- [x] 5.3 `PackerOptions.plugins?: ReadonlyArray<PackerPlugin>` threaded through `AbstractPacker`.
|
||||
- [x] 5.4 `Packer.pack` invokes `onPackStart` → per-entry `onEntry` → `onPackEnd` in declaration order around the pipeline.
|
||||
- [x] 5.5 `Unpacker.unpack` invokes `onUnpackStart` → `onEntryDecoded` → `onUnpackEnd`.
|
||||
- [x] 5.6 Subpath export `./plugins` in `package.json`.
|
||||
|
||||
## 6. Tests — schema registry, flags, plugins
|
||||
|
||||
- [x] 6.1 `src/schemas/registration.test.ts` + `.spec-conformance.test.ts` — every schema registered, V1 evolution metadata, BACKWARD compatibility, singleton invariant.
|
||||
- [x] 6.2 `src/flags/flags.test.ts` + `.spec-conformance.test.ts` — default provider returns V1 for every key, V2 throws `PACKER.FORMAT_VERSION_UNSUPPORTED`, custom evaluator wiring, integration with `Packer.pack` write path.
|
||||
- [x] 6.3 `src/plugins/plugins.test.ts` + `.spec-conformance.test.ts` — declaration-order hook fire, error → `PACKER.PLUGIN_FAILED`, noop-logger + double-integrity example plugins exercised against real `Packer` + `Unpacker`.
|
||||
|
||||
## 7. Tests — fast-check property (E-4)
|
||||
|
||||
- [x] 7.1 `src/__property__/round-trip.property.test.ts` — pack→unpack identity over arbitrary `Uint8Array` inputs; streaming↔batch equivalence via chunked pack.
|
||||
- [x] 7.2 `src/__property__/determinism.property.test.ts` — same inputs + seeded Rng + fixed Clock ⇒ byte-identical bundles.
|
||||
|
||||
## 8. Benchmarks (E-5)
|
||||
|
||||
- [x] 8.1 `src/__benchmarks__/packer.bench.ts` — mitata suite (whole-file pack/unpack, chunked single + 1000 chunks, integrity verify, plugin overhead).
|
||||
- [x] 8.2 `bun run benchmark` script in `package.json`.
|
||||
- [x] 8.3 `README.md` Benchmarks section with baseline numbers and hardware/runtime context line.
|
||||
|
||||
## 9. Documentation
|
||||
|
||||
- [x] 9.1 Sibling `.md` for every new `.ts` (≥200 words, basic + advanced examples).
|
||||
- [x] 9.2 Update `README.md` Modules list with `flags/`, `plugins/`, `schemas/registration`.
|
||||
|
||||
## 10. Pre-archive verification (the 5-gate triple)
|
||||
|
||||
- [x] 10.1 `bun run check` — Biome 0/0/0.
|
||||
- [x] 10.2 `bunx turbo lint typecheck test --filter=@suaveplan/packer` — green.
|
||||
- [x] 10.3 `bunx turbo test:coverage --filter=@suaveplan/packer` — 100/100/100/100.
|
||||
- [x] 10.4 `bun scripts/openspec-conformance.ts @suaveplan/packer` — exit 0.
|
||||
- [x] 10.5 `bunx turbo build --filter=@suaveplan/packer` — green.
|
||||
|
||||
## 11. Consumer sweep
|
||||
|
||||
- [x] 11.1 `bunx turbo lint typecheck test --filter=@suaveplan/drive-plugin-chunks --filter=@suaveplan/drive-webrtc` — green.
|
||||
|
||||
## 12. Pre-archive 9-row checklist
|
||||
|
||||
- [x] 12.1 Generics threaded; `expectTypeOf` assertions on every literal-type claim.
|
||||
- [x] 12.2 Zod schemas wired at every declared boundary.
|
||||
- [x] 12.3 Every error code in `error-codes.ts` thrown by at least one production path.
|
||||
- [x] 12.4 OTel instruments emitted from production paths.
|
||||
- [x] 12.5 No `export *` in feature barrels.
|
||||
- [x] 12.6 No direct `vitest` / `bun:test` imports.
|
||||
- [x] 12.7 No retroactive tick — every box above reflects work done in this change.
|
||||
- [x] 12.8 Sibling `.md` for every new `.ts`.
|
||||
- [x] 12.9 Items E-1, E-2, E-3, E-4, E-5 each have ≥1 production code path and ≥1 test pinning every named scenario.
|
||||
|
||||
## 13. Archive
|
||||
|
||||
- [x] 13.1 Fold `specs/packer/spec.md` into `openspec/specs/packer/spec.md`.
|
||||
- [x] 13.2 Move `openspec/changes/enterprise-packer-extensibility/` → `openspec/archive/<yyyy-mm-dd>-enterprise-packer-extensibility/`.
|
||||
- [x] 13.3 Refresh `MANIFEST.md`.
|
||||
Reference in New Issue
Block a user