Files
tech-log-frontend/docs/superpowers/specs/2026-08-01-refactoring-review-remediation-design.md
T

15 KiB

Refactoring Review Remediation Design

Purpose

Complete the existing runtime-integrity work, preserve the current uncommitted implementation snapshot, and then close the correctness and evidence gaps found by the repository-wide module, script, schema, and test review.

This design supplements 2026-08-01-runtime-integrity-refactor-design.md. It does not replace the existing release/boot or scope-owned server-state plans. Those changes remain the baseline on which the remediation phases build.

Chosen approach

Use a staged, in-place compatibility migration with test-first behavior fixes.

  • Preserve every pre-existing dirty-worktree change. Do not reset, restore, or replace the current implementation with a clean-branch rewrite.
  • Complete and verify the existing release/boot and scope-generation work before changing its public contracts.
  • Add a failing regression test before every production behavior change.
  • Fix active correctness and release blockers before structural extraction.
  • Keep public facades stable while removing duplicate internal authorities.
  • Do not enable the Service Worker capability until its bounded marker reader, full identity handshake, and ACTIVE fixture build are verified.

A big-bang rewrite is rejected because query ownership, HTTP execution, release evidence, and optional runtimes have independent failure modes. A P0/P1-only patch is also rejected because it would leave duplicate registries and misleading quality gates that can recreate the same defects.

Program phases

The work is delivered as independently testable sub-projects in this order.

  1. Existing implementation baseline
  2. Query invalidation correctness
  3. Mutation intent and effect settlement
  4. Immutable release promotion
  5. Release/evidence contract enforcement
  6. Quality and architecture gate accuracy
  7. HTTP contract and layer consolidation
  8. Service Worker hardening
  9. Characterized adapter decomposition

Each phase must leave type checking, linting, and its focused tests green. A later phase may depend only on explicit interfaces produced by an earlier one.

Existing implementation baseline

The current dirty snapshot contains the in-progress Release Manifest V2, exact boot pairing, scope-generation ownership, HTTP V3 executor, and optional runtime foundation. It is the source of truth for this program.

Before remediation begins, run the focused suites from the existing release/boot and scope-owned server-state plans. Resolve failures in those plans without changing the remediation contracts below. Record browser- or artifact-producing gates separately when they cannot run in the current environment.

Query invalidation architecture

One query-key module owns both bound keys and invalidation prefixes. Query-key schema version 2 is:

["query", 2, namespaceId, namespaceVersion, scopeFingerprint,
 definitionVersion, identityToken]

The invalidation prefix for the same namespace is exactly:

["query", 2, namespaceId, namespaceVersion]

Features declare namespace identities and topic-to-namespace edges; they never copy TanStack key shapes. Bootstrap composes one InvalidationRegistry, calls indexInvalidationRegistry once, and passes the resulting many-to-many index to the cache coordinator. Cross-context messages continue to carry opaque topics only. The receiving coordinator resolves every namespace for that topic locally.

The legacy flat query registry and the empty registry in query-keys.ts are removed after static usage reaches zero. Query persistence is disabled, so the key-version change requires no persisted-cache migration.

Mutation intent architecture

MutationIntent is an application-level command identity, not an HTTP adapter detail. The contracts layer defines its immutable shape, the application layer defines MutationIntentFactory, and a browser adapter implements the factory with crypto.randomUUID().

type MutationIntent = Readonly<{
  intentId: string;
  operationId: string;
  canonicalInputIdentity: string;
  idempotencyKey?: string;
  createdAtMonotonicMs: number;
}>;

useApplicationMutation creates exactly one intent after duplicate admission and before optimistic projection. The intent travels through the bound mutation execution context and feature input to ContractHttpExecutor. Every physical retry and reconciliation of that logical submit reuses the same intent. A new submit always creates a new intent.

For a KEYED descriptor, the executor requires a bounded, non-empty idempotency key before credential resolution or fetch admission. Missing or invalid keys return a pre-dispatch contract violation with NOT_STARTED and perform zero fetches. Query operations never emit an idempotency header. Intent IDs and keys are forbidden in URLs, query keys, diagnostics, and telemetry.

Mutation effect settlement

Command failures carry mandatory effect certainty. The mutation bridge derives settlement before changing optimistic state:

NOT_STARTED | NOT_APPLIED -> rollback
APPLIED_CONFIRMED         -> commit, then invalidate
MAYBE_APPLIED             -> retain uncertain layer; do not retry or invalidate

OptimisticLayerLease gains an uncertain state and an explicit reconciliation operation. An uncertain layer remains ordered with later layers and cannot be collapsed into the base until inspection resolves it as applied or not applied. The application async state exposes mutation-effect-unknown, and the controller exposes a reconciliation action associated with the original intent. Unknown effect never silently maps to conflict, success, or generic retryable failure.

Immutable release promotion

The release workflow has one byte-producing authority.

  1. immutable_build creates dist, build manifest, module inventory, and the local supply-chain documents once.
  2. The job publishes one immutable bundle and its distSha256.
  3. Provider jobs scan and attest that exact digest.
  4. The promotion job downloads the bundle and provider evidence, supplies VULNERABILITY_REPORT_PATH and PROVENANCE_ATTESTATION_PATH, and verifies schemas, signatures, and digest identity.
  5. Promotion consumes the verified bundle without rebuilding it.

The current build:release command is split into byte production, local evidence generation, provider verification, and promotion verification. Missing provider evidence remains FAIL_UNVERIFIED; it is never converted to a local pass. An attestation for a different digest, or any rebuild after attestation, fails promotion.

This repository validates but does not fabricate external provider evidence. The CI environment must supply a vulnerability report and signed provenance attestation produced for the published candidate digest. Until that external integration is configured, candidate build and local verification may pass but promotion remains intentionally unavailable.

Release and evidence contracts

One async verifyReleaseRuntimeCoherence policy is used by verify-release and the rollback runbook. V1 verifies the legacy scalar tuple. V2 verifies the exact package set and recomputes the contract-set digest. V2 never synthesizes an API contract version.

All machine-readable evidence writers call a common validated writer before touching the destination:

writeValidatedJsonArtifact({ path, schema, value }): Promise<void>

Executable Zod schemas are authoritative. Checked JSON schemas are generated views and a --check command fails on drift. CI maps every evidence path to an executable schema and validates content before upload, rather than checking existence only.

verifyBuildManifestOutputs confines declared paths to their approved roots, parses the module inventory, and compares its raw SHA-256 with moduleInventoryHash.

Repository file discovery is fail-closed. Required roots fail on absence, permissions, or read errors; optional roots permit only explicit ENOENT. Provenance and secret scanning share the tracked repository inventory so build inputs such as index.html, Vite configs, TypeScript configs, .nvmrc, and the provider workflow cannot be silently omitted.

Quality and architecture gates

Coverage reports distinguish selectedTotal from repositoryTotal. repositoryTotal contains every production TypeScript module and starts with a non-decreasing baseline. A critical-module registry immediately includes HTTP V3, request/response bounds, boot bounds, service-worker lifecycle, scope generation, and release loading. A changed high-risk module must have a policy entry or an owned, expiring waiver.

The HTTP scenario catalog is executable input to table-driven contract tests. Declaring a scenario without executing its status, effect, retry, fetch-count, media-type, body-bound, and scope-fence expectations does not satisfy the gate.

config/ci/gates.json is parsed by one shared schema. scripts/generate-ci-workflow.ts deterministically emits the complete provider workflow, and its --check mode fails when the checked-in workflow differs. Token and regex presence checks are not authoritative. Playwright configurations inherit forbidOnly: true, Vitest rejects .only, and fake timers are restored by the common test setup.

The Babel/Node resolver graph is the sole authoritative architecture analyzer while dependency-cruiser cannot parse TypeScript 7. A zero-module graph fails. Rules prohibit contracts from importing outer application/runtime layers and feature adapters from importing global concrete adapters.

HTTP contract consolidation

The installed contract contribution is the single source for method, path, input/output validators, retry semantics, effect classification, deadlines, and byte bounds. The reference DTO schema is defined once and requires a valid datetime when createdAt is present. Legacy registries and codecs are generated from the contribution during the compatibility window, then removed after production and tests have no callers.

Provider-neutral operation outcomes and typed operation maps live in contracts or application ports. Feature adapters do not import HttpExecutionOutcome from a concrete HTTP adapter and do not accept operationId: string paired with input: unknown. Runtime REQUEST_TIMEOUT_MS is a global maximum applied on top of descriptor deadlines.

Raw query and mutation overloads are removed from production exports after callers migrate to BoundQuery and BoundMutation. Test-only legacy harnesses remain outside the production public index until their tests migrate.

Service Worker hardening

Activation markers are read through a realm-safe bounded response reader. It checks declared length, reads at most maxBytes + 1, cancels an oversized or non-terminating stream, decodes fatal UTF-8, and validates the marker record. response.text() is not used for bounded worker protocol data.

Page and worker exchange a canonical identity digest covering protocol version, cache schema version, build, release, contract set, and static asset set. This wire-shape change increments the Service Worker protocol version to 2. Every tuple-field mutation changes the digest and rejects activation. The ACTIVE fixture build validates asset entries and recomputes the static set digest without changing the product's default null selection.

Adapter decomposition

Structural extraction begins only after behavior is characterized.

  • IndexedDB runtime and maintenance share one persisted-row schema containing record, receipt, retention, budget types, guards, and golden fixtures.
  • OPFS worker keeps its public facade while browser bootstrap, message host, core state machine, Web Lock lease, and physical I/O move into focused files.
  • Public response cache extracts manifest codec/digest and generic lock logic.
  • Download delivery extracts browser-managed, picker-streaming, and object-URL strategies behind the existing facade.

Resumable upload, websocket, reconnect, stream coordinator, HTTP V3, and Browser RPC remain intact unless a behavior test demonstrates an independent change reason. File length alone is not a split criterion.

Error handling

  • Active correctness failures use stable failure kinds and preserve effect certainty.
  • Mandatory release evidence, file inventory, schema, or digest failures fail closed.
  • Optional diagnostics cannot change command, cache, or release outcomes.
  • Cleanup continues through all participants but publishes no READY state after any mandatory failure.
  • Tool crashes, null exit status, signals, and timeouts never count as expected negative-fixture rejection.

Testing and verification

Every behavior change follows red-green-refactor. The minimum regression set includes:

  • Actual bindQuery keys for local, remote, fan-out, and generation-isolated invalidation.
  • Independent runtime intents, same-submit retry identity, missing-key pre-dispatch rejection, and secret-free diagnostics.
  • Optimistic NOT_APPLIED, APPLIED_CONFIRMED, and MAYBE_APPLIED settlement, including out-of-order layers and reconciliation.
  • V1/V2 release coherence and every contract-set tamper in both verifier and rollback drill.
  • Provider evidence absence, digest mismatch, post-attestation rebuild, and a valid immutable promotion fixture.
  • Required-root and unreadable-file failures, schema drift, invalid evidence, and module-inventory hash mismatch.
  • Complete V3 response/effect scenario execution and production read/write E2E.
  • Headerless oversized and non-terminating Service Worker marker streams.
  • Shared persisted-row acceptance/rejection across IndexedDB runtime and maintenance before extraction.

Repository completion requires fresh evidence from type checking, linting, non-browser tests, coverage, architecture, build, release verification, CI contract checks, browser capabilities, E2E, accessibility, visual tests where the environment supports them, schema parity, and diff hygiene. Unsupported browser gates are reported explicitly and are never claimed as passing.

Acceptance criteria

  • The existing release/boot and scope-generation work passes its original focused suites before remediation contracts are changed.
  • Mutation success invalidates every matching bound query locally and remotely.
  • No two independent logical commands reuse an idempotency key; missing KEYED intent prevents network admission.
  • MAYBE_APPLIED never causes arbitrary optimistic rollback, commit, retry, or invalidation.
  • Release promotion verifies and promotes the exact same immutable bytes.
  • V2 rollback verification cannot bypass contract-set package or digest checks.
  • Required files, artifact schemas, and manifest output hashes fail closed.
  • Coverage and scenario gates measure production behavior rather than declared subsets or source tokens.
  • Production uses one executable HTTP contract registry and respects clean architecture dependency direction.
  • Service Worker activation is bounded and full-identity coherent before the capability can become ACTIVE.
  • Adapter splits preserve their public facade and pass shared golden tests.