docs: plan refactoring review remediation

This commit is contained in:
DongHyeonka
2026-08-01 21:59:19 +09:00
parent a49c76b5b2
commit 92c3d438ab
4 changed files with 655 additions and 0 deletions
@@ -0,0 +1,154 @@
# HTTP Worker and Adapter Remediation Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Remove duplicate HTTP authorities, harden Service Worker activation bounds and identity, and decompose large browser adapters only after shared golden behavior is locked.
**Architecture:** Installed contract contributions are the HTTP source of truth and expose provider-neutral typed outcomes. Worker protocol V2 exchanges a canonical full-identity digest. Browser adapter facades remain stable while shared persisted schemas and cohesive internal modules are extracted.
**Tech Stack:** TypeScript 7, Fetch API, Service Worker API, IndexedDB, OPFS, React 19, Vitest 4, Playwright.
## Global Constraints
- Contract and application layers never import concrete adapter outcome types.
- Production exports accept only `BoundQuery` and `BoundMutation` after migration.
- Runtime timeout is a global ceiling applied over descriptor deadlines.
- Worker marker reads are bounded even without `Content-Length` and cancel oversized/non-terminating streams.
- Public adapter facades and product-default optional capability selection remain unchanged.
- Extraction follows characterization tests; file length alone does not justify a split.
---
### Task 1: Installed HTTP contract as single source of truth
**Files:**
- Modify: `src/features/reference-feature/contracts/reference-feature-contract.ts`
- Modify: `src/features/reference-feature/contracts/reference-schemas.ts`
- Modify: `src/contracts/external-contract-runtime.ts`
- Modify: `src/contracts/api-operations.ts`
- Modify: `src/contracts/rest-profiles.ts`
- Modify: `src/contracts/schema-registry.ts`
- Modify: `src/features/installed-feature-contracts.ts`
- Modify: `src/bootstrap/runtime-adapters.ts`
- Modify: `tests/features/reference-feature/reference-contract.test.ts`
- Modify: `tests/runtime-schema/http-schema.test.ts`
- Modify: `tests/integration/http-execution-contract.test.ts`
- [ ] Add parity tests showing method/path/validators/retry/effect/deadline/byte bounds come from one contribution; `createdAt` accepts omitted or RFC3339 datetime and rejects arbitrary strings.
- [ ] Run focused tests and confirm RED on duplicated descriptors and permissive date schema.
- [ ] Make the installed contribution authoritative; generate temporary legacy views from it and migrate all production callers before deleting the legacy registries/codecs.
- [ ] Apply `REQUEST_TIMEOUT_MS` as `min(runtimeCeiling, descriptorDeadline)` without replacing shorter descriptor deadlines.
- [ ] Re-run focused tests, prove `rg` has zero production callers of removed registries, and commit with `git commit -m "refactor: consolidate installed HTTP contracts"`.
### Task 2: Provider-neutral typed operation outcomes
**Files:**
- Create: `src/contracts/operation-outcome.ts`
- Create: `src/application/ports/contract-operation-executor.ts`
- Modify: `src/features/reference-feature/adapters/reference-http-gateway.ts`
- Modify: `src/bootstrap/runtime-adapters.ts`
- Modify: `src/adapters/http/http-execution-v3.ts`
- Modify: `tests/features/reference-feature/reference-contract.test.ts`
- Modify: `tests/unit/runtime-adapters.test.ts`
- [ ] Add compile/runtime tests that unknown operation IDs and mismatched input/output types fail, and that the feature gateway has no import from `src/adapters/http`.
- [ ] Run focused tests/typecheck and confirm RED because the port is `operationId: string`, `input: unknown`, and concrete `HttpExecutionOutcome` leaks inward.
- [ ] Derive `InstalledOperationMap` from installed contracts, expose generic `execute<K extends keyof Map>(operationId: K, input: Map[K]["input"], context)` and map HTTP outcomes to provider-neutral contract outcomes at the adapter boundary.
- [ ] Re-run tests, typecheck, and architecture; commit with `git commit -m "refactor: type installed contract operations"`.
### Task 3: Bound-only server-state exports
**Files:**
- Modify: `src/presentation/adapters/query/application-query.ts`
- Modify: `src/presentation/adapters/query/index.ts`
- Modify: `src/features/reference-feature/presentation/use-reference-feature.ts`
- Create: `tests/helpers/legacy-application-query-harness.tsx`
- Modify: `tests/component/application-query.test.tsx`
- [ ] Add type tests that production hooks reject raw query keys and raw mutation executors while bound definitions still compile.
- [ ] Run typecheck and confirm current overloads accept raw forms.
- [ ] Move legacy raw harness behavior under `tests/helpers`; remove `LegacyMutationOptions` and the raw query union from production exports; migrate feature callers to `bindQuery`/bound mutations.
- [ ] Run focused component tests and typecheck; commit with `git commit -m "refactor: expose bound server-state hooks only"`.
### Task 4: Bounded Service Worker marker reader
**Files:**
- Create: `src/adapters/service-worker/bounded-worker-response.ts`
- Modify: `src/adapters/service-worker/service-worker-lifecycle.ts`
- Modify: `tests/unit/service-worker-runtime.test.ts`
- [ ] Add tests for oversized declared length, headerless oversized chunks, invalid UTF-8, malformed JSON, and a non-terminating stream. Assert reader cancellation and bounded completion.
- [ ] Run `corepack pnpm exec vitest run tests/unit/service-worker-runtime.test.ts` and confirm RED because lifecycle calls `response.text()`.
- [ ] Implement realm-safe stream reads up to `maxBytes + 1`, fatal `TextDecoder`, explicit cancellation, and strict marker parsing. Never call `Response.text()` for protocol data.
- [ ] Re-run tests and commit with `git commit -m "fix: bound Service Worker activation markers"`.
### Task 5: Service Worker protocol V2 full identity
**Files:**
- Modify: `src/contracts/service-worker.ts`
- Modify: `src/adapters/service-worker/service-worker-protocol.ts`
- Modify: `src/adapters/service-worker/service-worker-entry.ts`
- Modify: `src/adapters/service-worker/service-worker-lifecycle.ts`
- Modify: `src/adapters/service-worker/service-worker-page-controller.ts`
- Modify: `src/bootstrap/register-service-worker.ts`
- Modify: `scripts/generate-build-manifest.ts`
- Modify: `tests/unit/service-worker-runtime.test.ts`
- Modify: `tests/unit/service-worker-build-input.test.ts`
- [ ] Add a tuple-mutation table for protocol/cache schema/build/release/contract/static set; each mutation must change the digest and reject activation. Add a valid ACTIVE fixture build that recomputes static set digest from asset entries.
- [ ] Run focused tests and confirm RED because protocol V1 compares partial fields.
- [ ] Set `SERVICE_WORKER_PROTOCOL_VERSION = 2`, define canonical sorted identity serialization, compute SHA-256 over every identity field, and exchange/validate the digest on every page-worker message.
- [ ] Keep default capability selection `null`; use ACTIVE only in the explicit fixture build.
- [ ] Re-run focused tests and the supported fixture build; commit with `git commit -m "fix: bind Service Worker activation to full identity"`.
### Task 6: Shared IndexedDB persisted-row schema
**Files:**
- Create: `src/adapters/storage/indexeddb/indexeddb-persisted-schema.ts`
- Modify: `src/adapters/storage/indexeddb/indexeddb-types.ts`
- Modify: `src/adapters/storage/indexeddb/indexeddb-runtime.ts`
- Modify: `src/adapters/storage/indexeddb/indexeddb-maintenance.ts`
- Create: `tests/fixtures/indexeddb/persisted-rows.ts`
- Modify: `tests/unit/indexeddb-runtime.test.ts`
- Modify: `tests/unit/indexeddb-maintenance.test.ts`
- [ ] Before extraction, run the same accepted/rejected record, receipt, retention, and budget golden rows through runtime and maintenance and assert identical verdicts.
- [ ] Confirm RED on at least one drift fixture using the duplicate current guards.
- [ ] Move persisted types/guards into the shared module; runtime and maintenance import it without behavior changes.
- [ ] Re-run both large suites and commit with `git commit -m "refactor: share IndexedDB persisted schemas"`.
### Task 7: Cohesive browser adapter decomposition
**Files:**
- Modify: `src/adapters/storage/opfs/opfs-worker-runtime.ts`
- Create: `src/adapters/storage/opfs/opfs-worker-bootstrap.ts`
- Create: `src/adapters/storage/opfs/opfs-worker-message-host.ts`
- Create: `src/adapters/storage/opfs/opfs-worker-core.ts`
- Create: `src/adapters/storage/opfs/opfs-worker-lock.ts`
- Create: `src/adapters/storage/opfs/opfs-physical-io.ts`
- Modify: `src/adapters/cache-storage/public-response-cache-adapter.ts`
- Create: `src/adapters/cache-storage/public-cache-manifest.ts`
- Create: `src/adapters/cache-storage/cache-lock.ts`
- Modify: `src/adapters/browser-files/download-delivery-adapter.ts`
- Create: `src/adapters/browser-files/download-browser-managed.ts`
- Create: `src/adapters/browser-files/download-picker-stream.ts`
- Create: `src/adapters/browser-files/download-object-url.ts`
- Modify: `tests/unit/opfs-worker-runtime.test.ts`
- Modify: `tests/unit/public-response-cache.test.ts`
- Modify: `tests/unit/browser-file-download.test.ts`
- [ ] Add golden facade tests for all success/failure/cancellation/lock-loss branches before moving code; snapshot externally observable operation order and error kinds.
- [ ] Run the three focused suites and capture GREEN characterization evidence.
- [ ] Extract OPFS bootstrap, host, core state machine, Web Lock, and physical I/O without changing public exports. Do not split the core state machine further.
- [ ] Extract public-cache manifest codec/digest and generic lock logic behind the same facade.
- [ ] Extract browser-managed, picker streaming, and object-URL download strategies behind the same delivery facade.
- [ ] Re-run the same golden suites after each extraction. Any failure is a refactor regression, not a fixture update.
- [ ] Commit each adapter independently with `refactor: decompose OPFS worker adapter`, `refactor: extract public cache internals`, and `refactor: extract download delivery strategies`.
### Task 8: HTTP/worker/adapter verification
- [ ] Run all focused tests named in Tasks 17.
- [ ] Run `corepack pnpm check:architecture`, `corepack pnpm check:types`, and `corepack pnpm lint`.
- [ ] Run `corepack pnpm test:all`.
- [ ] Run supported Service Worker, IndexedDB, OPFS, public-cache, and download Playwright capability specs.
- [ ] Run `git diff --check` and report unsupported browser gates without claiming success.
@@ -0,0 +1,110 @@
# Quality and Architecture Remediation Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make coverage, scenario, CI, and architecture gates measure executable production behavior and fail when their measured universe is empty or incomplete.
**Architecture:** One typed gate schema drives both the local runner and generated workflow. Coverage separates repository inventory from instrumented totals. The Babel/resolver graph is the sole architecture authority while TypeScript 7 is unsupported by dependency-cruiser.
**Tech Stack:** TypeScript 7, Node.js 24, Babel parser, Vitest 4, Playwright, Gitea Actions.
## Global Constraints
- A zero-file or zero-module result is failure, never success.
- High-risk changed modules need explicit coverage ownership or an owned, expiring waiver.
- Scenario declarations count only when a table-driven test executes all required assertions.
- Checked-in workflow content is generated deterministically from the same gate model used locally.
- Every enforcement change begins with a failing fixture.
---
### Task 1: Repository-aware risk coverage
**Files:**
- Modify: `vitest.config.ts`
- Modify: `config/testing/risk-coverage.json`
- Modify: `scripts/check-risk-coverage.ts`
- Modify: `tests/fixtures/coverage/below-threshold.json`
- Create: `tests/fixtures/coverage/repository-omission.json`
- Create: `tests/unit/risk-coverage.test.ts`
- [ ] Add tests asserting `selectedTotal`, `repositoryTotal`, uncovered repository modules, and changed high-risk ownership. A summary covering 14 files while production inventory is larger must fail.
- [ ] Run `corepack pnpm exec vitest run tests/unit/risk-coverage.test.ts` and confirm RED because only selected totals exist.
- [ ] Enumerate every production `.ts`/`.tsx` module under `src`, exclude declarations/stories/generated files explicitly, and emit both totals. Expand coverage instrumentation to `src/**/*.{ts,tsx}` with documented exclusions.
- [ ] Seed the critical registry with HTTP V3, bounded request/response readers, boot bounds, Service Worker lifecycle, scope generation, and release loading. Validate waiver owner, reason, and future expiry.
- [ ] Run focused tests and `corepack pnpm test:coverage`; commit with `git commit -m "fix: measure repository-wide risk coverage"`.
### Task 2: Executable HTTP scenario catalog
**Files:**
- Modify: `tests/mocks/scenarios/catalog.ts`
- Create: `tests/integration/http-scenario-catalog.test.ts`
- Modify: `tests/mocks/handlers/reference-resources.ts`
- Modify: `scripts/check-test-evidence.ts`
- Modify: `config/testing/test-evidence.json`
- [ ] Define typed expectations for status/outcome/effect/retry/fetch count/media type/body bound/scope fence for every declared scenario.
- [ ] Add a table-driven test that executes each operation/scenario pair through `ContractHttpExecutor` and asserts every expectation field. Add a deliberately declared-but-unexecuted fixture and make the evidence checker reject it.
- [ ] Run `corepack pnpm exec vitest run tests/integration/http-scenario-catalog.test.ts && node scripts/check-test-evidence.ts` and confirm RED because the current gate counts source tokens.
- [ ] Export execution receipts from the test artifact and make the checker compare exact catalog IDs to exact executed IDs; source-token counts become diagnostics only.
- [ ] Re-run tests/checker and commit with `git commit -m "test: execute the HTTP scenario catalog"`.
### Task 3: Shared CI gate schema and deterministic workflow generation
**Files:**
- Create: `scripts/contracts/ci-gates.ts`
- Create: `scripts/generate-ci-workflow.ts`
- Modify: `scripts/run-ci-gate.ts`
- Modify: `config/ci/gates.json`
- Modify: `.gitea/workflows/quality-gates.yml`
- Create: `tests/unit/ci-workflow-generation.test.ts`
- [ ] Add invalid gate fixtures for unknown fields, duplicate IDs, missing artifact schemas, unknown dependencies, and cycles. Add a snapshot test for the full generated workflow plus `--check` drift.
- [ ] Run `corepack pnpm exec vitest run tests/unit/ci-workflow-generation.test.ts` and confirm RED because no shared parser/generator exists.
- [ ] Parse gates once with strict Zod schemas. Generate every job, dependency, command, environment mapping, timeout, artifact upload/download, and schema validation deterministically.
- [ ] Replace regex/token workflow checks with `node scripts/generate-ci-workflow.ts --check`; generated YAML must match byte-for-byte.
- [ ] Re-run tests and check mode; commit with `git commit -m "refactor: generate CI workflow from gate contracts"`.
### Task 4: One authoritative architecture graph
**Files:**
- Modify: `scripts/check-architecture.ts`
- Modify: `config/architecture/layers.json`
- Modify: `.dependency-cruiser.json`
- Create: `tests/fixtures/architecture/forbidden/contracts-import-application.ts`
- Create: `tests/fixtures/architecture/forbidden/feature-adapter-imports-global-adapter.ts`
- Create: `tests/unit/architecture-policy.test.ts`
- [ ] Add fixtures proving contracts cannot import application/runtime layers, feature adapters cannot import concrete global adapters, unresolved imports fail, cycles fail, and a zero-module root fails.
- [ ] Run `corepack pnpm exec vitest run tests/unit/architecture-policy.test.ts` and confirm missing rules/zero-module behavior fail.
- [ ] Make the Babel parser plus Node/TS resolver graph authoritative. Keep dependency-cruiser output informational while it sees zero TS7 modules, and explicitly fail authoritative counts of zero modules or zero dependencies in a non-empty source tree.
- [ ] Add the two dependency-direction rules to the typed layer policy and ensure aliases/extensions resolve identically to TypeScript.
- [ ] Run focused tests and `corepack pnpm check:architecture`; commit with `git commit -m "fix: enforce architecture with the TS7 graph"`.
### Task 5: Test hygiene and production read/write E2E
**Files:**
- Modify: `vitest.config.ts`
- Modify: `tests/setup.ts`
- Modify: `playwright.config.ts`
- Modify: `playwright.dev.config.ts`
- Modify: `playwright.storybook.config.ts`
- Modify: `playwright.visual.config.ts`
- Create: `tests/e2e/reference-resource-write.spec.ts`
- Modify: `scripts/check-test-evidence.ts`
- [ ] Add a fixture containing `.only` and a leaking fake timer; assert the gate rejects/isolation restores them. Assert every Playwright config resolves `forbidOnly: true`.
- [ ] Add E2E that loads a real mocked GET response, submits POST, verifies request body/header contract, verifies response-rendered resource, then reloads and verifies read-after-write.
- [ ] Run focused Vitest and Playwright tests and confirm RED for inherited configs/current shallow E2E.
- [ ] Enable Vitest sequence hook that rejects `.only`, restore real timers in common `afterEach`, and centralize a Playwright base config with `forbidOnly: true` inherited by all configs.
- [ ] Make E2E evidence require both observed response and observed mutation receipt.
- [ ] Re-run supported tests; commit with `git commit -m "test: harden test isolation and read-write E2E"`.
### Task 6: Quality verification
- [ ] Run `corepack pnpm check:architecture`.
- [ ] Run `corepack pnpm test:coverage`.
- [ ] Run `node scripts/check-test-evidence.ts`.
- [ ] Run `node scripts/generate-ci-workflow.ts --check`.
- [ ] Run `corepack pnpm test:all`, `corepack pnpm check:types`, `corepack pnpm lint`, and `git diff --check`.
- [ ] Run browser/E2E gates only when the environment supports them and report exact commands separately.
@@ -0,0 +1,110 @@
# Release Evidence Remediation Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Build one immutable release bundle and fail promotion unless every artifact, provider report, schema, and digest proves it describes those exact bytes.
**Architecture:** Executable Zod contracts validate artifacts at every writer boundary. One fail-closed tracked-file inventory feeds security and provenance. CI creates the candidate once, scans that candidate, then promotes it without rebuilding.
**Tech Stack:** TypeScript 7, Node.js 24, Zod 4, Vite 8, Gitea Actions, Vitest 4.
## Global Constraints
- Repository code validates but never fabricates external vulnerability or signed provenance evidence.
- Missing evidence, unreadable required roots, tool crashes, signals, timeouts, and digest mismatch fail closed.
- V2 release identity is its exact contract package set and recomputed set digest; no scalar version is synthesized.
- Promotion consumes the same archived `dist` and `distSha256` produced by `immutable_build`.
- All production changes start with a failing fixture or unit test.
---
### Task 1: One V1/V2 runtime coherence verifier
**Files:**
- Create: `scripts/lib/release-runtime-coherence.ts`
- Modify: `scripts/verify-release.ts`
- Modify: `scripts/drill-runbook.ts`
- Modify: `src/contracts/release-tokens.ts`
- Modify: `tests/unit/release-coherence.test.ts`
- Modify: `tests/unit/release-artifacts.test.ts`
- [ ] Add a shared matrix covering V1 scalar success/mismatch and V2 package add/remove/version/digest tampering. Assert verifier and rollback drill return identical verdicts.
- [ ] Run `corepack pnpm exec vitest run tests/unit/release-coherence.test.ts tests/unit/release-artifacts.test.ts` and confirm RED because the drill compares only scalar release tokens.
- [ ] Implement async `verifyReleaseRuntimeCoherence({ release, runtime, contractPackages })`; V1 delegates to legacy scalar policy, V2 checks exact sorted package tuples then recomputes `contractSet.setDigest`.
- [ ] Remove V2 synthetic `0`/legacy scalar projection from `release-tokens.ts`; call the shared verifier from both scripts.
- [ ] Re-run the focused tests and commit with `git commit -m "fix: unify release runtime coherence verification"`.
### Task 2: Validated artifact writers and generated JSON schemas
**Files:**
- Create: `scripts/lib/validated-json-artifact.ts`
- Create: `scripts/generate-artifact-schemas.ts`
- Modify: `scripts/contracts/release-artifacts.ts`
- Modify: `scripts/generate-build-manifest.ts`
- Modify: `scripts/generate-supply-chain.ts`
- Modify: `scripts/collect-web-vitals-evidence.ts`
- Modify: `scripts/test-performance.ts`
- Modify: `scripts/verify-release.ts`
- Modify: `scripts/drill-runbook.ts`
- Modify: `schemas/artifacts/build-manifest.schema.json`
- Modify: `schemas/artifacts/dependency-inventory.schema.json`
- Modify: `schemas/artifacts/registry-snapshot.schema.json`
- Modify: `schemas/artifacts/supply-chain-verification.schema.json`
- Create: `tests/unit/validated-json-artifact.test.ts`
- Modify: `tests/unit/release-artifacts.test.ts`
- Modify: `tests/unit/json-schema.test.ts`
- [ ] Add tests proving invalid values do not touch the destination, a valid write is atomic, and `generate-artifact-schemas.ts --check` reports checked-in drift.
- [ ] Run focused tests and confirm RED because writers call `writeFile` directly and schemas are hand-maintained.
- [ ] Implement `writeValidatedJsonArtifact({ path, schema, value })`: parse first, write a sibling temporary file, rename atomically, and clean only its explicit temp file on failure.
- [ ] Route every listed writer through the helper. Generate draft-2020-12 schemas deterministically with `additionalProperties: false` and stable final newline.
- [ ] Add `generate:artifact-schemas` and `check:artifact-schemas` scripts; run generation then check mode.
- [ ] Run `corepack pnpm exec vitest run tests/unit/validated-json-artifact.test.ts tests/unit/release-artifacts.test.ts tests/unit/json-schema.test.ts` and commit with `git commit -m "refactor: validate generated evidence artifacts"`.
### Task 3: Manifest outputs and fail-closed repository inventory
**Files:**
- Create: `scripts/lib/repository-file-inventory.ts`
- Create: `scripts/lib/build-manifest-outputs.ts`
- Modify: `scripts/generate-supply-chain.ts`
- Modify: `scripts/security-scan.ts`
- Modify: `scripts/verify-release.ts`
- Modify: `config/security/secret-scan-policy.json`
- Modify: `tests/unit/supply-chain.test.ts`
- Create: `tests/unit/repository-file-inventory.test.ts`
- Modify: `tests/unit/release-artifacts.test.ts`
- [ ] Add fixtures for missing required root, optional `ENOENT`, unreadable file, untracked omission, path traversal, module-inventory tamper, and hash mismatch.
- [ ] Run focused tests and confirm current discovery skips read failures and verification accepts a stale `moduleInventoryHash`.
- [ ] Build inventory from `git ls-files -z` plus explicitly generated inputs; normalize and confine every path under repository root. Only configured optional roots may ignore exact `ENOENT`.
- [ ] Make provenance and secret scan consume the same inventory. Add `index.html`, Vite configs, all TS configs, `.nvmrc`, package/lock files, scripts, schemas, configs, and `.gitea/workflows/quality-gates.yml` to mandatory policy coverage.
- [ ] Implement `verifyBuildManifestOutputs` to confine declared output paths, read module inventory bytes, and compare raw SHA-256 to `moduleInventoryHash`.
- [ ] Re-run focused tests and commit with `git commit -m "fix: fail closed on release input discovery"`.
### Task 4: Immutable candidate, provider evidence, and promotion
**Files:**
- Modify: `package.json`
- Modify: `scripts/generate-supply-chain.ts`
- Modify: `scripts/verify-supply-chain-artifacts.ts`
- Modify: `scripts/verify-supply-chain-promotion.ts`
- Modify: `scripts/check-supply-chain-provider-fixtures.ts`
- Modify: `tests/unit/supply-chain.test.ts`
- Modify: `.gitea/workflows/quality-gates.yml`
- [ ] Add fixtures for absent provider evidence, valid matching digest, wrong digest, and post-attestation byte change. Assert only the valid immutable fixture passes promotion.
- [ ] Run `corepack pnpm exec vitest run tests/unit/supply-chain.test.ts && corepack pnpm check:supply-chain:provider-fixtures` and confirm RED for promotion wiring.
- [ ] Split scripts into `build:release-candidate`, `verify:local-evidence`, `verify:provider-evidence`, and `verify:promotion`; remove any build command from promotion.
- [ ] `immutable_build` archives `dist`, build manifest, module inventory, and local evidence together and publishes `distSha256`. Provider jobs download that archive and emit reports bound to the digest.
- [ ] Promotion downloads the same archive plus provider reports, exports `VULNERABILITY_REPORT_PATH` and `PROVENANCE_ATTESTATION_PATH`, verifies all schemas/signatures/digests, and uploads/deploys the unchanged bundle.
- [ ] Verify missing external evidence remains `FAIL_UNVERIFIED`; do not add a repository-generated passing provider fixture to production flow.
- [ ] Re-run fixtures and the workflow contract check, then commit with `git commit -m "fix: promote immutable verified release bundles"`.
### Task 5: Release/evidence verification
- [ ] Run `corepack pnpm check:artifact-schemas`.
- [ ] Run `corepack pnpm exec vitest run tests/unit/release-artifacts.test.ts tests/unit/release-coherence.test.ts tests/unit/validated-json-artifact.test.ts tests/unit/repository-file-inventory.test.ts tests/unit/supply-chain.test.ts tests/unit/json-schema.test.ts`.
- [ ] Run `corepack pnpm check:supply-chain:fixtures` and `corepack pnpm check:supply-chain:provider-fixtures`.
- [ ] Run the candidate build and local release verification with deterministic local environment values.
- [ ] Confirm promotion fails specifically with `FAIL_UNVERIFIED` when real external evidence paths are absent.
- [ ] Run `corepack pnpm check:types`, `corepack pnpm lint`, and `git diff --check`.
@@ -0,0 +1,281 @@
# Runtime Correctness Remediation Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make topic invalidation hit every real bound query and make each logical mutation preserve a unique intent and effect-aware optimistic state.
**Architecture:** Contracts own query-key and mutation-intent shapes. Bootstrap indexes feature invalidation contributions once. Presentation creates one intent per admitted logical submit, while HTTP consumes that intent and optimistic settlement follows the returned effect certainty.
**Tech Stack:** TypeScript 7, React 19, TanStack Query 5, Zod 4, Vitest 4.
## Global Constraints
- Query persistence remains disabled; the key-version change has no persisted migration.
- Query keys and invalidation prefixes are created only by `src/contracts/query-keys.ts`.
- Cross-context messages carry topics, never namespace IDs, query keys, input identities, intent IDs, or idempotency keys.
- A logical mutation creates one intent after duplicate admission and reuses it for every physical attempt.
- A missing `KEYED` idempotency key fails before credentials, fetch admission, or diagnostics containing caller data.
- `MAYBE_APPLIED` never rolls back, commits, invalidates, or retries automatically.
- Every production behavior change is preceded by a focused failing test.
---
### Task 1: Query-key V2 and namespace identities
**Files:**
- Modify: `src/contracts/query-keys.ts`
- Modify: `src/contracts/server-state.ts`
- Modify: `tests/component/application-query.test.tsx`
- Modify: `tests/unit/query-invalidation-registry.test.ts`
**Interfaces:**
- Adds `QUERY_KEY_SCHEMA_VERSION = 2`.
- Adds `QueryNamespaceIdentity = { namespaceId: string; namespaceVersion: number }`.
- Adds `defineQueryNamespaceIdentity`, `createBoundQueryKey`, `createQueryInvalidationPrefix`, and `queryNamespaceIdentityKey`.
- Changes `bindQuery` to delegate key construction to `createBoundQueryKey`.
- [ ] **Step 1: Add failing key/prefix parity tests**
```ts
const namespace = defineQueryNamespaceIdentity("reference-resource", 1);
const bound = bindQuery(definition, input, scope);
expect(bound.queryKey).toEqual([
"query", 2, "reference-resource", 1,
scope.fingerprint, definition.definitionVersion, bound.identity.token,
]);
expect(bound.queryKey.slice(0, 4)).toEqual(
createQueryInvalidationPrefix(namespace),
);
```
Also reject empty/control-character IDs, non-positive versions, and excessive UTF-8 length.
- [ ] **Step 2: Run RED**
Run: `corepack pnpm exec vitest run tests/component/application-query.test.tsx tests/unit/query-invalidation-registry.test.ts`
Expected: missing helper exports and current V1 key order mismatch.
- [ ] **Step 3: Implement the shared constructors**
`createBoundQueryKey` must return exactly:
```ts
Object.freeze([
"query", QUERY_KEY_SCHEMA_VERSION,
namespace.namespaceId, namespace.namespaceVersion,
scopeFingerprint, definitionVersion, identityToken,
]);
```
`createQueryInvalidationPrefix` returns the first four entries. `bindQuery` constructs the namespace identity from the definition rather than duplicating the tuple.
- [ ] **Step 4: Run GREEN**
Run: `corepack pnpm exec vitest run tests/component/application-query.test.tsx tests/unit/query-invalidation-registry.test.ts`
- [ ] **Step 5: Commit**
```bash
git add src/contracts/query-keys.ts src/contracts/server-state.ts tests/component/application-query.test.tsx tests/unit/query-invalidation-registry.test.ts
git commit -m "fix: align bound query keys with invalidation prefixes"
```
### Task 2: Many-to-many invalidation in production composition
**Files:**
- Modify: `src/contracts/query-invalidation.ts`
- Modify: `src/features/reference-feature/contracts/reference-feature-contract.ts`
- Modify: `src/features/installed-feature-contracts.ts`
- Modify: `src/adapters/query-cache/tanstack-cache-coordinator.ts`
- Modify: `src/bootstrap/runtime-adapters.ts`
- Modify: `tests/unit/query-invalidation-registry.test.ts`
- Modify: `tests/unit/tanstack-cache-coordinator.test.ts`
- Modify: `tests/features/reference-feature/reference-contract.test.ts`
- Modify: `tests/unit/runtime-adapters.test.ts`
**Interfaces:**
- `InvalidationRegistry.namespaces` and edges use `QueryNamespaceIdentity`.
- `indexInvalidationRegistry` returns every namespace identity for each topic.
- `createTanStackCacheCoordinator` consumes `InvalidationRegistryIndex`; topic versions remain a separate bounded map used only by cross-context transport.
- Installed features export `INVALIDATION_REGISTRY`, composed once at bootstrap.
- [ ] **Step 1: Add failing real-key invalidation and fan-out tests**
Seed `QueryClient` with real `bindQuery(...).queryKey` values, map one topic to two namespaces, call local and remote invalidation, and assert both matching queries are invalidated while an unrelated namespace is not. Assert the published event contains only topic/version.
- [ ] **Step 2: Run RED**
Run: `corepack pnpm exec vitest run tests/unit/query-invalidation-registry.test.ts tests/unit/tanstack-cache-coordinator.test.ts tests/features/reference-feature/reference-contract.test.ts tests/unit/runtime-adapters.test.ts`
Expected: the coordinator accepts the legacy flat registry and invalidates prefixes that do not match bound keys.
- [ ] **Step 3: Compose and index contributions once**
Feature contribution shape:
```ts
invalidation: Object.freeze({
topics: [REFERENCE_RESOURCE_INVALIDATION_TOPIC],
namespaces: [defineQueryNamespaceIdentity("reference-resource", 1)],
edges: [{
topicId: REFERENCE_RESOURCE_INVALIDATION_TOPIC,
namespace: defineQueryNamespaceIdentity("reference-resource", 1),
}],
})
```
`installed-feature-contracts.ts` concatenates these bounded contributions. `runtime-adapters.ts` calls `indexInvalidationRegistry(INVALIDATION_REGISTRY)` exactly once, derives transport topic versions, and passes the index to each generation's coordinator.
- [ ] **Step 4: Make the coordinator invalidate every indexed prefix**
For each topic, iterate `namespacesForTopic.get(topic)`, create the V2 prefix with `createQueryInvalidationPrefix`, and call `invalidateQueries({ exact: false, refetchType: "active" })`. Sequence-gap handling visits all indexed topics without duplicating namespace work.
- [ ] **Step 5: Remove legacy authorities**
Delete `QUERY_REGISTRY` from `src/contracts/query-keys.ts`, the flat installed `QUERY_REGISTRY`, `InstalledQueryInvalidationDefinition`, and feature-owned concrete TanStack namespace tuples after `rg` shows zero callers.
- [ ] **Step 6: Run GREEN**
Run: `corepack pnpm exec vitest run tests/unit/query-invalidation-registry.test.ts tests/unit/tanstack-cache-coordinator.test.ts tests/features/reference-feature/reference-contract.test.ts tests/unit/runtime-adapters.test.ts`
- [ ] **Step 7: Commit**
```bash
git add src/contracts/query-invalidation.ts src/features/reference-feature/contracts/reference-feature-contract.ts src/features/installed-feature-contracts.ts src/adapters/query-cache/tanstack-cache-coordinator.ts src/bootstrap/runtime-adapters.ts tests/unit/query-invalidation-registry.test.ts tests/unit/tanstack-cache-coordinator.test.ts tests/features/reference-feature/reference-contract.test.ts tests/unit/runtime-adapters.test.ts
git commit -m "fix: index many-to-many query invalidation"
```
### Task 3: Application-owned mutation intent
**Files:**
- Create: `src/contracts/mutation-intent.ts`
- Create: `src/application/ports/mutation-intent-factory.ts`
- Create: `src/adapters/platform/browser-mutation-intent-factory.ts`
- Modify: `src/contracts/server-state.ts`
- Modify: `src/presentation/adapters/query/application-query.ts`
- Create: `src/presentation/adapters/query/mutation-intent-provider.tsx`
- Modify: `src/presentation/adapters/query/server-state-generation-provider.tsx`
- Modify: `src/bootstrap/runtime-adapters.ts`
- Modify: `src/features/reference-feature/adapters/reference-http-gateway.ts`
- Modify: `src/adapters/http/http-effect-certainty.ts`
- Modify: `src/adapters/http/http-execution-v3.ts`
- Modify: `tests/component/application-query.test.tsx`
- Modify: `tests/unit/runtime-adapters.test.ts`
- Modify: `tests/unit/http-execution-v3.test.ts`
- Modify: `tests/integration/http-execution-contract.test.ts`
**Interfaces:**
- `MutationIntent` has the exact approved immutable shape.
- `MutationIntentFactory.create({ operationId, canonicalInputIdentity, requiresIdempotencyKey })` returns one intent.
- `BoundMutation.execute` context adds `intent: MutationIntent`.
- `HttpExecutionContext.intent` consumes the application intent without regenerating it.
- [ ] **Step 1: Add failing lifecycle tests**
Assert two independent submits receive different intent/key pairs; a `JOIN_IDENTICAL` waiter shares the admitted submit; physical HTTP retry sees the same key; queries have no intent header; diagnostics and URLs contain neither intent ID nor key.
- [ ] **Step 2: Run RED**
Run: `corepack pnpm exec vitest run tests/component/application-query.test.tsx tests/unit/runtime-adapters.test.ts tests/unit/http-execution-v3.test.ts tests/integration/http-execution-contract.test.ts`
Expected: bound mutation context has no intent and bootstrap produces resettable `http-key-N` values.
- [ ] **Step 3: Define intent validation and browser factory**
Validate bounded non-empty strings and finite non-negative monotonic timestamps. Use `crypto.randomUUID()` independently for `intentId` and required idempotency key; permit deterministic injected factories in tests.
- [ ] **Step 4: Create intent after duplicate admission**
Keep canonical identity calculation before duplicate lookup. Only the execution that wins admission calls the factory. Pass the same frozen intent through `mutation.mutateAsync({ input, intent })` and every bound mutation/feature gateway call.
- [ ] **Step 5: Remove adapter-local sequence identity**
Delete `contractExecutionSequence`, `http-intent-N`, `http-key-N`, and the unused HTTP-layer `MutationIntent` factory. Bootstrap passes the supplied intent into `ContractHttpExecutor` unchanged.
- [ ] **Step 6: Run GREEN**
Run the command from Step 2 and expect all intent lifecycle assertions to pass.
- [ ] **Step 7: Commit**
```bash
git add src/contracts/mutation-intent.ts src/application/ports/mutation-intent-factory.ts src/adapters/platform/browser-mutation-intent-factory.ts src/contracts/server-state.ts src/presentation/adapters/query/application-query.ts src/presentation/adapters/query/mutation-intent-provider.tsx src/presentation/adapters/query/server-state-generation-provider.tsx src/bootstrap/runtime-adapters.ts src/features/reference-feature/adapters/reference-http-gateway.ts src/adapters/http/http-effect-certainty.ts src/adapters/http/http-execution-v3.ts tests/component/application-query.test.tsx tests/unit/runtime-adapters.test.ts tests/unit/http-execution-v3.test.ts tests/integration/http-execution-contract.test.ts
git commit -m "fix: preserve logical mutation intent"
```
### Task 4: Fail KEYED commands before dispatch
**Files:**
- Modify: `src/adapters/http/http-execution-v3.ts`
- Modify: `tests/unit/http-execution-v3.test.ts`
- Modify: `tests/integration/http-execution-contract.test.ts`
- [ ] Add tests for absent, empty, control-character, and over-budget keys. Spy on `attachCredentials` and `fetch`; both must remain at zero and the result must be `CONTRACT_VIOLATION` with `effect: "NOT_STARTED"`.
- [ ] Run `corepack pnpm exec vitest run tests/unit/http-execution-v3.test.ts tests/integration/http-execution-contract.test.ts` and confirm RED because KEYED commands currently dispatch without a key.
- [ ] Add `MISSING_IDEMPOTENCY_KEY` to the request violation union and validate before credential resolution. Reject a key on `NONE`/query descriptors as the same pre-dispatch contract class.
- [ ] Re-run the focused tests and confirm GREEN, including same-key physical retry.
- [ ] Commit with `git commit -m "fix: reject invalid keyed mutation intents"`.
### Task 5: Effect-aware optimistic settlement and reconciliation
**Files:**
- Modify: `src/presentation/adapters/query/optimistic-layer-runtime.ts`
- Modify: `src/presentation/adapters/query/application-query.ts`
- Modify: `src/application/view-models/async-state.ts`
- Modify: `src/contracts/errors.ts`
- Modify: `tests/unit/optimistic-layer-runtime.test.ts`
- Modify: `tests/component/application-query.test.tsx`
**Interfaces:**
- `OptimisticLayerLease` adds `markUncertain()` and `reconcile("APPLIED" | "NOT_APPLIED")`.
- Layer status becomes `pending | uncertain | committed`; collapse stops before unresolved uncertain layers.
- Controller adds `reconcileUnknownEffect(resolution)` tied to the original intent.
- Async state adds `mutation-effect-unknown`.
- [ ] **Step 1: Add failing certainty matrix tests**
Cover `NOT_STARTED`, `NOT_APPLIED`, `APPLIED_CONFIRMED`, and `MAYBE_APPLIED`; out-of-order later commits; applied/not-applied reconciliation; scope closure. Assert unknown effect does not call `invalidate` or expose generic retry.
- [ ] **Step 2: Run RED**
Run: `corepack pnpm exec vitest run tests/unit/optimistic-layer-runtime.test.ts tests/component/application-query.test.tsx`
Expected: current catch path rolls every failure back.
- [ ] **Step 3: Derive settlement before touching optimistic state**
Use `failure.effect ?? "NOT_STARTED"` only for failures known to be pre-dispatch. The mutation bridge switches explicitly:
```ts
switch (effect) {
case "NOT_STARTED":
case "NOT_APPLIED": rollback(); break;
case "APPLIED_CONFIRMED": commit(); scheduleInvalidation(); break;
case "MAYBE_APPLIED": markUncertain(); exposeReconciliation(); break;
}
```
- [ ] **Step 4: Preserve ordered uncertain layers**
Projection still applies uncertain layers. `collapse` may consume committed layers only until the first pending/uncertain layer. Reconciliation converts uncertain to committed or removes it, then reprojects all later layers.
- [ ] **Step 5: Run GREEN**
Run the command from Step 2 and expect all certainty and ordering cases to pass.
- [ ] **Step 6: Commit**
```bash
git add src/presentation/adapters/query/optimistic-layer-runtime.ts src/presentation/adapters/query/application-query.ts src/application/view-models/async-state.ts src/contracts/errors.ts tests/unit/optimistic-layer-runtime.test.ts tests/component/application-query.test.tsx
git commit -m "fix: retain uncertain optimistic mutations"
```
### Task 6: Runtime correctness verification
- [ ] Run `corepack pnpm exec vitest run tests/unit/query-invalidation-registry.test.ts tests/unit/tanstack-cache-coordinator.test.ts tests/unit/http-execution-v3.test.ts tests/unit/optimistic-layer-runtime.test.ts tests/component/application-query.test.tsx tests/integration/http-execution-contract.test.ts tests/features/reference-feature/reference-contract.test.ts tests/unit/runtime-adapters.test.ts`.
- [ ] Run `corepack pnpm check:types`.
- [ ] Run `corepack pnpm lint`.
- [ ] Run `corepack pnpm test:all`.
- [ ] Run `git diff --check`.
- [ ] Record any browser-only gate as unverified unless its Playwright command actually ran.