From 5bcbeffa9da91c41633284ede4082c12fa6a9022 Mon Sep 17 00:00:00 2001 From: Nicholas Petrasek Date: Wed, 30 Sep 2026 21:32:06 +0200 Subject: [PATCH] chore: clean release documentation and patch development dependency --- .../2026-09-03-immuview-v2-implementation.md | 707 ------------------ .../2026-09-03-legal-deployment-release.md | 409 ---------- ...-09-04-product-story-and-launch-content.md | 269 ------- .../specs/2026-09-03-immuview-v2-design.md | 352 --------- ...6-09-03-legal-deployment-release-design.md | 69 -- ...-09-04-product-story-and-launch-content.md | 283 ------- package-lock.json | 6 +- 7 files changed, 3 insertions(+), 2092 deletions(-) delete mode 100644 docs/superpowers/plans/2026-09-03-immuview-v2-implementation.md delete mode 100644 docs/superpowers/plans/2026-09-03-legal-deployment-release.md delete mode 100644 docs/superpowers/plans/2026-09-04-product-story-and-launch-content.md delete mode 100644 docs/superpowers/specs/2026-09-03-immuview-v2-design.md delete mode 100644 docs/superpowers/specs/2026-09-03-legal-deployment-release-design.md delete mode 100644 docs/superpowers/specs/2026-09-04-product-story-and-launch-content.md diff --git a/docs/superpowers/plans/2026-09-03-immuview-v2-implementation.md b/docs/superpowers/plans/2026-09-03-immuview-v2-implementation.md deleted file mode 100644 index c9241bb..0000000 --- a/docs/superpowers/plans/2026-09-03-immuview-v2-implementation.md +++ /dev/null @@ -1,707 +0,0 @@ -# ReadonlyView v2 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:** Rebuild and rename ImmuView as ReadonlyView: a production-grade, deeply readonly, lazy, live JavaScript membrane with a minimal API, verified package, documentation website, and release-ready automation. - -**Architecture:** Each `readonlyView` call owns a WeakMap-backed membrane that maps sources to invariant-safe shadow proxies and maps views back to sources. Generic objects/functions and dedicated Array, Map, Set, and Date adapters delegate reads live, wrap every exposed supported value, and reject every mutation route; unsupported built-ins fail lazily and explicitly. - -**Tech Stack:** Node.js 24 LTS, TypeScript, Vite, Vitest, fast-check, ESLint flat config, Prettier, Playwright, esbuild, npm, vanilla TypeScript/CSS documentation site. - -**Spec:** `docs/superpowers/specs/2026-09-03-immuview-v2-design.md` - -## Global Constraints - -- Package identity is `@nipe-solutions/readonly-view`, product identity is `ReadonlyView`, repository identity is `NIPE-Solutions/readonly-view`, and development version is `2.0.0-alpha.0`; do not publish. -- Runtime support is Node.js 22 and 24 plus current evergreen Chromium, Firefox, and WebKit; development and release use Node.js 24 LTS. -- Runtime dependency count is exactly zero. -- Public exports are exactly `readonlyView`, `isReadonlyView`, `DirectMutationError`, `UnsupportedTypeError`, and `DeepReadonly`. -- ReadonlyView never intentionally mutates, freezes, seals, prevents extensions on, serializes, or eagerly traverses a source. -- Separate top-level calls create separate membranes; identity is stable inside a membrane; passing an existing view is idempotent. -- Direct source proxies remain subject to the documented trust limitation. -- Unsupported built-ins throw `UnsupportedTypeError` when first reached. -- Runtime behavior follows red-green-refactor; configuration and generated-output tasks are verified immediately. -- Preserve v1 tags and history; remove the v1 API without a compatibility layer. - ---- - -### Task 1: Modern toolchain and repository skeleton - -**Files:** - -- Modify: `package.json` -- Replace: `package-lock.json`, `tsconfig.json` -- Create: `.nvmrc`, `tsconfig.build.json`, `vite.config.ts`, `vitest.config.ts`, `eslint.config.js`, `.prettierignore`, `test/setup.ts` -- Remove: `jest.config.js`, `.eslintrc.js`, `.prettierrc`, `.npmignore` - -**Interfaces:** - -- Consumes: Node.js 24 and npm. -- Produces: strict TypeScript, Vitest projects, dual-format build commands, and format/lint/type/test scripts. - -- [ ] **Step 1: Replace package metadata and scripts** - -Set name `@nipe-solutions/readonly-view`, version `2.0.0-alpha.0`, `type: "module"`, `engines.node: ">=22 <25"`, zero runtime dependencies, and: - -```json -{ - "main": "./dist/index.cjs", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js", - "require": "./dist/index.cjs" - }, - "./package.json": "./package.json" - }, - "files": ["dist"], - "sideEffects": false -} -``` - -Install current mutually compatible TypeScript, Vite, Vitest/coverage, ESLint flat-config packages, typescript-eslint, Prettier, fast-check, Playwright, esbuild, and Node types as dev dependencies. - -- [ ] **Step 2: Configure strict compilation and tests** - -Use ES2022, Bundler resolution, `strict`, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`, `noImplicitOverride`, `useUnknownInCatchVariables`, `verbatimModuleSyntax`, and `skipLibCheck: false`. Configure Vitest for `test/unit`, `test/integration`, and `test/regression`. - -- [ ] **Step 3: Install and verify the harness** - -Run: - -```bash -npm install -npm outdated || true -npm ls --depth=0 -npm run format:check -npm run lint -npm run typecheck -npm run test:unit -- --passWithNoTests -``` - -Expected: all validation commands exit 0 and no Jest reference remains. - -- [ ] **Step 4: Commit** - -```bash -git add package.json package-lock.json .nvmrc tsconfig.json tsconfig.build.json vite.config.ts vitest.config.ts eslint.config.js .prettierignore test/setup.ts -git add -u -git commit -m "build: modernize the v2 toolchain" -``` - -### Task 2: Public types and error contract - -**Files:** - -- Create: `src/public-types.ts`, `src/errors.ts`, `test/types/deep-readonly.test-d.ts`, `test/unit/errors.test.ts` -- Replace: `src/index.ts` -- Remove: `src/ImmuView.ts`, `src/ImmuView.unit.spec.ts` - -**Interfaces:** - -- Consumes: strict compiler. -- Produces: `DeepReadonly`, errors, and exact root exports; runtime functions may temporarily throw until Task 3. - -- [ ] **Step 1: Write failing compile-time tests** - -Cover primitives, nullability, symbols, unions, recursion, arrays, labeled/optional/rest tuples, Map, ReadonlyMap, Set, ReadonlySet, Date setter absence, and call signatures: - -```ts -declare const view: DeepReadonly<{ - tuple: [number, { value: string }]; - map: Map<{ id: number }, { value: string }>; - date: Date; -}>; -// @ts-expect-error deeply readonly -view.tuple[1].value = 'changed'; -// @ts-expect-error readonly collection -view.map.set({ id: 1 }, { value: 'x' }); -// @ts-expect-error readonly Date -view.date.setTime(0); -``` - -Run `npm run test:types`; expect missing-type failures. - -- [ ] **Step 2: Implement the public type** - -Implement a distributive conditional in this order: primitives, functions, Date structural reader, tuple/array, Map, Set, object mapped type. Keep helper types private. - -- [ ] **Step 3: Write failing error tests** - -Assert `instanceof`, `name`, safe message, `operation`, optional `property`, and `objectKind` for `DirectMutationError`; assert kind metadata for `UnsupportedTypeError`. Include symbol properties and prove no source serialization. - -- [ ] **Step 4: Implement errors and exports** - -Use: - -```ts -new DirectMutationError({ - operation: 'set', - property: 'name', - objectKind: 'Object', -}); -new UnsupportedTypeError('RegExp'); -``` - -Do not retain source objects. - -- [ ] **Step 5: Verify and commit** - -```bash -npm run test:types -npm run test:unit -- test/unit/errors.test.ts -npm run build:dist -git add src test -git add -u -git commit -m "feat: define the v2 public contract" -``` - -Expected: declarations expose only the five approved exports and no internal helper. - -### Task 3: Live object membrane and invariant-safe reflection - -**Files:** - -- Create: `src/membrane.ts`, `src/classify.ts` -- Modify: `src/index.ts` -- Create: `test/unit/objects.test.ts`, `test/unit/reflection.test.ts`, `test/regression/proxy-invariants.test.ts`, `test/integration/identity.test.ts`, `test/integration/source-integrity.test.ts` - -**Interfaces:** - -- Consumes: public types/errors. -- Produces: `createMembrane().wrap`, `readonlyView`, `isReadonlyView`, source-to-view and view-to-source WeakMap metadata. - -- [ ] **Step 1: Write failing root/liveness/identity tests** - -Test primitives unchanged, separate membranes unequal, existing-view idempotence, no eager getter invocation, source replace/add/delete visibility, cycles, and shared references. Run targeted tests and confirm failure from the missing implementation. - -- [ ] **Step 2: Implement minimal registration and `get`** - -Use: - -```ts -interface Membrane { - wrap(value: T): DeepReadonly; - unwrapSameMembrane(value: unknown): unknown; -} -``` - -Create one extensible object-shaped shadow, register it before return, and only wrap a nested value from a trap. - -- [ ] **Step 3: Write failing reflection/invariant tests** - -Test keys, values, entries, names, symbols, descriptors, `Reflect.ownKeys/get/has`, `in`, `for...in`, spread, destructuring, null/custom prototypes, `instanceof`, and descriptor values. Include frozen/sealed/non-extensible sources and non-configurable/non-writable nested values. - -- [ ] **Step 4: Implement reflection traps** - -Add intentional `getOwnPropertyDescriptor`, `ownKeys`, `has`, and `getPrototypeOf`. Virtualize descriptors onto the extensible shadow, wrap data values, preserve accessor functions safely, and never invoke getters during enumeration. - -- [ ] **Step 5: Write failing mutation-route tests** - -Cover assignment, nested assignment, delete, defineProperty, Object.assign, all relevant Reflect operations, setPrototypeOf, preventExtensions, and symbol keys. - -- [ ] **Step 6: Implement rejecting mutation traps** - -All routes throw a shared `DirectMutationError` helper; none forwards or returns false. - -- [ ] **Step 7: Verify source integrity and commit** - -```bash -npm run test:unit -- test/unit/objects.test.ts test/unit/reflection.test.ts test/regression/proxy-invariants.test.ts test/integration/identity.test.ts test/integration/source-integrity.test.ts -git add src test -git commit -m "feat: add the invariant-safe live object membrane" -``` - -Expected: descriptor/prototype/extensibility snapshots remain byte-for-byte equivalent before and after creation/reads. - -### Task 4: Functions, accessors, and custom classes - -**Files:** - -- Create: `src/functions.ts` -- Modify: `src/membrane.ts` -- Create: `test/unit/functions.test.ts`, `test/unit/accessors.test.ts`, `test/unit/classes.test.ts`, `test/regression/method-escape.test.ts` - -**Interfaces:** - -- Consumes: membrane wrap and private metadata. -- Produces: callable shadows with readonly view receivers and wrapped results. - -- [ ] **Step 1: Write failing method tests** - -Test `this.count++` throws without source change, read methods work, closure mutation remains possible, detached calls preserve JS semantics, function-owned objects are wrapped, and repeated reads preserve identity. - -- [ ] **Step 2: Implement callable/construct traps** - -If `thisArg` is a known view, keep it readonly; otherwise preserve the explicit receiver. Wrap returned objects. Construct with `Reflect.construct` only for constructable sources and wrap the result. - -- [ ] **Step 3: Write failing accessor/class tests** - -Test getters receive readonly `this`, returned values are wrapped, enumeration does not invoke getters, setters are blocked, prototype reflection is protected, the documented `instanceof` tradeoff holds, public class writes fail, and private-brand access rejects without mutable rebinding. - -- [ ] **Step 4: Implement accessor/class semantics** - -Use `Reflect.get(source, key, receiver)`. Descriptor accessors must not expose source-bound mutable receivers. Unknown custom prototypes use this generic path. - -- [ ] **Step 5: Verify and commit** - -```bash -npm run test:unit -- test/unit/functions.test.ts test/unit/accessors.test.ts test/unit/classes.test.ts test/regression/method-escape.test.ts -git add src test -git commit -m "feat: secure functions accessors and class receivers" -``` - -### Task 5: Arrays and tuple fidelity - -**Files:** - -- Create: `src/builtins/array.ts` -- Modify: `src/membrane.ts`, `test/types/deep-readonly.test-d.ts` -- Create: `test/unit/arrays.test.ts`, `test/regression/array-escapes.test.ts` - -**Interfaces:** - -- Consumes: membrane/callable behavior. -- Produces: an array shadow for which `Array.isArray(view)` is true. - -- [ ] **Step 1: Write failing array read/mutation tests** - -Cover live indexes/length/holes, symbols, iteration, all listed non-mutating methods, nested outputs, and every mutator: copyWithin, fill, pop, push, reverse, shift, sort, splice, unshift. Cover index/delete/length writes. - -- [ ] **Step 2: Run and confirm array length/invariant failures** - -Run `npm run test:unit -- test/unit/arrays.test.ts test/regression/array-escapes.test.ts`. - -- [ ] **Step 3: Implement the array adapter** - -Use `[]` as shadow, report its unavoidable non-configurable `length` compatibly, delegate live reads, reject the exact mutator set, and operate non-mutating methods against the view so callback values are protected. - -- [ ] **Step 4: Extend compile tests** - -Cover labeled, optional, readonly, variadic, and nested tuples; verify fixed positions/rest values are deeply readonly. - -- [ ] **Step 5: Verify and commit** - -```bash -npm run test:unit -- test/unit/arrays.test.ts test/regression/array-escapes.test.ts -npm run test:types -git add src test -git commit -m "feat: add invariant-safe readonly arrays" -``` - -### Task 6: Map and Set adapters - -**Files:** - -- Create: `src/builtins/map.ts`, `src/builtins/set.ts` -- Modify: `src/membrane.ts` -- Create: `test/unit/map.test.ts`, `test/unit/set.test.ts`, `test/integration/collection-identity.test.ts`, `test/regression/collection-escapes.test.ts` - -**Interfaces:** - -- Consumes: `wrap`, `unwrapSameMembrane`. -- Produces: protected collection reads/iteration/callbacks and rejected mutators. - -- [ ] **Step 1: Write failing Map tests** - -Cover size/get/has/keys/values/entries/iterator/forEach, source-side changes, wrapped keys/values, same-membrane view lookup, callback collection, and stable methods. - -- [ ] **Step 2: Write failing Set tests** - -Cover size/has/keys/values/entries/iterator/forEach, source-side changes, wrapped values, same-membrane lookup, callback collection, and stable methods. - -- [ ] **Step 3: Run and confirm native receiver failures** - -Run targeted collection tests; expect internal-slot receiver errors or missing behavior. - -- [ ] **Step 4: Implement lazy adapters** - -Iterators wrap on each `next()`; Map entries return `[wrap(key), wrap(value)]`; callbacks receive wrapped arguments and the view. Translate only recognized same-membrane views for get/has. - -- [ ] **Step 5: Write and satisfy collection escape tests** - -Test extracted set/add/delete/clear functions plus every iterator/callback/descriptor path; source snapshots must remain unchanged after rejection. - -- [ ] **Step 6: Verify and commit** - -```bash -npm run test:unit -- test/unit/map.test.ts test/unit/set.test.ts test/integration/collection-identity.test.ts test/regression/collection-escapes.test.ts -git add src test -git commit -m "feat: add readonly Map and Set adapters" -``` - -### Task 7: Date and unsupported values - -**Files:** - -- Create: `src/builtins/date.ts` -- Modify: `src/classify.ts`, `src/membrane.ts` -- Create: `test/unit/date.test.ts`, `test/unit/unsupported.test.ts`, `test/regression/date-mutators.test.ts`, `test/integration/existing-proxy.test.ts` - -**Interfaces:** - -- Consumes: wrapping/errors. -- Produces: complete Date semantics and conservative known-built-in rejection. - -- [ ] **Step 1: Write failing Date tests** - -Test all common local/UTC reads, conversions, method identity, source-side mutation visibility, and every runtime Date setter including deprecated `setYear` when present. - -- [ ] **Step 2: Implement centralized Date classification** - -Bind only safe reads to the source internal-slot receiver. Every name classified as a Date setter returns a rejecting function. Keep the list exhaustive and test-generated. - -- [ ] **Step 3: Write failing unsupported tests** - -Parameterize RegExp, Error, URL, URLSearchParams, ArrayBuffer, SharedArrayBuffer when available, DataView, every TypedArray, WeakMap, WeakSet, Promise, and identifiable native/host objects. Assert root and lazy nested rejection with safe kind metadata. - -- [ ] **Step 4: Implement side-effect-minimizing classification** - -Use known intrinsic prototypes/constructors with guarded feature detection. Do not read `Symbol.toStringTag` or call `Object.prototype.toString`. - -- [ ] **Step 5: Test source proxies** - -Use forwarding, throwing, and trap-counting proxies. Assert no unnecessary eager traps and faithful propagation under the trust limitation. - -- [ ] **Step 6: Verify and commit** - -```bash -npm run test:unit -- test/unit/date.test.ts test/unit/unsupported.test.ts test/regression/date-mutators.test.ts test/integration/existing-proxy.test.ts -git add src test -git commit -m "feat: support Date and reject unsafe built-ins" -``` - -### Task 8: Adversarial, randomized, and documentation-example tests - -**Files:** - -- Create: `test/regression/adversarial.test.ts`, `test/regression/property-based.test.ts`, `test/integration/docs-examples.test.ts`, `test/types/docs-examples.ts`, `test/fixtures/graphs.ts` - -**Interfaces:** - -- Consumes: complete runtime API. -- Produces: broad source-escape, invariant, random-graph, and example-drift confidence. - -- [ ] **Step 1: Add deterministic escape-route tests** - -Attack nested objects, arrays, Map/Set outputs, descriptors, getters, functions, iterators, callbacks, symbols, spread, and destructuring through every write/reflection route. - -- [ ] **Step 2: Prove the regression test can fail** - -Temporarily disable wrapping for one iterator route, run its targeted test to see the expected failure, restore the implementation, and rerun green. - -- [ ] **Step 3: Add bounded fast-check graphs** - -Generate depth-limited objects/arrays/symbols/Map/Set/Date plus shared references and explicit cycles. Assert termination, stable descriptors, identity, no invariant TypeErrors, and no source change after attempted mutation. - -- [ ] **Step 4: Mirror documentation examples** - -Create runtime and compile-time cases for basic, nested, live, arrays, Maps, Sets, Dates, cycles, identity, and methods. - -- [ ] **Step 5: Verify and commit** - -```bash -npm run test:unit -npm run test:types -npm run coverage -git add test -git commit -m "test: add adversarial randomized and example coverage" -``` - -### Task 9: Build and real tarball verification - -**Files:** - -- Modify: `vite.config.ts`, `tsconfig.build.json`, `package.json` -- Create: `test/package/consumer-esm.mjs`, `test/package/consumer-cjs.cjs`, `test/package/consumer-types.ts`, `test/package/consumer-bundle.mjs`, `test/package/tsconfig.json`, `test/package/verify-package.mjs`, `test/package/verify-package.test.mjs`, `scripts/extract-public-api.mjs`, `scripts/extract-public-api.test.mjs` - -**Interfaces:** - -- Consumes: root source entry. -- Produces: ESM/CJS/declaration output and packed-artifact consumer checks. - -- [ ] **Step 1: Write failing package-contract tests** - -Assert allowed tarball files, exact five exports, working ESM/require/types/bundler, tree shaking, metadata, and rejected private imports such as `@nipe-solutions/readonly-view/src/membrane`. - -- [ ] **Step 2: Run `npm run test:package` and confirm failure** - -Expected: missing or invalid dist/tarball contract. - -- [ ] **Step 3: Finalize dual output** - -Vite emits `dist/index.js` and `dist/index.cjs`; TypeScript emits only `dist/index.d.ts`. No runtime dependency is bundled or declared. - -- [ ] **Step 4: Implement isolated tarball installation** - -Use `npm pack --json`, `mkdtemp`, install the exact tarball with scripts disabled, run ESM/CJS, compile types, bundle with esbuild, reject private paths, then delete only the validated temporary directory. - -- [ ] **Step 5: Verify and commit** - -```bash -npm run build:dist -npm run test:api -npm run test:package -npm pack --dry-run -git add package.json package-lock.json vite.config.ts tsconfig.build.json scripts test/package -git commit -m "build: verify the real dual-format npm package" -``` - -### Task 10: Browser matrix - -**Files:** - -- Create: `playwright.config.ts`, `test/browser/index.html`, `test/browser/browser.spec.ts` -- Modify: `package.json` - -**Interfaces:** - -- Consumes: real built ESM. -- Produces: `npm run test:browser` for Chromium, Firefox, and WebKit. - -- [ ] **Step 1: Write the browser semantic harness** - -Cover objects, invariant descriptors, arrays, Map, Set, Date, symbols, cycles, shared identity, source liveness, and representative rejected mutations. - -- [ ] **Step 2: Run Chromium and confirm wiring failure** - -```bash -npx playwright install chromium -npm run test:browser -- --project=chromium -``` - -- [ ] **Step 3: Configure build/server and three projects** - -Use a deterministic localhost port, Playwright `webServer`, trace on first retry, and no touch project because this library has no input UI. - -- [ ] **Step 4: Verify and commit** - -```bash -npx playwright install chromium firefox webkit -npm run test:browser -git add playwright.config.ts test/browser package.json package-lock.json -git commit -m "test: verify semantics in evergreen browsers" -``` - -### Task 11: Benchmarks and measured size budgets - -**Files:** - -- Create: `benchmarks/benchmark.mjs`, `benchmarks/fixtures.mjs`, `benchmarks/README.md`, `scripts/check-bundle-size.mjs`, `scripts/check-bundle-size.test.mjs`, `scripts/size-budget.json` -- Modify: `package.json` - -**Interfaces:** - -- Consumes: dist/esbuild. -- Produces: `benchmark`, `benchmark:smoke`, `test:size`. - -- [ ] **Step 1: Add correct-result benchmark scenarios** - -Measure plain access, creation, first/repeated reads, large shallow creation, deep reads, arrays, Map, Set, Date, shallow freeze, and recursive deep freeze. Print runtime/OS/sampling method. - -- [ ] **Step 2: Run and document the baseline** - -Run `npm run benchmark`; record factual results and tradeoffs in `benchmarks/README.md`. - -- [ ] **Step 3: Write failing size checker tests** - -Measure minified ESM, gzip, declarations, and packed artifact; verify clear over-budget errors. - -- [ ] **Step 4: Set real budgets after measurement** - -Choose round limits with reasonable headroom above the measured implementation baseline and record baseline plus limits in `scripts/size-budget.json`. - -- [ ] **Step 5: Verify and commit** - -```bash -npm run benchmark:smoke -node --test scripts/check-bundle-size.test.mjs -npm run test:size -git add benchmarks scripts package.json package-lock.json -git commit -m "perf: add transparent benchmarks and size budgets" -``` - -### Task 12: README, architecture, migration, and project docs - -**Files:** - -- Rename/replace: `Readme.md` -> `README.md`, `LICENCE.md` -> `LICENSE`, `CODEOFCONDUCT.md` -> `CODE_OF_CONDUCT.md` -- Replace: `CONTRIBUTING.md` -- Create: `CHANGELOG.md`, `SECURITY.md`, `docs/architecture.md`, `docs/guarantees.md`, `docs/supported-types.md`, `docs/migration-v1-v2.md`, `docs/performance.md`, `docs/security-and-trust.md`, `docs/RELEASING.md`, and ADRs 0001–0005 for the five approved consequential decisions - -**Interfaces:** - -- Consumes: actual semantics, scripts, size, benchmark results. -- Produces: precise user/maintainer docs whose examples are tested. - -- [ ] **Step 1: Rewrite README** - -Lead with the 30-second ownership example, then installation, guarantees, comparisons, supported values, API, performance, documentation, contribution, and license. Remove state-management wording and old badges. - -- [ ] **Step 2: Write semantic/architecture docs** - -Document mappings, shadows, traps, adapters, identity, descriptors, methods/classes/private fields, unsupported values, proxies, memory, and trust. Cite relevant test paths. - -- [ ] **Step 3: Write migration and changelog** - -Show exact v1/v2 code; list removal of `.value`, `internalSet`, deep merge, validation, lifecycle, default export, and internals. Keep Unreleased and undated 2.0.0 preparation. - -- [ ] **Step 4: Modernize community/release docs** - -Use Contributor Covenant 2.1, GitHub private reporting, Node 24 setup, actual scripts, regression workflow, PR expectations, and provenance release procedure. - -- [ ] **Step 5: Verify and commit** - -```bash -npm run test:types -npm run test:unit -- test/integration/docs-examples.test.ts -npm run format:check -git add README.md LICENSE CODE_OF_CONDUCT.md CONTRIBUTING.md CHANGELOG.md SECURITY.md docs -git add -u -git commit -m "docs: document the focused v2 ownership model" -``` - -### Task 13: Collection-ready documentation website - -**Files:** - -- Create: `website/index.html`, `website/main.ts`, `website/styles/tokens.css`, `website/styles/site.css`, `website/content/navigation.ts`, `website/content/pages.ts`, `website/components/header.ts`, `website/components/docs-shell.ts`, `website/components/code-example.ts`, `website/components/support-table.ts`, `website/examples/runtime.ts`, `website/tsconfig.json`, `vite.website.config.ts`, `scripts/verify-website.mjs`, `scripts/verify-website.test.mjs`, `test/browser/website.spec.ts` -- Modify: `package.json` - -**Interfaces:** - -- Consumes: built ReadonlyView and docs. -- Produces: framework-free static site and `dev`, `build:website`, `test:website`. - -- [ ] **Step 1: Define collection-ready visual tokens** - -Create accessible neutral surfaces/ink, one restrained accent, technical typography, responsive spacing/type/radius/shadow tokens, visible focus, and reduced-motion behavior. Reserve a collection identity slot without inventing other products. - -- [ ] **Step 2: Build the reference-inspired shell** - -Implement a strong first screen, evidence blocks, persistent desktop/mobile docs navigation, table of contents, previous/next links, support tables, copyable code, and restrained footer using semantic HTML. - -- [ ] **Step 3: Add real interactive examples** - -Import the built library; demonstrate basic/nested/live/array/Map/Set/Date/cycle/identity/method behavior. Render safe error metadata and never evaluate arbitrary code. - -- [ ] **Step 4: Write website verifier tests first** - -Assert every route/heading/example exists, local links resolve, no React/framework imports exist, and no private package import is used. Run and confirm failure before implementing the verifier. - -- [ ] **Step 5: Build and browser-test** - -Test desktop/mobile navigation, copy controls, live-view output, mutation errors, focus, reduced motion, and narrow overflow in the three-browser setup. - -- [ ] **Step 6: Verify and commit** - -```bash -npm run test:website -npm run build:website -npm run test:browser -- --grep "documentation" -git add website vite.website.config.ts scripts test/browser/website.spec.ts package.json package-lock.json -git commit -m "docs: add the collection-ready ReadonlyView website" -``` - -### Task 14: GitHub hygiene, CI, and guarded release - -**Files:** - -- Replace: `.github/workflows/ci.yml`, `.github/workflows/publish.yml` with `.github/workflows/release.yml` -- Create: `.github/ISSUE_TEMPLATE/bug.yml`, `.github/ISSUE_TEMPLATE/feature.yml`, `.github/ISSUE_TEMPLATE/config.yml`, `.github/PULL_REQUEST_TEMPLATE.md`, `renovate.json`, `scripts/verify-release-readiness.mjs`, `scripts/verify-release-readiness.test.mjs` -- Modify: `package.json` - -**Interfaces:** - -- Consumes: all quality scripts. -- Produces: `check`, `release:check`, CI/browser jobs, and manual provenance publishing. - -- [ ] **Step 1: Write failing release-policy tests** - -Fixture-test exact version/channel confirmation, next/latest rules, changelog/package/license/README, zero dependencies, exports, and tarball allowlist. - -- [ ] **Step 2: Implement release readiness and `npm run check`** - -Order: format, lint, types, unit/type tests, dist, API, size, package, website verification/build, benchmark smoke. The verifier never publishes. - -- [ ] **Step 3: Create least-privilege CI** - -Node 24 quality job followed by Chromium/Firefox/WebKit jobs; use npm caching, concurrency cancellation, and failure-only artifacts. - -- [ ] **Step 4: Create manual guarded release** - -Require `publish with `, protected environment, id-token only in publish, full quality/browser checks, unpublished-version test, provenance publish, registry propagation, and GitHub release. Do not add automatic publish triggers. - -- [ ] **Step 5: Add low-noise Renovate and templates** - -Group compatible dev tooling, limit concurrent PRs, schedule reasonably, and collect minimal reproduction/environment data in issues. - -- [ ] **Step 6: Verify and commit** - -```bash -node --test scripts/verify-release-readiness.test.mjs -npm run check -git add .github renovate.json scripts package.json package-lock.json -git add -u -git commit -m "ci: add v2 quality and guarded release workflows" -``` - -### Task 15: Final adversarial audit and readiness evidence - -**Files:** - -- Create: `docs/releases/v2-alpha-readiness.md` -- Modify only after failing regression tests: relevant `src/*`, `test/regression/*`, docs, and size baseline - -**Interfaces:** - -- Consumes: complete repository. -- Produces: traceable Definition-of-Done evidence without publishing. - -- [ ] **Step 1: Audit every source-exposure path** - -Review reads, descriptors, accessors, methods, iterators, callbacks, symbols, spread/destructuring, collection translation, Date binding, classes/private fields, constructors, unsupported classification, and proxies. Every confirmed defect receives a failing regression test before its fix. - -- [ ] **Step 2: Audit every Proxy trap against ECMAScript invariants** - -Check get, set, deleteProperty, defineProperty, getOwnPropertyDescriptor, ownKeys, has, getPrototypeOf, setPrototypeOf, isExtensible, preventExtensions, apply, and construct across all shadow/source states. - -- [ ] **Step 3: Audit runtime/types/docs alignment** - -Map each support-table row and `DeepReadonly` branch to runtime classification and tests; remove or correct any unverified statement. - -- [ ] **Step 4: Run fresh full evidence commands** - -```bash -npm ci -npm run check -npm run test:browser -npm run benchmark -npm pack --dry-run -``` - -Record exact test counts, browser projects, sizes, benchmark environment/summary, tarball list, dependency count, and limitations in the readiness document. - -- [ ] **Step 5: Verify publication safety and commit** - -```bash -git diff --check -git status --short -npm view @nipe-solutions/readonly-view@2.0.0-alpha.0 version || true -git add docs/releases/v2-alpha-readiness.md README.md docs scripts/size-budget.json src test -git commit -m "chore: complete the ReadonlyView v2 alpha audit" -``` - -Expected: registry lookup finds no v2 prerelease; nothing is published, tagged, pushed, or released. - -- [ ] **Step 6: Re-run final verification** - -```bash -npm run check -npm run test:browser -git status --short --branch -``` - -Expected: both gates pass and the working tree is clean. diff --git a/docs/superpowers/plans/2026-09-03-legal-deployment-release.md b/docs/superpowers/plans/2026-09-03-legal-deployment-release.md deleted file mode 100644 index de06b74..0000000 --- a/docs/superpowers/plans/2026-09-03-legal-deployment-release.md +++ /dev/null @@ -1,409 +0,0 @@ -# ReadonlyView Legal, Deployment, and Stable Release 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:** Add bilingual legal pages, production Vercel deployment, reference-grade GitHub automation, and publish `@nipe-solutions/readonly-view@2.0.0` as a verified stable release. - -**Architecture:** Keep the Vite site framework-free and add four static legal entry points sharing the existing design system. Use Vercel Git integration for preview/production deployments and the guarded GitHub release workflow for initial token-authenticated publication with OIDC provenance after all local and hosted checks pass; switch future releases to npm trusted publishing after the package exists. - -**Tech Stack:** HTML, CSS, TypeScript, Vite 8, Playwright, GitHub Actions, Vercel, GoDaddy DNS, npm trusted publishing. - -**Spec:** `docs/superpowers/specs/2026-09-03-legal-deployment-release-design.md` - -## Global Constraints - -- Publish exactly `@nipe-solutions/readonly-view@2.0.0` publicly under `latest`. -- Use `https://readonly-view.nipesolutions.com` as the production documentation origin. -- Preserve zero runtime dependencies and the framework-free Vite website. -- Reuse the approved NIPE Solutions e.U. legal operator data and bilingual reference text. -- Do not publish until the release commit is merged to `main`, CI is green, and the exact tarball is verified. -- Do not deprecate or otherwise mutate the old `immuview` npm package. - ---- - -### Task 1: Bilingual legal routes and production website metadata - -**Files:** - -- Create: `website/privacy/index.html` -- Create: `website/impressum/index.html` -- Create: `website/de/datenschutz/index.html` -- Create: `website/de/impressum/index.html` -- Create: `website/legal.css` -- Create: `website/public/robots.txt` -- Create: `website/public/sitemap.xml` -- Create: `website/public/favicon.svg` -- Create: `website/public/favicon-32x32.png` -- Create: `website/public/apple-touch-icon.png` -- Create: `website/public/site.webmanifest` -- Modify: `website/index.html` -- Modify: `website/styles.css` -- Modify: `vite.website.config.ts` -- Modify: `scripts/verify-website.mjs` -- Modify: `test/browser/website.spec.ts` -- Modify: `test/integration/docs-examples.test.ts` - -**Interfaces:** - -- Consumes: existing `.masthead`, `.brand`, footer, color tokens, and `npm run build:website`. -- Produces: four stable legal routes, language cross-links, canonical production URLs, and multi-page Vite output under `website/dist`. -- Produces: a crisp ReadonlyView tab-icon family derived from the existing `R` brand mark. - -- [ ] **Step 1: Add failing build-verification assertions** - -Extend `scripts/verify-website.mjs` to read all five built HTML files and assert their defining content: - -```js -const pages = new Map([ - [ - 'privacy/index.html', - ['Privacy Policy', 'Vercel Inc.', 'no non-essential cookies'], - ], - [ - 'impressum/index.html', - ['Impressum', 'NIPE Solutions e.U.', 'ATU78464412'], - ], - [ - 'de/datenschutz/index.html', - ['Datenschutzerklärung', 'Vercel Inc.', 'Datenschutzbehörde'], - ], - [ - 'de/impressum/index.html', - ['Impressum', 'NIPE Solutions e.U.', 'FN 585066t'], - ], -]); -``` - -Also assert that the main page links each route and no page contains `v2 alpha`. - -Assert that the home page contains examples for nested protection, owner-side live updates, arrays, Map, Set, Date, cycles, shared identity, and mutation rejection. - -Assert that all pages link `/favicon.svg`, `/favicon-32x32.png`, `/apple-touch-icon.png`, and `/site.webmanifest`, and that every referenced asset exists in `website/dist`. - -- [ ] **Step 2: Run the website check and confirm it fails** - -Run: `npm run test:website` - -Expected: failure because the legal build outputs do not exist. - -- [ ] **Step 3: Add the legal HTML and shared legal layout** - -Create semantic pages with one `h1`, a last-updated date, language switch, linked email/phone, company details, and the approved English/German privacy sections. Use a shared `.legal-shell`, `.legal-content`, and `.legal-meta` layout in `website/legal.css`; retain `:focus-visible` and a one-column mobile breakpoint. - -- [ ] **Step 4: Configure the Vite multi-page build** - -Change `vite.website.config.ts` to resolve these inputs: - -```ts -rollupOptions: { - input: { - index: resolve(import.meta.dirname, 'website/index.html'), - privacy: resolve(import.meta.dirname, 'website/privacy/index.html'), - impressum: resolve(import.meta.dirname, 'website/impressum/index.html'), - datenschutz: resolve(import.meta.dirname, 'website/de/datenschutz/index.html'), - 'de-impressum': resolve(import.meta.dirname, 'website/de/impressum/index.html'), - }, -}, -``` - -Use root-absolute asset and navigation URLs because production routes are nested. - -- [ ] **Step 5: Update the home page, examples, and crawl metadata** - -Create an original SVG favicon with a dark rounded-square core, white `R`, and blue membrane accent. Render deterministic 32×32 and 180×180 PNG variants from that SVG, add a minimal web manifest, and add favicon, Apple-touch, manifest, and theme-color tags to every page. - -Remove the hero release-status pill entirely. Move `v2` and `zero runtime dependencies` into the proof/metadata area, add canonical/OG production URLs, and add footer links to all legal pages. Add copyable examples for nested objects, owner-side live updates, arrays, Map, Set, Date, cycles, shared identity, and mutation rejection. Add `robots.txt` and a sitemap containing the five canonical URLs. - -Extend `test/integration/docs-examples.test.ts` with executable equivalents of the new examples, including: - -```ts -const shared = { id: 1 }; -const source = { - items: [shared], - map: new Map([['selected', shared]]), - set: new Set([shared]), -}; -const view = readonlyView(source); -expect(view.items[0]).toBe(view.map.get('selected')); -expect([...view.set][0]).toBe(view.items[0]); -expect(() => Reflect.set(view.items[0], 'id', 2)).toThrow(DirectMutationError); -``` - -- [ ] **Step 6: Add browser route coverage** - -Add a Playwright test that opens each route and checks its heading, language link, footer, and absence of horizontal overflow: - -```ts -for (const [route, heading] of legalRoutes) { - await page.goto(route); - await expect( - page.getByRole('heading', { level: 1, name: heading }), - ).toBeVisible(); - expect( - await page.evaluate(() => document.body.scrollWidth), - ).toBeLessThanOrEqual( - await page.evaluate(() => document.documentElement.clientWidth), - ); -} -``` - -- [ ] **Step 7: Verify and commit** - -Run: `npm run test:website && npm run test:browser -- --project=chromium` - -Expected: all website and Chromium tests pass. - -Commit: `feat(website): add bilingual legal pages` - -### Task 2: Vercel configuration and public metadata - -**Files:** - -- Create: `vercel.json` -- Modify: `package.json` -- Modify: `package-lock.json` -- Modify: `README.md` -- Modify: `SECURITY.md` -- Modify: `docs/compatibility.md` -- Modify: `scripts/verify-release.mjs` - -**Interfaces:** - -- Consumes: `npm run build:website` and `website/dist` from Task 1. -- Produces: repeatable Vercel builds and a single canonical documentation origin across metadata. - -- [ ] **Step 1: Add failing metadata verification** - -Extend `scripts/verify-release.mjs` with exact assertions: - -```js -assert( - pkg.homepage === 'https://readonly-view.nipesolutions.com', - 'Homepage is stale', -); -const vercel = JSON.parse(await readFile(resolve(root, 'vercel.json'), 'utf8')); -assert( - vercel.buildCommand === 'npm run build:website', - 'Unexpected Vercel build', -); -assert(vercel.outputDirectory === 'website/dist', 'Unexpected Vercel output'); -``` - -- [ ] **Step 2: Confirm the release verifier fails** - -Run: `node scripts/verify-release.mjs` - -Expected: failure because package homepage and `vercel.json` are not production-ready. - -- [ ] **Step 3: Add Vercel configuration** - -Create: - -```json -{ - "$schema": "https://openapi.vercel.sh/vercel.json", - "buildCommand": "npm run build:website", - "framework": null, - "installCommand": "npm ci", - "outputDirectory": "website/dist" -} -``` - -- [ ] **Step 4: Update public URLs and package version** - -Run `npm version 2.0.0 --no-git-tag-version`, set `homepage` to the production domain, and update README, security, and compatibility links. Confirm both `package-lock.json` version fields equal `2.0.0`. - -- [ ] **Step 5: Verify and commit** - -Run: `npm run build:website && node scripts/verify-release.mjs` - -Expected: both commands pass for `@nipe-solutions/readonly-view@2.0.0`. - -Commit: `chore(release): prepare v2 production metadata` - -### Task 3: Reference-grade CI and guarded release workflow - -**Files:** - -- Modify: `.github/workflows/ci.yml` -- Modify: `.github/workflows/release.yml` -- Modify: `docs/RELEASING.md` -- Modify: `CONTRIBUTING.md` - -**Interfaces:** - -- Consumes: `npm run release:check`, Playwright projects, packed artifact output, npm channel verifier. -- Produces: dependency-ordered CI, failure diagnostics, and a stable-only guarded release path using GitHub environment `npm`. - -- [ ] **Step 1: Validate workflow syntax before edits** - -Run: `npx prettier --check .github/workflows/ci.yml .github/workflows/release.yml` - -Expected: pass, establishing the existing baseline. - -- [ ] **Step 2: Align the CI workflow** - -Use `actions/checkout@v7`, retain `actions/setup-node@v7` and `actions/upload-artifact@v7`, change primary quality to `npm run release:check`, make Node 22 and browser jobs depend on `quality`, and upload `playwright-report/` plus `test-results/` only on browser failure. Keep Chromium, Firefox, WebKit, packed artifact, Node 22, concurrency, and read-only permissions. - -- [ ] **Step 3: Tighten stable release validation** - -Use `actions/checkout@v7`, keep exact confirmation `publish 2.0.0 with latest`, require `main`, use environment `npm`, grant `id-token: write` only to publish, verify `2.0.0` is absent before publish, carry exactly one verified tarball into the publish job, and verify registry propagation before a SHA-targeted GitHub release. Use a short-lived granular `NPM_TOKEN` environment secret only for the initial package bootstrap, with `--provenance`; remove it after trusted publishing can be configured. Preserve generic version inputs for future releases but enforce stable/latest through `verify-release-channel.mjs`. - -- [ ] **Step 4: Document hosted deployment and publication** - -Update `docs/RELEASING.md` and `CONTRIBUTING.md` with Vercel Git behavior, custom domain, npm environment/trusted-publisher tuple, exact dispatch inputs, post-publish checks, and rollback/non-republish guidance. - -- [ ] **Step 5: Verify and commit** - -Run: `npm run format:check && npm run lint && node scripts/verify-release-channel.mjs latest` - -Expected: all commands pass. - -Commit: `ci: align deployment and stable release gates` - -### Task 4: Finalize changelog and verify the release candidate - -**Files:** - -- Modify: `CHANGELOG.md` -- Modify: `docs/release-readiness.md` - -**Interfaces:** - -- Consumes: completed Tasks 1–3 and version `2.0.0`. -- Produces: an auditable stable changelog and exact local release evidence. - -- [ ] **Step 1: Promote release notes** - -Leave an empty `## Unreleased` section and move the complete v2 notes beneath `## 2.0.0 - 2026-09-03`, retaining Breaking Changes, Added, Fixed, Performance, and Documentation headings. - -- [ ] **Step 2: Run the practical local gate** - -Run: `npm ci && npm run release:check` - -Expected: formatting, lint, TypeScript, 65+ semantic tests, type tests, size, packed consumers, website verification, benchmark smoke, release metadata, and tarball creation all pass. - -- [ ] **Step 3: Run the browser matrix** - -Run: `npm run test:browser` - -Expected: Chromium, Firefox, and WebKit pass, except the repository's documented macOS 14 WebKit exclusion when applicable. - -- [ ] **Step 4: Inspect the exact package** - -Run: `npm pack --dry-run --json` and `tar -tzf .artifacts/nipe-solutions-readonly-view-2.0.0.tgz` - -Expected: only package metadata, README, LICENSE, changelog, and intended `dist` files; no tests, website source, benchmarks, source maps, or private modules. - -- [ ] **Step 5: Record measurements and commit** - -Update `docs/release-readiness.md` with exact test totals, tarball sizes, minified/gzip sizes, browser results, and the pending hosted steps. - -Commit: `docs: finalize readonly-view 2.0.0 release notes` - -### Task 5: Review, merge, and Vercel production deployment - -**Files:** - -- External: GitHub pull request and CI -- External: Vercel project for `NIPE-Solutions/readonly-view` -- External: GoDaddy DNS for `nipesolutions.com` - -**Interfaces:** - -- Consumes: reviewed release branch and `vercel.json`. -- Produces: green `main`, Vercel preview/production deployments, DNS verification, and valid HTTPS at the production origin. - -- [ ] **Step 1: Push and open a focused pull request** - -Push the release-preparation branch, open a PR against `main`, and include legal routes, workflow deltas, local gate evidence, package version, and explicit statement that npm is not yet published. - -- [ ] **Step 2: Review CI and preview deployment** - -Wait for quality, Node 22, Chromium, Firefox, WebKit, and Vercel preview checks. Open the preview and verify the home page plus all legal routes on desktop and mobile. - -- [ ] **Step 3: Merge and verify production CI** - -Merge only after all required checks pass, then wait for the `main` CI run to finish successfully. - -- [ ] **Step 4: Link the Vercel project** - -In the authenticated Vercel account, import `NIPE-Solutions/readonly-view`, select the appropriate NIPE Solutions team, confirm production branch `main`, and verify that Vercel reads the committed build and output settings. - -- [ ] **Step 5: Configure the custom domain** - -Add `readonly-view.nipesolutions.com` to the Vercel project, read the exact required DNS record, create that record in the authenticated GoDaddy account, and wait for Vercel to report a valid configuration and issued TLS certificate. - -- [ ] **Step 6: Verify production** - -Check status 200, canonical URL, title, home interaction, all four legal routes, sitemap, robots, and HTTPS using both browser inspection and HTTP requests. - -### Task 6: npm trusted publishing and irreversible stable release - -**Files:** - -- External: GitHub environment `npm` -- External: npm organization/package trusted-publisher settings -- External: GitHub Actions Release run -- External: npm package and GitHub release - -**Interfaces:** - -- Consumes: green merge commit on `main`, exact version `2.0.0`, npm organization access, production documentation. -- Produces: public `@nipe-solutions/readonly-view@2.0.0`, `latest` dist-tag, provenance, and GitHub `v2.0.0` release. - -- [ ] **Step 1: Configure GitHub release protection** - -Create the repository environment `npm`, restrict it to `main`, and add the authenticated owner as a required reviewer when the repository plan supports reviewers. Do not add a long-lived npm token. - -- [ ] **Step 2: Configure first-publication authentication** - -Create a short-lived granular npm token with only the required `@nipe-solutions` package-write access and store it as the `NPM_TOKEN` secret on the protected `npm` environment. The initial workflow uses that token only in the publish job and uses OIDC for `--provenance`; it is not OIDC-only. - -- [ ] **Step 3: Reconfirm registry absence and commit identity** - -Run: `npm view @nipe-solutions/readonly-view@2.0.0 version` - -Expected before publication: npm `E404`. Confirm the release workflow SHA equals the reviewed green `main` SHA. - -- [ ] **Step 4: Dispatch the release** - -Run: - -```bash -gh workflow run release.yml --ref main \ - -f version=2.0.0 \ - -f channel=latest \ - -f 'confirmation=publish 2.0.0 with latest' -``` - -Approve the protected `npm` environment deployment when prompted and monitor every job through registry propagation and GitHub release creation. - -- [ ] **Step 5: Verify the public package** - -Run: - -```bash -npm view @nipe-solutions/readonly-view@2.0.0 name version dist-tags.latest repository homepage -test -n "$(npm view @nipe-solutions/readonly-view@2.0.0 dist.attestations.url)" -consumer_dir="$(mktemp -d)" -( - cd "$consumer_dir" - npm init --yes >/dev/null - npm install --ignore-scripts --save-exact @nipe-solutions/readonly-view@2.0.0 - node --input-type=module -e \ - "import('@nipe-solutions/readonly-view').then(m => console.log(typeof m.readonlyView))" -) -gh release view v2.0.0 --repo NIPE-Solutions/readonly-view \ - --json tagName,targetCommitish,url -``` - -Expected: exact name/version, `latest` equals `2.0.0`, production homepage, the temporary consumer import prints `function`, provenance is visible in npm, and the GitHub release targets the dispatched SHA. - -- [ ] **Step 6: Switch future releases to trusted publishing** - -In npm package settings, configure repository `NIPE-Solutions/readonly-view`, workflow `release.yml`, environment `npm`. Delete the `NPM_TOKEN` GitHub environment secret, revoke the granular token, and remove `NODE_AUTH_TOKEN` from the publish step when preparing the next release. - -- [ ] **Step 7: Record final release evidence** - -Update the release-readiness record or GitHub release notes with the production site, CI run, npm version, provenance, tarball integrity, and known limitations. Do not modify the old `immuview` package. diff --git a/docs/superpowers/plans/2026-09-04-product-story-and-launch-content.md b/docs/superpowers/plans/2026-09-04-product-story-and-launch-content.md deleted file mode 100644 index 7897b0a..0000000 --- a/docs/superpowers/plans/2026-09-04-product-story-and-launch-content.md +++ /dev/null @@ -1,269 +0,0 @@ -# ReadonlyView Product Story and Launch Content 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:** Reframe ReadonlyView around the owner/consumer boundary, add concrete use-case and decision documentation, and prepare honest channel-native launch content without changing runtime behavior. - -**Architecture:** Preserve the existing single-page, framework-free Vite site and repository documentation structure. Add product-story sections before the technical reference, keep all code examples synchronized through executable integration tests, and store unpublished campaign drafts in a root `marketing/` directory excluded by the package allowlist. - -**Tech Stack:** Markdown, semantic HTML, CSS, TypeScript, Vite 8, Vitest, Playwright, Node.js 22/24 - -**Spec:** `docs/superpowers/specs/2026-09-04-product-story-and-launch-content.md` - -## Global Constraints - -- Do not modify `src/`, public exports, runtime semantics, or support policy. -- Lead with “Expose live internal data without exposing mutation.” -- Use “owner” and “consumer” consistently; never describe the source as immutable. -- Preserve the current website identity, interactive demo, legal pages, and framework-free implementation. -- Do not add dependencies, tracking, analytics, decorative animation, fabricated social proof, or security claims. -- Keep limitations prominent and comparisons fair to the problem each alternative solves. -- Every published code example must execute in tests. -- Nothing in `marketing/` is posted or scheduled by this work. - ---- - -### Task 1: README product story and executable boundary examples - -**Files:** - -- Modify: `README.md` -- Modify: `test/integration/docs-examples.test.ts` -- Modify: `test/types/docs-examples.ts` - -**Interfaces:** - -- Consumes: `readonlyView`, `DirectMutationError`, and existing integration/type-test conventions. -- Produces: canonical SDK, registry, and plugin examples reused conceptually by the website and focused docs. - -- [ ] **Step 1: Add failing runtime examples** - -Add three tests to `test/integration/docs-examples.test.ts`. The SDK test must construct a class with private `#state`, expose `readonly state = readonlyView(this.#state)`, mutate through `connect`, observe the update through the existing view, reject `Reflect.set(view.user, 'name', ...)`, and verify the source-owned user is unchanged. The registry test must expose a readonly `Map`, allow owner-side `register`, reject `view.set`, and compare entries before/after rejection. The plugin test must pass a readonly context into `initialize`, reject nested configuration mutation, and preserve the source value. - -- [ ] **Step 2: Run the focused test and confirm the new cases fail before fixtures exist** - -Run: `npx vitest run --project unit test/integration/docs-examples.test.ts` - -Expected: failure caused by the newly referenced SDK, registry, or plugin fixtures not yet being defined. - -- [ ] **Step 3: Implement the fixtures and assertions** - -Use complete local fixtures in the test file, including this SDK shape: - -```ts -class Client { - #state = { connected: false, user: null as { name: string } | null }; - readonly state = readonlyView(this.#state); - - connect(user: { name: string }) { - this.#state.connected = true; - this.#state.user = user; - } -} -``` - -Assert owner liveness, rejected consumer mutation, and source snapshots explicitly. - -- [ ] **Step 4: Add compile-time examples** - -In `test/types/docs-examples.ts`, add the SDK public state shape and `@ts-expect-error` assignments for `client.state.connected`, registry `.set`, and nested plugin configuration. Keep valid reads and owner methods free of expected errors. - -- [ ] **Step 5: Rewrite the README** - -Use this section order: opening value proposition and minimal example; Install; Why this exists (SDK); Use ReadonlyView when; comparison table; ownership mental model; guarantees; supported values; When not to use it; performance; API; documentation; license. Include npm/docs/GitHub links, compact npm/pnpm/Yarn/Bun install commands, and no more than three badges after the opening example if they do not displace it. - -The comparison columns must be TypeScript `readonly`, `Object.freeze`, deep freeze, snapshot/Immer, and ReadonlyView. Rows must cover compile-time protection, runtime depth, whether the owner retains mutation, liveness, traversal/copying, and new-state production. - -- [ ] **Step 6: Verify and commit** - -Run: `npx vitest run --project unit test/integration/docs-examples.test.ts && npm run test:types && npx prettier --check README.md test/integration/docs-examples.test.ts test/types/docs-examples.ts` - -Expected: focused runtime and type tests pass; formatting passes. - -Commit: `docs: explain readonly-view through library boundaries` - -### Task 2: Focused mental-model, use-case, choice, and API documentation - -**Files:** - -- Create: `docs/mental-model.md` -- Create: `docs/use-cases.md` -- Create: `docs/choosing-an-approach.md` -- Modify: `docs/api.md` -- Modify: `README.md` - -**Interfaces:** - -- Consumes: canonical examples and vocabulary established in Task 1. -- Produces: authoritative destinations for ownership, fit, comparisons, and every public export. - -- [ ] **Step 1: Create the mental-model page** - -Explain owner, source graph, `readonlyView`, membrane, consumer, lazy wrapping, liveness, stable same-membrane identity, independent top-level membranes, and access-path—not global—protection. Include the exact diagram from the spec and links to guarantees, supported types, and security/trust. - -- [ ] **Step 2: Create the use-case page** - -Include complete SDK, registry, and plugin-context examples aligned with Task 1. For each, label owner, consumer, why a clone becomes stale, allowed owner mutation, and rejected consumer mutation. Add shorter sections for response caches, configuration managers, engine state, and diagnostic state, followed by “When not to use ReadonlyView.” - -- [ ] **Step 3: Create the decision guide** - -Give TypeScript `readonly`, `Object.freeze`, deep freeze, cloning/snapshots, Immer-style updates, and ReadonlyView an individual “choose this when” section. Add the same precise comparison matrix used by the README and conclude that the approaches solve different ownership/lifecycle problems. - -- [ ] **Step 4: Expand the API reference** - -For `readonlyView`, `isReadonlyView`, `DirectMutationError`, `UnsupportedTypeError`, and `DeepReadonly`, add signature, purpose, minimal example, guarantees, and edge cases. Explicitly cover primitives, existing views, independent membranes, error metadata, lazy unsupported nested values, and generic/overloaded callable type limitations. Link detailed semantics rather than duplicating them. - -- [ ] **Step 5: Wire the documentation index** - -Add Mental model, Use cases, and Choosing an approach to the README documentation links. Check every relative link with: - -```bash -node -e "const fs=require('node:fs'); for(const f of ['README.md','docs/mental-model.md','docs/use-cases.md','docs/choosing-an-approach.md','docs/api.md']) { if(!fs.existsSync(f)) throw new Error(f) }" -``` - -- [ ] **Step 6: Verify and commit** - -Run: `npx prettier --check README.md docs/mental-model.md docs/use-cases.md docs/choosing-an-approach.md docs/api.md && npm run test:unit && npm run test:types` - -Expected: formatting, 77+ runtime tests, and strict type examples pass. - -Commit: `docs: add readonly-view adoption guides` - -### Task 3: Website story, use cases, fit, and comparison - -**Files:** - -- Modify: `website/index.html` -- Modify: `website/styles.css` -- Modify: `scripts/verify-website.mjs` -- Modify: `test/browser/website.spec.ts` - -**Interfaces:** - -- Consumes: README positioning and canonical examples from Tasks 1–2. -- Produces: a conversion-oriented home-page journey before the existing technical reference. - -- [ ] **Step 1: Add failing build assertions** - -In `scripts/verify-website.mjs`, require the built home page to contain IDs `why`, `use-cases`, `comparison`, and `fit`; the primary sentence; SDK, registry, and plugin headings; “Good fit” and “Not a fit”; links to npm, GitHub, and the three new GitHub-hosted docs; and canonical production metadata. - -- [ ] **Step 2: Add failing browser expectations** - -In `test/browser/website.spec.ts`, verify sidebar links scroll to the four new IDs, the three CTA destinations have correct `href` values, each new section has one visible level-two heading, and the existing narrow-viewport overflow assertion still applies after navigating to `#use-cases`. - -- [ ] **Step 3: Run red tests** - -Run: `npm run test:website && npm run test:browser -- --project=chromium` - -Expected: failures for missing new sections and navigation links. - -- [ ] **Step 4: Implement semantic HTML** - -Retain the hero demo and proof strip. Insert, before the existing Introduction section: “Why this exists” with the SDK boundary; “Built for public read surfaces” with SDK, registry, and plugin scenarios; “Choose the right tool” with a compact accessible table; and a two-column “Good fit / Not a fit” section followed by npm/docs/GitHub CTAs. Add matching sidebar and on-page links. Update description/OG copy to “Expose live internal data without exposing mutation.” - -- [ ] **Step 5: Extend the existing design system** - -Add focused CSS for `.story`, `.use-case-list`, `.fit-grid`, and `.story-actions`. Use current paper/ink/blue tokens, rules, code surfaces, typography, and breakpoint conventions. Avoid identical rounded cards, new shadows, all-caps eyebrows, and non-interactive motion. Ensure readable lines below 80 characters and one-column mobile layouts. - -- [ ] **Step 6: Run green website verification** - -Run: `npm run test:website && npm run test:browser -- --project=chromium` - -Expected: website build assertions and Chromium browser tests pass. - -- [ ] **Step 7: Commit** - -Run: `git diff --check && npx prettier --check website/index.html website/styles.css scripts/verify-website.mjs test/browser/website.spec.ts` - -Commit: `docs(website): lead with readonly-view use cases` - -### Task 4: Launch-ready marketing library - -**Files:** - -- Create: `marketing/README.md` -- Create: `marketing/launch-announcement.md` -- Create: `marketing/technical-article.md` -- Create: `marketing/comparison-article.md` -- Create: `marketing/sdk-tutorial.md` -- Create: `marketing/mutation-escape-thread.md` - -**Interfaces:** - -- Consumes: canonical positioning, examples, guarantees, and limitations from Tasks 1–3. -- Produces: drafts for manual human review and publication; no automated external actions. - -- [ ] **Step 1: Create the campaign index** - -Document audience, goal, recommended publication sequence, canonical URLs, voice, prohibited claims, and a checklist requiring final channel-rule review, live-link checks, code formatting, disclosure, proofreading, and date/version confirmation. - -- [ ] **Step 2: Draft channel-native launch announcements** - -Write distinct GitHub/NIPE, personal LinkedIn, NIPE LinkedIn, X/Bluesky thread, and adaptable community versions. Each must state the boundary problem, show or link one concise example, identify `@nipe-solutions/readonly-view`, link the production site and repository, and acknowledge that it is not a sandbox or state manager. - -- [ ] **Step 3: Draft the technical article** - -Write “Why a readonly Proxy is harder than it looks” with concrete sections on property descriptors, getters/method returns, callback collection arguments, iterators, internal slots, and future native APIs. Explain shadow targets and fail-closed inventories in approachable language and end with appropriate/non-appropriate use cases. - -- [ ] **Step 4: Draft the comparison article and SDK tutorial** - -Keep the comparison fair and decision-oriented. In the tutorial, evolve a mutable leaked SDK reference into the tested `Client` pattern, add consumer and owner tests, then cover unsupported values, Proxy overhead, and trust boundaries. - -- [ ] **Step 5: Draft the seven-part escape-route thread** - -Use one post each for the naïve proxy premise, descriptors, returns/getters, callbacks, iterators, internal-slot built-ins, and future methods/fail-closed behavior. Finish with the repository and documentation links without alarmist security language. - -- [ ] **Step 6: Verify package exclusion and commit** - -Run: `npx prettier --check marketing && npm pack --dry-run --json | node -e "let s=''; process.stdin.on('data', d => s += d).on('end', () => { const p=JSON.parse(s)[0]; if(p.files.some(f => f.path.startsWith('marketing/'))) throw new Error('marketing shipped') })"` - -Expected: all drafts format cleanly and no `marketing/` path appears in the tarball. - -Commit: `docs(marketing): prepare readonly-view launch campaign` - -### Task 5: Discoverability, complete verification, and handoff - -**Files:** - -- Modify: `package.json` -- Modify: `package-lock.json` -- Modify: `docs/release-readiness.md` - -**Interfaces:** - -- Consumes: all prior content and tests. -- Produces: focused npm keywords and final evidence for a communication-only pull request. - -- [ ] **Step 1: Update package keywords** - -Set both manifests to the spec’s ordered keyword list: `readonly`, `deep-readonly`, `immutable`, `immutability`, `proxy`, `membrane`, `typescript`, `javascript`, `live-view`, `runtime`, `object`, `map`, `set`. Do not change the published version. - -- [ ] **Step 2: Update release-readiness documentation** - -Add a short post-release communication note stating that README/site/use-case/API content was expanded without runtime or export changes. Do not overwrite the existing `STABLE READY` evidence or imply a new npm release was published. - -- [ ] **Step 3: Run the complete local gate** - -Run: `npm run release:check` - -Expected: formatting, lint, typecheck, 77+ runtime tests, strict types, size limits, package consumers, website verification, benchmark smoke, release metadata, and package artifact all pass. - -- [ ] **Step 4: Run the local browser suite** - -Run: `npm run test:browser` - -Expected: Chromium and Firefox pass; WebKit follows the existing macOS 14 exclusion and remains required in Linux CI. - -- [ ] **Step 5: Inspect scope and package** - -Run: `git diff origin/main...HEAD --stat && git diff origin/main...HEAD -- src/ && npm pack --dry-run --json` - -Expected: no `src/` diff, no new exports or dependencies, and the same seven intended package files apart from updated package metadata/README content. - -- [ ] **Step 6: Commit final metadata** - -Commit: `docs: finalize readonly-view product story` - -- [ ] **Step 7: Request review and open a pull request** - -Use `superpowers:requesting-code-review`, address verified findings, push `docs/product-story`, and open a PR against `main`. State explicitly that the PR changes communication and development-only drafts, not runtime semantics, and that nothing was posted externally. diff --git a/docs/superpowers/specs/2026-09-03-immuview-v2-design.md b/docs/superpowers/specs/2026-09-03-immuview-v2-design.md deleted file mode 100644 index 6098a7c..0000000 --- a/docs/superpowers/specs/2026-09-03-immuview-v2-design.md +++ /dev/null @@ -1,352 +0,0 @@ -# ReadonlyView v2 Design Specification - -## Status - -Approved in conversation on 2026-09-03. This document defines the semantic and engineering contract for the v2 rewrite. It does not authorize publishing a package. - -## Product definition - -ReadonlyView creates a deeply readonly, lazy, live view over mutable JavaScript data. It is a runtime-enforced readonly membrane with matching TypeScript readonly types. - -ReadonlyView is not a state manager, store, reactive primitive, mutation API, validator, merge utility, snapshot, clone, or security sandbox. The source owner mutates the source directly. Consumers receive the view. - -```ts -import { readonlyView } from '@nipe-solutions/readonly-view'; - -const source = { user: { name: 'Alice', roles: ['admin'] } }; -const view = readonlyView(source); - -source.user.name = 'Bob'; -console.log(view.user.name); // Bob - -view.user.roles.push('editor'); // TypeScript error and DirectMutationError -``` - -## Public API - -The package root exports only: - -```ts -readonlyView; -isReadonlyView; -DirectMutationError; -UnsupportedTypeError; -DeepReadonly; -``` - -There is no default export, configuration object, mutation API, `unwrap`, registry, plugin system, or public internal module. Package exports reject private subpath imports. - -```ts -function readonlyView(source: T): DeepReadonly; -function isReadonlyView(value: unknown): boolean; -``` - -Primitives are returned unchanged. Passing a ReadonlyView view to `readonlyView` returns the same view. - -## Core guarantees - -- Writes made through a view are rejected with `DirectMutationError`. -- Supported nested values reachable through a view are protected lazily. -- ReadonlyView never intentionally writes to, freezes, seals, prevents extensions on, changes descriptors of, changes prototypes of, serializes, or eagerly traverses the source. -- Owner-side mutations remain visible through an existing view, including property replacement, addition and deletion and collection/Date changes. -- One source object maps to one view inside a membrane, including across object fields, arrays, collections, descriptors, iterators, and cycles. -- Circular graphs terminate and preserve identity. -- Unsupported values fail explicitly when first reached rather than exposing an unsafe reference. -- Runtime and public TypeScript declarations describe the same supported semantics. - -## Non-guarantees and trust model - -- ReadonlyView is not a security boundary or capability sandbox. -- It cannot revoke or protect another reference to the source. -- A function can mutate independently captured state or cause arbitrary external side effects. -- A getter can cause arbitrary side effects. -- A consumer-supplied Proxy can execute arbitrary traps or lie about its target. JavaScript provides no reliable general-purpose Proxy detection. -- Cross-realm and host objects can have environment-specific behavior. Values not classified as supported are rejected where they can be identified. -- Private class fields use JavaScript brand checks and may reject a proxy receiver. - -The implementation avoids unnecessary user-code execution during classification. It does not depend on `Object.prototype.toString` or user-controlled `Symbol.toStringTag` for correctness. - -## Membrane architecture - -Every top-level object call creates a membrane containing two WeakMaps: - -- source to view, for stable identity and cycle handling; -- view to source, for internal collection lookup translation and existing-view recognition. - -The second map may also be represented by module-private WeakMap metadata shared only for view recognition. Neither mapping is exported, and no global strong reference retains a user graph. - -Separate top-level calls create independent membranes: - -```ts -readonlyView(source) !== readonlyView(source); -``` - -Identity is guaranteed inside each membrane. Existing ReadonlyView inputs are idempotent: - -```ts -const first = readonlyView(source); -readonlyView(first) === first; -``` - -The implementation registers a source/view pair before nested wrapping can occur. - -## Shadow targets and Proxy invariants - -ReadonlyView does not directly proxy arbitrary sources. Direct proxying cannot safely replace the value of a non-configurable, non-writable data property, because the Proxy `get` invariant requires the exact target value and would leak a mutable child. - -Instead, views use controlled shadows: - -- ordinary objects and class instances use extensible object-shaped shadows; -- arrays use array shadows so `Array.isArray(view)` remains true; -- functions use callable/constructable shadows where the source permits those operations; -- Map, Set, and Date use object shadows plus dedicated adapters. - -Shadows remain internally extensible. `preventExtensions` on the view always throws, which allows live source keys to appear and disappear without conflicting with `ownKeys` invariants. View mutation traps never forward to the source. - -`ownKeys`, `has`, `get`, and prototype observation delegate to current source state. Descriptor reflection virtualizes source descriptors onto the shadow. Data values and getter results are wrapped. Configurability is normalized when required by the shadow contract; source descriptors remain untouched. The architecture and API documentation state that reflected descriptors describe the view, not an identical copy of source descriptor identity. - -Array `length` and callable shadow properties receive dedicated compatibility tests because their shadows contain unavoidable non-configurable properties. Every trap is justified by a semantic test and an invariant regression test. - -## Mutation interception - -All mutation entry points throw `DirectMutationError`: - -- `set`, including nested properties, array indexes, and array length; -- `deleteProperty`; -- `defineProperty`; -- `setPrototypeOf`; -- `preventExtensions`; -- `Object.assign`, `Reflect.set`, `Reflect.deleteProperty`, and `Reflect.defineProperty` as consequences of those traps; -- Array mutators; -- Map `set`, `delete`, and `clear`; -- Set `add`, `delete`, and `clear`; -- all standard Date setter methods, including local and UTC variants. - -The mutation error is small and stable: - -```ts -class DirectMutationError extends Error { - readonly operation: string; - readonly property?: PropertyKey; - readonly objectKind: string; -} -``` - -Messages identify the operation and safe property representation without serializing or inspecting the source. Symbols are formatted without invoking user code. The exact exported metadata types are kept narrow and documented. - -`UnsupportedTypeError` records the detected kind without retaining or stringifying the rejected object. - -## Reads, descriptors, and reflection - -The following behave naturally and live where Proxy invariants permit: - -- `Object.keys`, `Object.values`, and `Object.entries`; -- own property names and symbols; -- own property descriptors; -- `Reflect.ownKeys`, `Reflect.get`, and `Reflect.has`; -- the `in` operator and `for...in`; -- spread and destructuring; -- `Object.getPrototypeOf`, `instanceof`, and `Array.isArray`. - -Any path exposing an object value passes through the membrane, including descriptors, accessors, iteration results, collection callbacks, and well-known symbols. - -Getters execute with the readonly receiver and returned values are wrapped. A setter is never invoked through the view. Getter side effects are outside the guarantee and are documented. - -## Functions and methods - -Generic functions retain their call signature in `DeepReadonly` and are represented by callable views when runtime wrapping is needed. A normal `view.method()` invocation calls the source function with the readonly receiver. A write through `this` therefore reaches the membrane and throws. - -Calls with another explicit `this` preserve normal JavaScript call semantics; ReadonlyView protects only values reached through the view. Function-owned object properties are deeply wrapped. Construct calls, where supported, wrap the returned object and never expose an unwrapped result. - -Dedicated Map, Set, and Date adapters call native methods with valid source receivers and wrap outputs. Generic method handling never binds these native methods blindly. - -Custom instances expose a readonly view of their prototype. Public fields and public methods follow normal membrane semantics, so writes through method `this` throw. This intentionally makes `view instanceof SourceClass` false: exposing the raw prototype to preserve the comparison would be a mutation escape. Private-field methods and accessors may fail native brand checks because rebinding them to the source would create another escape hatch. These limitations are explicit. - -## Supported values - -| Value | Classification | Semantics | -| ----------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------- | -| primitives, `null`, `undefined`, bigint, symbol | Fully supported | Returned unchanged | -| plain and null-prototype objects | Fully supported | Live property and reflection view | -| Array and tuple | Fully supported | Array identity preserved; mutators rejected | -| Map / ReadonlyMap | Fully supported | Read methods and iteration wrap keys and values; mutators rejected | -| Set / ReadonlySet | Fully supported | Reads and iteration wrap values; mutators rejected | -| Date | Fully supported | Read methods work; complete setter family rejected | -| Function | Supported with documented semantics | Calls allowed; readonly receiver; outputs wrapped; external side effects possible | -| custom class instance | Supported with documented semantics | Prototype viewed; public state protected; `instanceof` false; private brands may reject | -| consumer Proxy | Supported with trust limitation | Membrane does not claim stronger guarantees than source traps permit | -| RegExp | Explicitly unsupported | Stateful native methods and `lastIndex` make generic wrapping unsafe | -| Error | Explicitly unsupported | Engine-specific internal state and mutable custom fields are not claimed safe | -| URL / URLSearchParams | Explicitly unsupported | Native setters and mutators require a dedicated future adapter | -| ArrayBuffer / SharedArrayBuffer | Explicitly unsupported | Mutable backing memory cannot be made readonly by an object Proxy | -| DataView / TypedArray | Explicitly unsupported | Internal slots and backing-memory mutation require separate semantics | -| WeakMap / WeakSet | Explicitly unsupported | Mutating APIs and non-enumerable contents require dedicated semantics | -| Promise | Explicitly unsupported | Internal-slot methods and asynchronously exposed values require dedicated semantics | -| unknown host/native object | Explicitly unsupported when identifiable | Correctness is preferred over accidental method exposure | - -Unsupported values throw lazily when accessed, including when used as a root. Support can expand in future minor releases only with complete semantics and tests. - -## Array semantics - -Index reads, iteration, callbacks, non-mutating methods, spread, and returned nested values use the membrane. Mutating methods throw before touching the source. The complete standardized mutator set includes `copyWithin`, `fill`, `pop`, `push`, `reverse`, `shift`, `sort`, `splice`, and `unshift`. New mutators introduced by the language must be evaluated before being treated as safe. - -Non-mutating methods operate with the view as receiver where generic array semantics allow it, ensuring values encountered by callbacks are views. Tuple typing retains fixed positions and readonly structure. - -## Map and Set semantics - -Map keys and values exposed through `keys`, `values`, `entries`, iteration, and `forEach` are wrapped. `forEach` receives the readonly collection as its third argument. `get` and `has` translate a view from the same membrane back to its source solely for lookup, so both source and view key forms work when the caller already owns them. Unknown objects are passed through as lookup candidates without mutation. - -Set iteration and callbacks expose wrapped values and the readonly Set. `has` applies the same same-membrane translation. Mutation methods always throw. Collection method functions have stable identity when repeatedly read from the same view. - -## Date semantics - -All standardized read methods work with a valid source receiver. All standardized local and UTC setter variants throw. Method classification is centralized, exhaustive against the target JavaScript library, and tested so an incomplete handwritten blacklist cannot silently ship. Owner mutation through the source Date remains live. - -## TypeScript model - -`DeepReadonly` is distributive over unions and handles: - -- primitives and functions; -- mutable and readonly arrays; -- tuples, including readonly tuples and optional/rest members; -- Map and ReadonlyMap as `ReadonlyMap, DeepReadonly>`; -- Set and ReadonlySet as `ReadonlySet>`; -- Date as an unexported `ReadonlyDate` helper that structurally exposes only non-mutating Date members; -- optional and nullable properties; -- symbol keys; -- recursive object types without eager expansion errors. - -Functions preserve calls while their owned properties are readonly where TypeScript can express that intersection cleanly. The public declaration file must not expose internal handler, membrane, shadow, or build-tool types. Dedicated positive and `@ts-expect-error` tests define the contract. - -Types for explicitly unsupported roots remain representable because TypeScript cannot generally reject them without harming inference; runtime rejection and the support matrix are authoritative. The declarations must not falsely replace unsupported native mutation methods with a readonly interface. - -## Performance and memory - -Initial wrapping performs classification, creates one shadow/proxy, and registers WeakMap metadata. It does not walk nested properties or invoke getters. Nested views are created on first exposure and reused thereafter. - -Benchmarks cover creation, first and repeated nested access, large shallow and deep graphs, arrays, Map reads, Set iteration, Date reads, plain access, shallow `Object.freeze`, and recursive deep freeze. Results include environment and methodology. CI runs a functional benchmark smoke test but does not gate noisy timing ratios. - -No process-global strong cache retains sources or views. WeakMap entries become collectible with their membrane objects. - -## Package and compatibility - -The npm package is `@nipe-solutions/readonly-view`, the product name is ReadonlyView, and the repository is `NIPE-Solutions/readonly-view`. The public factory remains `readonlyView`. The package has zero runtime dependencies and uses current stable development dependencies at implementation time. Dependency versions are pinned by the npm lockfile and maintained with Renovate under a low-noise grouped policy. - -Outputs: - -- ESM; -- CommonJS; -- one clean TypeScript declaration entry point; -- source maps only if package verification demonstrates a concrete debugging benefit and the size budget includes them. - -The package uses an explicit exports map, `main`, `module`, `types`, `files`, `sideEffects: false`, complete repository metadata, MIT license metadata, Node engine range, and npm provenance configuration. Runtime support is Node.js 22 and 24, current evergreen Chromium/Firefox/WebKit verified in CI, and modern bundlers. Development and release use Node.js 24 LTS. Deno and Bun are documented only after explicit verification. - -The emitted JavaScript targets a modern baseline compatible with the verified runtime matrix. TypeScript's documented minimum version is determined by a consumer fixture and CI, not assumed from the compiler used to build. - -## Repository structure - -```text -.github/ - ISSUE_TEMPLATE/ - workflows/ -benchmarks/ -docs/ - adr/ - superpowers/ -scripts/ -src/ - builtins/ - errors.ts - membrane.ts - public-types.ts - index.ts -test/ - browser/ - integration/ - package/ - regression/ - types/ - unit/ -website/ -``` - -Files are split by coherent responsibility, not to imitate a large monorepo. `src/index.ts` is the only package entry point. - -## Test strategy - -Development follows strict red-green-refactor cycles. Tests are organized by semantic contract rather than implementation trap count. - -Required suites cover: - -- primitive and root behavior; -- deep and live object behavior; -- shared identity and cycles; -- arrays and every mutation/read route; -- Map, Set, Date, functions, symbols, getters, descriptors, and classes; -- frozen, sealed, non-extensible, non-configurable, and non-writable source cases; -- all reflection and prototype operations; -- direct and nested adversarial mutation attempts; -- descriptor, iterator, callback, method, spread, and destructuring escape routes; -- unsupported values and existing proxies; -- source descriptor/prototype/extensibility snapshots before and after creation and reads; -- randomized graphs using `fast-check` when it materially expands state-space coverage; -- compile-time positive and negative cases; -- real packed-artifact ESM, CommonJS, TypeScript, bundler, tree-shaking, exports, metadata, and file-list contracts; -- Chromium, Firefox, and WebKit runtime behavior. - -Every confirmed defect receives a failing regression test before its fix. - -## Build, size, and quality gates - -Modern TypeScript, ESLint flat config, Prettier, Vitest, Vite/library tooling, and Playwright are used at current mutually compatible versions. Tool selection may change during implementation if an equivalent simpler tool produces cleaner dual-format output; the zero-runtime-dependency and package-contract requirements do not change. - -`npm run check` executes format checking, lint, type checking, unit/integration/regression/type tests, build, API/declaration checks, packed-package verification, size verification, and documentation build. Browser matrix and full benchmarks remain separate documented commands; CI runs browser tests in parallel jobs and a benchmark smoke test. - -Size automation records minified ESM, minified+gzip ESM, declaration size, and packed artifact size. Exact budgets are established from the completed v2 baseline with documented reasonable headroom and then committed as hard CI limits. Budgets are not selected in advance or gamed by removing correctness code. - -## Documentation and collection-ready website - -The README explains the product in the first screen and links to deeper documentation. Every behavioral claim maps to a test. - -The documentation set includes introduction, getting started, core/ownership model, guarantees and non-guarantees, runtime and compile-time behavior, support matrix, type-specific guides, functions/classes, cycles, symbols, descriptors/invariants, API, comparison, migration, performance/memory, security/trust, FAQ, contributing, release process, and selected ADRs. - -The website is a framework-free Vite/TypeScript/CSS static site. It follows the reference repository's philosophy and information architecture: strong first-screen explanation, evidence-oriented feature sections, persistent docs navigation, responsive reading layouts, searchable or easily navigable API content, real runnable examples, and release/support information. Its visual identity is minimal, technical, premium, and consistent with a future collection of small NIPE-quality primitives. - -Collection readiness means shared-looking design tokens, naming conventions, navigation slots, content components, and visual rhythm that could later be reused. It does not introduce `@nipe-solutions/core`, a runtime design package, React, or premature cross-repository abstractions. ReadonlyView remains independently buildable and publishable. - -README and website examples have mirrored compile/runtime tests. Interactive examples execute the real built library and show both rejected view mutations and visible source-side changes. - -## Migration and versioning - -The development version is `2.0.0-alpha.0` until release workflow decisions require another prerelease. Nothing is published automatically during implementation. - -The v1 API is removed: - -- `readonly` becomes `readonlyView`; -- `.value` is removed; -- `internalSet`, validation, error-handler options, validation errors, deep merge, and public internals are removed; -- the owner mutates the source directly. - -The migration guide is explicitly titled “ImmuView v1 to ReadonlyView v2,” explains the package rename and narrower ownership model, and points existing users to the retained v1 tags. No compatibility layer ships in v2. - -## CI and release - -Pull requests and main-branch pushes run install, formatting, lint, types, semantic tests, build, API/declaration checks, tarball consumer tests, size checks, documentation build, benchmark smoke tests, and the three-browser matrix. - -The release workflow is manual and environment-protected. It requires exact version/channel confirmation, a clean quality gate, browser verification, changelog/version/license/package validation, an unpublished-version check, npm trusted publishing/provenance permissions, registry propagation verification, and GitHub release creation. Stable publication is restricted to the main branch and prereleases to an explicit non-latest tag. - -Repository hygiene includes issue forms/templates, a PR template, Renovate configuration, a standard Code of Conduct, security policy using GitHub private vulnerability reporting, contribution guide, changelog with Unreleased/2.0.0 sections, and release documentation. - -## Selected architecture decisions - -Short ADRs record only consequential decisions: - -1. live membrane instead of frozen snapshot; -2. removal of the state-management API; -3. shadow targets for invariant safety; -4. explicit rejection of unsupported built-ins; -5. custom-class and private-field semantics. - -## Acceptance criteria - -The implementation is complete only when the user-provided Definition of Done is traceable to implementation, tests, or explicit documented non-support; `npm run check` succeeds; configured browser tests succeed; the real tarball passes all consumer checks; size baselines and benchmark results are recorded; the final adversarial audit finds no confirmed untested escape hatch; and no package has been published. diff --git a/docs/superpowers/specs/2026-09-03-legal-deployment-release-design.md b/docs/superpowers/specs/2026-09-03-legal-deployment-release-design.md deleted file mode 100644 index 4b93d68..0000000 --- a/docs/superpowers/specs/2026-09-03-legal-deployment-release-design.md +++ /dev/null @@ -1,69 +0,0 @@ -# ReadonlyView Legal, Deployment, and Stable Release Design - -**Status:** Approved on 2026-09-03 - -## Outcome - -ReadonlyView will expose complete bilingual legal pages, deploy its Vite documentation site through Vercel at `https://readonly-view.nipesolutions.com`, align repository automation with the NIPE Solutions reference repository, and publish `@nipe-solutions/readonly-view@2.0.0` under npm's `latest` tag. - -## Legal pages - -The site will publish four stable routes: - -- `/privacy/` -- `/impressum/` -- `/de/datenschutz/` -- `/de/impressum/` - -The text and operator data will be adapted from the NIPE Solutions React Spring Bottom Sheet v5 site. The operator is NIPE Solutions e.U., proprietor Nicholas Petrasek, Achtergasse 10, 1230 Wien, Austria. Contact, VAT, company-register, court, authority, chamber, and trade details remain identical to the approved reference pages. Privacy text will describe Vercel hosting, ordinary request logs, email/contact handling, no non-essential cookies or analytics, GDPR rights, and the Austrian data-protection authority. The pages must not claim legal guarantees beyond the source text. - -Legal pages use the established ReadonlyView visual system: shared masthead and footer, narrow readable measure, explicit language switching, keyboard-visible focus styles, and responsive layouts. The main footer links all English and German legal routes. - -## Website architecture - -The existing framework-free Vite website remains in place. Legal pages are static HTML entry points with shared CSS; Vite is configured as a multi-page build so route directories produce deployable `index.html` files. The site remains zero-JavaScript on legal routes and retains the current interactive module only on the documentation home page. - -The hero contains only the product proposition, install command, and interactive ownership demonstration. It must not use a release-status pill or the phrase `v2 alpha · zero runtime dependencies`. Version and zero-dependency facts belong in the quieter proof/metadata area. - -The documentation home includes concise, copyable code examples for nested objects, owner-side live updates, arrays, Map, Set, Date, circular references, shared identity, and rejected mutations. Examples use the real public API, remain short enough to scan, and are mirrored by automated documentation-example tests rather than presented as decorative code. - -Production metadata, canonical URLs, Open Graph URLs, sitemap, robots file, package homepage, and repository documentation use `https://readonly-view.nipesolutions.com`. - -The tab icon uses the established ReadonlyView brand mark rather than a generic shield or lock: a dark rounded-square `R` core with a blue membrane accent, simplified for legibility at 16×16. The site ships a scalable SVG favicon plus raster browser and Apple-touch variants, with theme colors and manifest metadata shared by every page. - -## Vercel and DNS - -The repository will include a `vercel.json` with `npm ci`, `npm run build:website`, and `website/dist` output. The GitHub repository will be connected to Vercel using Git integration: pull requests receive preview deployments and `main` is the production branch. The custom domain is `readonly-view.nipesolutions.com`; its exact Vercel-provided DNS record will be configured in GoDaddy and verified rather than guessed. - -## GitHub workflows - -Automation follows the reference repository's sequencing and supply-chain model without importing React-specific jobs: - -- current pinned major versions for checkout, Node setup, and artifact upload; -- Node 24 primary quality gate and retained Node 22 compatibility gate; -- browser jobs depend on quality and cover Chromium, Firefox, and WebKit; -- Playwright diagnostics upload only on failure; -- release runs only from `main`, requires exact version/channel/confirmation, verifies package and registry state, uses the protected `npm` environment and OIDC trusted publishing, verifies propagation, and creates a GitHub release; -- packed artifact verification, declaration checks, size budgets, website build, and benchmarks remain required. - -Vercel deployment uses Vercel Git integration, not duplicated GitHub Actions deployment secrets. - -## Stable npm publication - -The package name is `@nipe-solutions/readonly-view`, version `2.0.0`, visibility public, and distribution tag `latest`. Package metadata and the lockfile must agree. The changelog will contain a real `2.0.0` entry and a fresh empty `Unreleased` section. - -Before publication, the exact packed artifact must pass the full release check and browser matrix. npm trusted publishing will be configured for `NIPE-Solutions/readonly-view`, workflow `release.yml`, environment `npm`, if npm permits initial publication through OIDC. If the registry requires an authenticated first publication, the first release may use the already authenticated npm organization account, but it must still publish the verified artifact with provenance when supported. Publication is followed by registry import checks, provenance inspection, and a GitHub `v2.0.0` release. - -The old `immuview` package is outside this change and will be deprecated separately by the owner. - -## Release order - -1. Add and test legal pages and production metadata. -2. Align workflows and release documentation. -3. Run the full local quality, package, website, and browser gates. -4. Merge the reviewed release-preparation branch to `main` and wait for green CI. -5. Link and deploy Vercel, configure GoDaddy DNS, and verify HTTPS/routes. -6. Configure npm/GitHub trusted publishing and protection. -7. Dispatch the stable release and verify npm and GitHub release state. - -Public npm publication must not precede a green release commit on `main`. diff --git a/docs/superpowers/specs/2026-09-04-product-story-and-launch-content.md b/docs/superpowers/specs/2026-09-04-product-story-and-launch-content.md deleted file mode 100644 index 42ef127..0000000 --- a/docs/superpowers/specs/2026-09-04-product-story-and-launch-content.md +++ /dev/null @@ -1,283 +0,0 @@ -# ReadonlyView Product Story and Launch Content Design - -**Date:** 2026-09-04 - -**Status:** Approved - -## Objective - -Make ReadonlyView immediately understandable and compelling to library and SDK -authors without weakening its precise technical claims. Preserve the current -runtime, public API, documentation architecture, visual identity, and -framework-free website. This work changes product communication only. - -The primary message is: - -> Expose live internal data without exposing mutation. - -The supporting definition remains: - -> ReadonlyView creates deeply readonly, runtime-enforced views over mutable -> JavaScript data. The owner retains mutation authority; consumers receive a -> live read surface. - -“Readonly membrane” remains useful technical vocabulary, but it follows the -problem statement rather than leading it. - -## Audience and Jobs - -The primary audience is authors of JavaScript and TypeScript libraries, SDKs, -plugin systems, developer tools, engines, and long-lived service objects. They -need to expose state that consumers can inspect and that owners can continue to -update, without returning a mutable internal reference. - -The content must help a visitor answer, in order: - -1. What problem does this solve? -2. Is that a problem I have? -3. Why not use TypeScript `readonly`, `Object.freeze`, a clone, or Immer? -4. What does the ownership model look like in real code? -5. What guarantees and limitations can I rely on? -6. How do I install and use it? - -## Editorial Principles - -- Lead with the user’s boundary problem, not Proxy terminology. -- Use “owner” and “consumer” consistently. -- Say that the source remains mutable; avoid presenting the source as - immutable. -- Use concrete examples before abstraction. -- Keep every comparison scoped to the problem each tool actually solves. -- State limitations close to claims, including Proxy overhead, unsupported - values, custom-class caveats, and the fact that this is not a sandbox. -- Do not claim “secure,” “zero cost,” “faster than Immer,” “tamper-proof,” or - broad state-management capabilities. -- Do not invent customers, testimonials, adoption numbers, or benchmarks. -- Keep code examples executable and covered by tests. - -## README Design - -The README remains concise enough for npm and GitHub scanning. Its content -order becomes: - -1. Product name and value proposition: “Expose live internal data without - exposing mutation.” -2. One-sentence definition and the existing minimal source/view example. -3. Compact installation command and links to npm, documentation, and GitHub. -4. “Why this exists” using an SDK with private mutable state and a public live - readonly view. -5. “Use ReadonlyView when” with five concrete boundary conditions. -6. Comparison table covering TypeScript `readonly`, `Object.freeze`, deep - freeze, immutable snapshots/Immer, and ReadonlyView. -7. Ownership mental model rendered as a small text diagram. -8. Guarantees, supported values, and “When not to use it.” -9. Performance statement, API summary, and deeper documentation links. - -The README may use a small badge row after the opening example, limited to npm -version, CI, and license. Badges are omitted if their visual weight pushes the -value proposition or example down significantly. Installation alternatives -for pnpm, Yarn, and Bun appear in a compact collapsed or inline form; this does -not constitute a compatibility claim beyond installing the package. - -The comparison table distinguishes “live backing object” from “runtime -protection through the exposed reference.” `Object.freeze` is described as -shallow and as constraining the frozen object itself. Deep freeze is described -as recursive and owner-constraining. Immer is described as producing new state, -not as a direct competitor or a live view. - -## Website Design - -The current visual identity, typography, palette, interactive two-sided demo, -responsive layout, legal routes, and framework-free Vite implementation remain. -No decorative animation or redesign is introduced. - -The home-page journey becomes: - -1. Existing hero, with the headline or lede strengthened by the primary - problem statement. -2. Existing proof strip. -3. New “Why this exists” section centered on the owner/consumer boundary and - an SDK example. -4. New “Built for public read surfaces” section with three materially distinct - scenarios: - - SDK state that stays current after connection changes; - - a registry whose owner can register entries while consumers only read; - - a plugin context that prevents accidental configuration mutation. -5. New compact “Choose the right tool” comparison. -6. New “Good fit / not a fit” section. -7. Existing technical documentation, examples, support matrix, API, migration, - and FAQ. - -Primary calls to action are “Install from npm,” “Read the documentation,” and -“View on GitHub.” They appear near the hero and at the end of the fit section. -The page does not use pricing-style cards, fake social proof, or generic feature -tiles. New structural elements use the existing rules, spacing, blue accent, -and code surfaces. - -The sidebar and on-page navigation gain entries for the new story sections. -Metadata continues to use the canonical production origin. Open Graph copy is -updated to the plain-language value proposition while preserving the existing -image and canonical URL. - -## Documentation Design - -Create these focused documents: - -### `docs/mental-model.md` - -Explain source ownership, the view, the membrane, liveness, laziness, identity, -and the access-path boundary. Include this conceptual flow: - -```text -Owner code ──mutable reference──▶ Source object graph - │ - readonlyView() - │ -Consumer code ◀──readonly access── Readonly membrane -``` - -State explicitly that controlling one access path does not make the underlying -graph globally immutable. - -### `docs/use-cases.md` - -Include complete examples for SDK public state, an internal registry, and a -plugin context. Add shorter treatments of response caches, configuration -managers, shared engine state, and diagnostics. Each major example states the -owner, consumer, why a clone would be stale, and which mutations remain valid. -Include “When not to use ReadonlyView.” - -### `docs/choosing-an-approach.md` - -Compare TypeScript `readonly`, `Object.freeze`, deep freeze, cloning/snapshots, -Immer-style immutable updates, and ReadonlyView. Give each approach a fair -“choose this when” explanation. Avoid declaring a universal winner. - -### `docs/api.md` - -Expand every public export with purpose, signature, minimal example, -guarantees, and edge cases. Cover independent membranes, re-wrapping an -existing view, error metadata, primitive behavior, unsupported roots and -nested values, and the type/runtime caveats of `DeepReadonly`. - -Update the README and website documentation indexes to link these pages. Do not -duplicate detailed reflection, custom-class, or unsupported-type material that -already has an authoritative home; link to it. - -## Executable Example Coverage - -Extend `test/integration/docs-examples.test.ts` with consumer fixtures matching -the new SDK, registry, and plugin examples. Tests verify both halves of the -contract: - -- owner mutations remain visible through an existing view; -- consumer mutation attempts throw `DirectMutationError`; -- rejected attempts leave the source unchanged; -- nested objects and collection values remain protected; -- examples compile under the existing strict type test configuration where - TypeScript-specific mutation assertions are appropriate. - -Extend `scripts/verify-website.mjs` to assert the new home-page sections, core -copy, links, and examples are present in the production build. Extend the -website Playwright suite to verify the new navigation targets and CTA links, -and retain mobile overflow checks. - -No runtime tests or implementation changes are expected because this project -does not alter semantics. - -## Marketing Deliverables - -Create a root `marketing/` directory. It is development material and remains -excluded from the npm tarball by the existing package `files` allowlist. - -### `marketing/launch-announcement.md` - -Provide channel-native drafts for: - -- a concise GitHub/NIPE announcement; -- a personal LinkedIn post; -- a NIPE Solutions LinkedIn post; -- an X/Bluesky thread; -- a short community post that can be adapted only where self-promotion is - permitted. - -Each version leads with the boundary problem, includes one small example, names -the package, links to the canonical site and repository, and includes one -honest limitation. Do not paste identical copy across platforms. - -### `marketing/technical-article.md` - -Draft “Why a readonly Proxy is harder than it looks.” Cover naïve Proxy leaks -through descriptors, method returns, callbacks, iterator values, native -internal slots, and future platform methods. Explain the fail-closed method -inventory without reproducing internal source code. End with when the technique -is appropriate and link to the repository. - -### `marketing/comparison-article.md` - -Draft a practical decision guide comparing compile-time readonly types, -freezing, snapshots, immutable-update libraries, and live readonly views. - -### `marketing/sdk-tutorial.md` - -Draft a step-by-step tutorial that evolves an SDK from returning a mutable -internal state reference to exposing a live readonly view. Cover testing and -limitations. - -### `marketing/mutation-escape-thread.md` - -Draft a compact seven-part technical thread about common readonly Proxy escape -routes. The tone is educational rather than alarmist. - -Every draft includes a short publishing checklist for final link verification, -channel rules, disclosure, formatting, and proofreading. Nothing is posted or -scheduled by this work. - -## Search and Discoverability - -Update package keywords to the focused set: - -```text -readonly, deep-readonly, immutable, immutability, proxy, membrane, -typescript, javascript, live-view, runtime, object, map, set -``` - -Retain accurate page titles, canonical URLs, sitemap entries, Open Graph tags, -and social image references. Copy should naturally contain terms developers -search for, such as “runtime readonly,” “deep readonly,” and “live readonly -view”; do not keyword-stuff headings or prose. - -## Non-Goals - -- No runtime, public API, type, package-name, or support-policy changes. -- No React or framework integration. -- No state-management positioning. -- No analytics, newsletter, comments, search service, or tracking scripts. -- No visual redesign, animation campaign, or documentation framework migration. -- No automatic social posting or use of authenticated social accounts. -- No fabricated benchmarks, endorsements, users, or security claims. - -## Verification and Acceptance - -The change is accepted when: - -- a first-time library author can identify the problem and intended audience - from the README’s opening sections; -- README, website, and focused docs consistently explain owner versus consumer; -- SDK, registry, and plugin examples are executable and tested; -- comparison claims are precise and limitations remain prominent; -- every public export has useful API documentation; -- all marketing drafts are complete, channel-specific, and publication-ready - after final human review; -- the package tarball still excludes website, docs source, tests, and marketing; -- `npm run release:check` passes; -- the full Playwright browser matrix passes in CI; -- no runtime files or public exports change. - -## Delivery Strategy - -Implement this as a dedicated pull request based on `main`, independent of the -post-release evidence PR. Keep commits reviewable by grouping documentation and -README work, website work, marketing drafts, and verification updates. The PR -must state that it changes communication only and does not publish marketing -content or a new npm version. diff --git a/package-lock.json b/package-lock.json index 6b50fd4..b78a254 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2059,9 +2059,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": {