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,144 @@
# Enterprise hardening — `@suaveplan/packer` resilience cluster
**Package**: `@suaveplan/packer`
**Status**: PROPOSED
**Type**: feature
**Design**: (inline below — small scope)
**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. The `enterprise/packer.md` audit (L43, L46–L47)
calls out three remaining resilience gaps that block the ENTERPRISE label:
1. No retry primitives on IO-backed sources (`ReadableStream.read()` calls
throw straight up the stack on transient errors).
2. No idempotency layer — repeat packs over identical inputs re-run the full
gzip + AEAD + ed25519 pipeline every time.
3. The whole-file `pack` / `unpack` paths do not accept `AbortSignal`; only
the chunked path honours cancellation today.
4. `Date.now()` and `crypto.getRandomValues()` are called at production code
paths in `src/`, violating the audit's "no `Math.random()` / `Date.now()`
at module scope; injectable clock and RNG" line.
This is cluster #2 of a 3-cluster enterprise hardening of `@suaveplan/packer`
(integrity → resilience → extensibility). Cluster #3 is deliberately out of
scope here — see "Out of scope" below.
## What changes
- ADDED: `src/retry/retryable-read.ts` — `retryRead(reader, opts)` helper that
wraps `ReadableStream` reads in `@suaveplan/retry`'s `withRetry`, honours
`AbortSignal`, and returns the same `{ value, done }` shape. Defaults:
`createExponentialBackoff({ initial: 25, base: 2, max: 1000, jitter: "equal" })`
+ `maxAttempts: 4`.
- ADDED: `RetryPolicy` option bundle on `PackerOptions`, `PackChunkedOptions`,
and `UnpackChunkedOptions` (`retryConfig?`, `signal?`). Threaded through to
`normalizeSource` and the abstract `collect` path so every `reader.read()`
in production code is retry-aware.
- ADDED: `src/idempotency/idempotency.ts` — `PackIdempotencyGuard` that
computes a content-derived key (`blake3(payload) || compressionLevel ||
encryptionAlgo || signed`-bit) and short-circuits to a cached `PackedData`
when the same input is packed again. Backed by an injectable
`IdempotencyStoreAdapter` (default `MemoryIdempotencyStore`).
- ADDED: `PackerOptions.idempotency?: { store, ttlMs?, enabled? }`.
`Packer.pack` consults the guard before any work; a hit returns the cached
bundle bytes. Misses populate the store after the pack completes.
- ADDED: `signal?: AbortSignal` on `UnpackOptions`. `Unpacker.unpack` now
checks `signal.aborted` at every yield point (before integrity verify,
before signature verify, before AEAD decrypt, before gunzip, before
checksum verify, before the final emit).
- ADDED: `PackOptions` interface (`{ filename?, signal? }`) and an additional
`Packer.pack(data, opts: PackOptions)` overload. The original
`Packer.pack(data, filename?)` overload remains for back-compat.
- ADDED: `signal?: AbortSignal` on `PackChunkedOptions` — already honoured on
the unpacker; now enforced on the chunked packer between every emitted
chunk and the source pull loop.
- ADDED: `Clock` and `Rng` injection on `PackerOptions`, `PackChunkedOptions`,
`UnpackChunkedOptions`. Defaults wire to real platform implementations
(`() => Date.now()` for `Clock`, a wrapper around `crypto.getRandomValues`
for `Rng`). Every `Date.now()` / `crypto.getRandomValues()` /
`performance.now()` call in production `src/` is replaced with a call
through the injected `Clock` / `Rng`.
- MODIFIED: `AbstractPacker.buildMetadata` reads `timestamp` from the
resolved `Clock` instead of `Date.now()`.
- MODIFIED: `packer/packer.ts` reads nonce bytes via the resolved `Rng`
instead of calling `crypto.getRandomValues` directly.
- MODIFIED: every `nowMs()` helper across `packer/`, `unpacker/`,
`chunked/packer/`, `chunked/unpacker/` now reads through the resolved
`monotonicClock` (defaults to `performance.now()` for backward parity).
## Out of scope
- `@suaveplan/schema-registry` integration → cluster #3.
- `@suaveplan/feature-flags` for format-version rollouts → cluster #3.
- Plugin / extension interface → cluster #3.
- `fast-check` property tests for round-trip invariants → cluster #3.
- mitata benchmarks → cluster #3.
## Source mapping
This change derives from `../suaveplan/enterprise/packer.md`.
| Requirement | Source lines | Notes |
|---|---|---|
| PACKER-22 (`@suaveplan/retry` on IO) | L47 | Verbatim — "**retry** primitives for IO-backed sources". |
| PACKER-23 (idempotency cache) | L47 | Verbatim — "**Idempotency** — repeat packs over the same inputs are a no-op". |
| PACKER-24 (whole-file AbortSignal) | L46 | Verbatim — "**Backpressure & cancellation** — `AbortSignal` everywhere async". |
| PACKER-25 (injected `Clock` + `Rng`) | L43 | Verbatim — "no `Math.random()` / `Date.now()` at module scope; injectable clock and RNG". |
### Requirements with no source line
None — every requirement in this cluster maps directly to an audit bullet.
## Verification approach
- `src/retry/retryable-read.test.ts` + `.spec-conformance.test.ts` — pins
`retryRead` against an injectable failing reader, asserts the retry policy
is consulted, asserts `AbortSignal` cancels mid-backoff.
- `src/idempotency/idempotency.test.ts` + `.spec-conformance.test.ts` — pins
identical-input dedup, cache-miss population, custom-store injection.
- `src/packer/packer.spec-conformance.test.ts` (extended) — pins `pack`
abort, idempotency hit, `Clock`-driven timestamp, `Rng`-driven nonce.
- `src/unpacker/unpacker.spec-conformance.test.ts` (extended) — pins
pre-flight abort, mid-pipeline abort.
- `src/chunked/packer/packer.spec-conformance.test.ts` (extended) — pins
signal-driven mid-stream abort.
## Migration impact
Public-surface additions only. No existing signature changes shape — the new
`Packer.pack(data, opts: PackOptions)` overload sits beside the existing
`Packer.pack(data, filename?)`. The `Clock` / `Rng` / `idempotency` /
`retryConfig` / `signal` 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 `signal` passed**: pack / unpack behaviour is identical to cluster #1.
- **No `idempotency` passed**: no cache layer is consulted.
- **No `retryConfig` passed**: a single read is attempted (no retries) for
parity with cluster #1 — the default policy is `maxAttempts: 1` so the
retry layer is a no-op unless opted in.
- **No `clock` / `rng` passed**: defaults call `Date.now()` /
`crypto.getRandomValues` exactly as before, so wire output is identical.

View File

@@ -0,0 +1,193 @@
<!-- markdownlint-disable MD024 -->
# Capability Spec Delta: packer (enterprise-packer-resilience)
**Package**: `@suaveplan/packer`
**Status**: PROPOSED
> Every requirement below carries a `**Verified by:**` line at authoring time. No exceptions.
## ADDED Requirements
### Requirement: PACKER-22 — `@suaveplan/retry` on IO-backed source reads
The package SHALL route every IO-backed source pull (every `reader.read()`
call against a `ReadableStream` consumed by `normalizeSource` or the
abstract `collect` helper) through `@suaveplan/retry`'s `withRetry`. The
retry policy MUST be configurable per call via a `retryConfig?: RetryConfig`
option on `PackerOptions`, `PackChunkedOptions`, and
`UnpackChunkedOptions`. When `retryConfig` is absent the package MUST
default to a single-attempt policy (`maxAttempts: 1`) so wire behaviour
matches cluster #1. When `retryConfig` is supplied the retry loop MUST
honour the caller's `AbortSignal`: cancellation MUST abort the retry loop
immediately (no further attempts, no further sleeps) and surface as a
`RetryAbortedError`.
#### Scenario: failing reader is retried up to maxAttempts
- **WHEN** `normalizeSource` consumes a `ReadableStream` whose `read()`
rejects the first two times and resolves on the third, with
`retryConfig: { maxAttempts: 3, delay: createConstantBackoff({ delay: 0 }) }`
- **THEN** the iterable yields the eventually-read bytes and the consumer
observes three total read attempts
#### Scenario: AbortSignal cancels the retry loop mid-backoff
- **WHEN** the retry loop is sleeping between attempts and the caller's
`AbortSignal` fires
- **THEN** the retry rejects with `RetryAbortedError` and no further
`reader.read()` is invoked
#### Scenario: default policy is a single attempt
- **WHEN** `retryConfig` is not supplied and a `read()` rejects
- **THEN** the rejection surfaces unchanged and the iterable terminates
with the original error (no retries)
**Verified by:** `src/retry/retryable-read.test.ts::"retries up to maxAttempts on a failing reader"` AND `src/retry/retryable-read.test.ts::"cancels the retry loop when AbortSignal fires mid-backoff"` AND `src/retry/retryable-read.spec-conformance.test.ts::"default policy is a single attempt with no retries"` AND `src/retry/retryable-read.spec-conformance.test.ts::"retryRead surfaces RetryAbortedError when signal fires before first read"`
---
### Requirement: PACKER-23 — Idempotent repeat-pack short-circuit
The package SHALL expose an injectable idempotency cache on
`PackerOptions.idempotency` (`{ store, ttlMs?, enabled? }`). When enabled,
`Packer.pack(data, opts?)` MUST compute a content-derived dedup key from
`blake3(data)` plus the packer's pipeline parameters
(`compressionLevel`, `encryption.algorithm`, `signing` enabled bit), look
the key up in the supplied `IdempotencyStoreAdapter`, and on hit return
the previously-built `PackedData` without re-running the gzip / AEAD /
sign / integrity pipeline. On miss the pipeline MUST run as before and
the resulting `PackedData` MUST be written back to the store under the
same key with the configured TTL (default `86_400_000` ms / 24 h).
Idempotency MUST be opt-in: callers that do not pass `idempotency` see
the legacy non-cached behaviour.
#### Scenario: identical pack inputs short-circuit on the second call
- **WHEN** `Packer.pack(data)` is called twice with identical `data`,
identical options, and a `MemoryIdempotencyStore` injected through
`idempotency.store`
- **THEN** the second call returns a `PackedData` whose `data` bytes and
`metadata.integrity` are byte-equal to the first call's result, and
the underlying pipeline (gzip / AEAD / sign) ran exactly once
#### Scenario: cache miss populates the store
- **WHEN** the first call's dedup key is absent from the store
- **THEN** the store's `set(key, record)` is invoked exactly once after
the pack completes, with the resulting `PackedData` carried on
`record.response`
#### Scenario: idempotency disabled by default
- **WHEN** `Packer.pack(data)` is called with no `idempotency` option
- **THEN** no store interaction occurs and the pipeline runs end-to-end
on every call
**Verified by:** `src/idempotency/idempotency.test.ts::"second pack with identical input short-circuits to cached PackedData"` AND `src/idempotency/idempotency.test.ts::"cache miss populates the store with the resulting PackedData"` AND `src/idempotency/idempotency.spec-conformance.test.ts::"idempotency is disabled by default"` AND `src/idempotency/idempotency.spec-conformance.test.ts::"custom IdempotencyStoreAdapter is consulted exactly once per pack"`
---
## MODIFIED Requirements
### Requirement: PACKER-24 — Whole-file `AbortSignal` propagation
The package SHALL accept an `AbortSignal` on every async public
entrypoint and MUST check the signal at every yield point. `Unpacker.unpack`
accepts `UnpackOptions.signal?` and MUST check it before integrity
verify, before signature verify, before AEAD decrypt, before gunzip, and
before the final emit. `Packer.pack` accepts an additional overload
`pack(data, opts: PackOptions)` where `PackOptions.signal?` is checked
before compression, before encryption, before signing, before integrity
compute, and before the final return. `packChunks` accepts
`PackChunkedOptions.signal?` and MUST check it on every loop iteration
before yielding the next packed chunk. Aborted operations MUST throw
`PackerError(PACKER.ABORTED)` with the i18n catalog message
`packer.error.ABORTED.<entrypoint>`. The original
`Packer.pack(data, filename?)` overload MUST remain a valid call shape
for back-compat.
#### Scenario: Unpacker.unpack aborts before integrity verify
- **WHEN** `Unpacker.unpack(packed, { signal })` is called with a signal
that is already aborted
- **THEN** the call rejects with `PackerError(PACKER.ABORTED)` and no
integrity recomputation runs
#### Scenario: Unpacker.unpack aborts between pipeline steps
- **WHEN** the signal fires after integrity verify but before AEAD decrypt
- **THEN** the call rejects with `PackerError(PACKER.ABORTED)` and no
decrypted plaintext is materialised
#### Scenario: Packer.pack(data, opts) honours opts.signal
- **WHEN** `Packer.pack(data, { signal })` is called with a signal that
is already aborted
- **THEN** the call rejects with `PackerError(PACKER.ABORTED)` before
any compression / encryption / signing / integrity work runs
#### Scenario: packChunks aborts mid-stream
- **WHEN** `packChunks(source, { ..., signal })` is iterated and the
signal fires after the second emitted chunk
- **THEN** the iterator rejects with `PackerError(PACKER.ABORTED)`
before yielding any further chunks
#### Scenario: legacy filename overload remains valid
- **WHEN** `Packer.pack(data, "logo.png")` is called
- **THEN** the call succeeds, `metadata.mimeType` is `"image/png"`, and
no signal-related behaviour kicks in
**Verified by:** `src/unpacker/unpacker.spec-conformance.test.ts::"rejects with PACKER.ABORTED when signal is pre-aborted"` AND `src/unpacker/unpacker.spec-conformance.test.ts::"rejects with PACKER.ABORTED when signal fires between pipeline steps"` AND `src/packer/packer.spec-conformance.test.ts::"rejects with PACKER.ABORTED when opts.signal is pre-aborted"` AND `src/chunked/packer/packer.spec-conformance.test.ts::"aborts mid-stream when signal fires after the second chunk"` AND `src/packer/packer.spec-conformance.test.ts::"legacy pack(data, filename) overload still surfaces mimeType"`
---
### Requirement: PACKER-25 — Injected `Clock` and `Rng` everywhere
The package SHALL accept `clock?: Clock` and `rng?: Rng` (types from
`@suaveplan/types`) on every public option bundle (`PackerOptions`,
`PackChunkedOptions`, `UnpackChunkedOptions`). Every production
read of wall-clock time MUST go through the resolved `Clock` (no direct
`Date.now()`); every production source of randomness MUST go through
the resolved `Rng` (no direct `Math.random()` /
`crypto.getRandomValues`); every elapsed-time measurement MUST go
through an injected `monotonicClock?: MonotonicClock` (defaults to a
wrapper around `performance.now()`). The defaults MUST produce
byte-identical wire output to the cluster #1 behaviour. Determinism:
two `Packer.pack` calls with identical inputs, identical options, and a
seeded `Rng` MUST produce byte-identical `PackedData.data` and
identical `metadata.integrity` values.
#### Scenario: timestamp comes from the injected Clock
- **WHEN** `Packer.pack(data)` is called with a clock returning a fixed
value `1_700_000_000_000`
- **THEN** `result.metadata.timestamp === 1_700_000_000_000`
#### Scenario: AEAD nonce comes from the injected Rng
- **WHEN** `Packer.pack(data, opts?)` is called with `encryption` set and
an `Rng` whose `nextBytes(n)` returns a fixed byte pattern
- **THEN** `result.metadata.nonce` equals the byte pattern truncated to
the nonce length
#### Scenario: same inputs + same seed produce byte-identical output
- **WHEN** two `Packer.pack(data)` calls run with the same seeded `Rng`
and the same fixed `Clock`
- **THEN** their `data` and `metadata.integrity.entryDigest` are
byte-equal
#### Scenario: no production file calls Date.now / Math.random / crypto.getRandomValues directly
- **WHEN** a recursive grep against `src/**/*.ts` (excluding `*.test.ts`,
`*.types.ts`, `*.schemas.ts`) is run for `Date.now\|Math.random\|crypto.getRandomValues`
- **THEN** zero matches exist outside the default-Clock / default-Rng
factories in `src/runtime/runtime.ts`
**Verified by:** `src/packer/packer.spec-conformance.test.ts::"timestamp is sourced from the injected Clock"` AND `src/packer/packer.spec-conformance.test.ts::"AEAD nonce is sourced from the injected Rng"` AND `src/packer/packer.spec-conformance.test.ts::"two packs with identical inputs and seeded Rng produce byte-identical output"` AND `src/runtime/runtime.spec-conformance.test.ts::"no production file calls Date.now / Math.random / crypto.getRandomValues directly"`
<!-- markdownlint-enable MD024 -->

View File

@@ -0,0 +1,95 @@
# Tasks — Enterprise hardening (resilience 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 — Runtime defaults (R-4 foundation)
- [ ] 2.1 New `src/runtime/runtime.ts` — `defaultClock`, `defaultMonotonicClock`, `defaultRng`, `resolveRuntime(opts)` returning `{ clock, monotonicClock, rng }`.
- [ ] 2.2 Sibling `.md`, `.types.ts`, `index.ts` barrel.
- [ ] 2.3 Extend `PackerOptions`, `PackChunkedOptions`, `UnpackChunkedOptions` with `clock?: Clock`, `monotonicClock?: MonotonicClock`, `rng?: Rng`.
## 3. Implementation — Retry (R-1)
- [ ] 3.1 Add `@suaveplan/retry` to `packer/package.json` deps (workspace:^).
- [ ] 3.2 New `src/retry/retryable-read.ts` — `retryRead(reader, opts)` wrapping `reader.read()` in `withRetry`.
- [ ] 3.3 Extend `normalizeSource` to accept `retryConfig?` and route ReadableStream reads through `retryRead`.
- [ ] 3.4 Extend `AbstractPacker.packStream`'s `collect` helper to accept and propagate `retryConfig?` to `normalizeSource`.
- [ ] 3.5 Thread `retryConfig?` and `signal?` through `PackerOptions`, `PackChunkedOptions`, `UnpackChunkedOptions`.
## 4. Implementation — Idempotency (R-2)
- [ ] 4.1 Add `@suaveplan/idempotency` to `packer/package.json` deps (workspace:^).
- [ ] 4.2 New `src/idempotency/idempotency.ts` — `PackIdempotencyGuard` consuming `IdempotencyStoreAdapter`, computing content-derived dedup key.
- [ ] 4.3 Sibling `.md`, `.types.ts`, `index.ts` barrel.
- [ ] 4.4 Add `idempotency?: { store, ttlMs?, enabled? }` to `PackerOptions`.
- [ ] 4.5 `Packer.pack` short-circuits on cache hit before any pipeline work; populates store on miss.
## 5. Implementation — Whole-file AbortSignal (R-3)
- [ ] 5.1 Add `signal?: AbortSignal` to `UnpackOptions`.
- [ ] 5.2 Add `PackOptions` interface (`{ filename?, signal? }`) on `types/packer.types.ts`.
- [ ] 5.3 Overload `Packer.pack(data, filenameOrOpts?, opts?)` accepting either filename string or `PackOptions`.
- [ ] 5.4 `Unpacker.unpack` checks `signal.aborted` at every yield point (5 places).
- [ ] 5.5 `Packer.pack` checks `signal.aborted` at every yield point (5 places).
- [ ] 5.6 `packChunks` checks `signal.aborted` between emitted chunks.
- [ ] 5.7 New i18n catalog keys `packer.error.ABORTED.pack`, `packer.error.ABORTED.pack-chunks`.
## 6. Implementation — Clock / Rng audit-and-replace (R-4)
- [ ] 6.1 `AbstractPacker.buildMetadata` reads `timestamp` from `this.options.clock`.
- [ ] 6.2 `packer/packer.ts` `randomBytes` replaced with `this.options.rng.nextBytes(n)`.
- [ ] 6.3 All `nowMs()` helpers across the package route through `this.options.monotonicClock` (or resolved per-call for chunked async fns).
- [ ] 6.4 Grep verify: zero `Date.now\|Math.random\|crypto.getRandomValues\|performance.now` outside `src/runtime/runtime.ts`.
## 7. Tests
- [ ] 7.1 `src/runtime/runtime.test.ts` + `.spec-conformance.test.ts` — pins defaults; grep audit.
- [ ] 7.2 `src/retry/retryable-read.test.ts` + `.spec-conformance.test.ts` — pins retry attempts, abort mid-backoff, default single-attempt.
- [ ] 7.3 `src/idempotency/idempotency.test.ts` + `.spec-conformance.test.ts` — pins hit / miss / disabled / custom store.
- [ ] 7.4 `src/packer/packer.spec-conformance.test.ts` extended — abort, idempotency, clock, rng, determinism, legacy overload.
- [ ] 7.5 `src/unpacker/unpacker.spec-conformance.test.ts` extended — pre-flight abort, mid-pipeline abort.
- [ ] 7.6 `src/chunked/packer/packer.spec-conformance.test.ts` extended — mid-stream abort.
- [ ] 7.7 Every `it(...)` carries `{ timeout: 5000 }` (or 30000 for async I/O).
## 8. Documentation
- [ ] 8.1 Sibling `.md` for every new implementation `.ts` (≥200 words, basic + advanced examples).
- [ ] 8.2 Update `README.md` Modules list with `retry/`, `idempotency/`, `runtime/`.
## 9. Pre-archive verification (the 5-gate triple)
- [ ] 9.1 `bun run check` — Biome 0/0/0.
- [ ] 9.2 `bunx turbo lint typecheck test --filter=@suaveplan/packer` — green.
- [ ] 9.3 `bunx turbo test:coverage --filter=@suaveplan/packer` — 100/100/100/100.
- [ ] 9.4 `bun scripts/openspec-conformance.ts @suaveplan/packer` — exit 0.
- [ ] 9.5 `bunx turbo build --filter=@suaveplan/packer` — green.
## 10. Consumer sweep
- [ ] 10.1 `bunx turbo lint typecheck test --filter=@suaveplan/drive-plugin-chunks --filter=@suaveplan/drive-webrtc` — green.
## 11. Pre-archive 9-row checklist
- [ ] 11.1 Generics threaded; `expectTypeOf` assertions on every literal-type claim.
- [ ] 11.2 Zod schemas wired at every declared boundary.
- [ ] 11.3 Every error code in `error-codes.ts` thrown by at least one production path.
- [ ] 11.4 OTel instruments emitted from production paths and asserted with a real in-memory `MeterProvider` (NO `vi.mock`).
- [ ] 11.5 No `export *` in feature barrels.
- [ ] 11.6 No direct `vitest` / `bun:test` imports.
- [ ] 11.7 No retroactive tick — every box above reflects work done in this change.
- [ ] 11.8 Sibling `.md` for every new `.ts`.
- [ ] 11.9 Items R-1, R-2, R-3, R-4 each have ≥1 production code path and ≥1 test pinning every named scenario.
## 12. Archive
- [ ] 12.1 Fold `specs/packer/spec.md` into `openspec/specs/packer/spec.md`.
- [ ] 12.2 Move `openspec/changes/enterprise-packer-resilience/` → `openspec/archive/<yyyy-mm-dd>-enterprise-packer-resilience/`.
- [ ] 12.3 Refresh `MANIFEST.md`.