Coherent.js v1.0 — Wave 1: Demolition 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.
Spec: docs/superpowers/specs/2026-05-17-coherent-v1-hardening-design.md — Wave 1.
Goal: Strip 1.0-blocking legacy code, deprecated APIs, and unsubstantiated README marketing claims; produce a green-CI trunk ready for Wave 2 (package consolidation).
Architecture: Pure deletion + module-level throw-shims for whole files being removed. No new feature work. Each task ends in one atomic, CI-green commit so trunk is shippable after every task.
Tech Stack: pnpm workspaces, Vitest, ESM only, Node ≥ 20.
Wave 1 NOT in scope (handled in later waves):
- Deletion of
packages/client/src/hydration.jsitself — its non-legacyHydrateexports (autoHydrate,makeHydratable, etc.) are still consumed bypackages/runtime/, which is itself slated for deletion in Wave 2. Removing the file before runtime is dropped would break runtime tests for no benefit. Wave 1 only removes the public re-exports. - All package consolidation (Wave 2)
- API surface lockdown (Wave 3)
Pre-flight
- Step 1: Confirm clean working tree
Run: cd /Users/thomasdrouvin/Perso/coherent && git status
Expected: only the unrelated package.json modification from the existing session; no other uncommitted changes. If anything else is dirty, stop and investigate.
- Step 2: Establish baseline — full test suite must pass before we start
Run: pnpm test
Expected: all tests pass. If anything fails, fix or document the failure before proceeding — we need a green baseline to detect regressions caused by Wave 1.
- Step 3: Establish baseline — lint and typecheck must pass
Run: pnpm lint && pnpm typecheck
Expected: both green. Same reason as above.
Task A: Remove legacy hydration named exports from @coherent.js/client
Files:
- Modify:
packages/client/src/index.js(lines 36-45) - Modify:
packages/client/types/index.d.ts(lines 693-733) - Modify:
packages/client/types/hydration.d.ts(lines 46-52) - Modify:
packages/cli/test/import-audit.test.js(lines 166-172) - Create:
packages/client/test/legacy-removal.test.js
Why this task: The legacyHydrate alias and six other legacy hydration names are re-exported from the package's public entry. They're flagged for removal in the spec. The underlying hydration.js file stays (Wave 2 problem) because packages/runtime/ still imports those same functions internally — but the public API surface should not advertise them.
- Step 1: Write the failing test
Create packages/client/test/legacy-removal.test.js:
import { describe, it, expect } from 'vitest';
describe('Wave 1: removed public exports', () => {
it('does not export legacyHydrate from @coherent.js/client', async () => {
const mod = await import('../src/index.js');
expect(mod.legacyHydrate).toBeUndefined();
});
it('does not export hydrateAll from @coherent.js/client', async () => {
const mod = await import('../src/index.js');
expect(mod.hydrateAll).toBeUndefined();
});
it('does not export hydrateBySelector from @coherent.js/client', async () => {
const mod = await import('../src/index.js');
expect(mod.hydrateBySelector).toBeUndefined();
});
it('does not export enableClientEvents from @coherent.js/client', async () => {
const mod = await import('../src/index.js');
expect(mod.enableClientEvents).toBeUndefined();
});
it('does not export makeHydratable from @coherent.js/client', async () => {
const mod = await import('../src/index.js');
expect(mod.makeHydratable).toBeUndefined();
});
it('does not export autoHydrate from @coherent.js/client', async () => {
const mod = await import('../src/index.js');
expect(mod.autoHydrate).toBeUndefined();
});
it('does not export registerEventHandler from @coherent.js/client', async () => {
const mod = await import('../src/index.js');
expect(mod.registerEventHandler).toBeUndefined();
});
it('still exports the modern hydrate API', async () => {
const mod = await import('../src/index.js');
expect(typeof mod.hydrate).toBe('function');
});
});- Step 2: Run the new test — confirm it fails as expected
Run: pnpm --filter @coherent.js/client run test -- test/legacy-removal.test.js
Expected: 7 of 8 tests FAIL (all the toBeUndefined ones) because those exports still exist. The still exports the modern hydrate API test should PASS.
- Step 3: Remove the legacy re-export block from
packages/client/src/index.js
Delete lines 36-45:
// Legacy exports for backward compatibility
export {
hydrate as legacyHydrate,
hydrateAll,
hydrateBySelector,
enableClientEvents,
makeHydratable,
autoHydrate,
registerEventHandler,
} from './hydration.js';Replace with a single comment line marking what was removed:
// 1.0: removed legacy hydration re-exports — see docs/migration/1.0#removed-legacy-hydration- Step 4: Remove the legacy declarations from
packages/client/types/index.d.ts
Delete the function declarations for all seven legacy names: legacyHydrate, hydrateAll, hydrateBySelector, enableClientEvents, makeHydratable, autoHydrate, registerEventHandler (the first five start around line 693-733; autoHydrate and registerEventHandler may appear further down — verify by grep). Leave the modern hydrate declaration above them untouched.
Run to verify range: grep -n "^export function \(legacyHydrate\|hydrateAll\|hydrateBySelector\|enableClientEvents\|makeHydratable\|autoHydrate\|registerEventHandler\)" packages/client/types/index.d.ts
Delete each named-export block these point at. Also grep-and-remove any straggling references to those symbols elsewhere in the same file (option types are fine to leave; only the export function declarations go).
- Step 5: Remove legacy names from
packages/client/types/hydration.d.ts
In the export { block at lines 46-52, remove the 7 legacy names so the block contains only modern exports. If the entire block becomes empty after removal, delete it.
- Step 6: Update
packages/cli/test/import-audit.test.jsexpected exports
In the '@coherent.js/client' array (around line 152-173), delete these 7 lines (the legacy names):
'legacyHydrate',
'hydrateAll',
'hydrateBySelector',
'enableClientEvents',
'makeHydratable',
'autoHydrate',
'registerEventHandler'Keep the trailing comma syntax valid — the last remaining entry should not have a trailing comma.
- Step 7: Run the new legacy-removal test — confirm all 8 pass
Run: pnpm --filter @coherent.js/client run test -- test/legacy-removal.test.js
Expected: 8/8 PASS.
- Step 8: Run the affected sibling test suites
Run: pnpm --filter @coherent.js/client run test
Expected: full client package suite green. The core-logic.test.js HMR test will still pass at this point — Task B touches it.
Run: pnpm --filter @coherent.js/cli run test -- test/import-audit.test.js
Expected: import-audit test green (no longer expecting legacy names).
- Step 9: Run typecheck
Run: pnpm typecheck
Expected: green. If TypeScript complains about a legacyHydrate reference somewhere we missed, find it with grep -rn legacyHydrate packages/*/types packages/*/src and remove.
- Step 10: Commit
git add packages/client/src/index.js \
packages/client/types/index.d.ts \
packages/client/types/hydration.d.ts \
packages/client/test/legacy-removal.test.js \
packages/cli/test/import-audit.test.js
git commit -m "$(cat <<'EOF'
refactor(client): remove legacyHydrate and 6 other legacy public exports
Drops legacyHydrate, hydrateAll, hydrateBySelector, enableClientEvents,
makeHydratable, autoHydrate, registerEventHandler from the
@coherent.js/client public API. Underlying hydration.js file remains
for now (consumed by @coherent.js/runtime, both go in Wave 2).
Part of Wave 1 demolition for v1.0 stable. See
docs/superpowers/specs/2026-05-17-coherent-v1-hardening-design.md.
EOF
)"Task B: Replace client/src/hmr.js with a module-level throw-shim
Files:
- Modify:
packages/client/src/hmr.js(replace entire content) - Modify:
packages/client/test/core-logic.test.js(lines 104-112)
Why this task: The legacy hmr.js file is a deprecated compatibility shim that auto-initializes HMR on import. The modular hmr/ directory is the real API. We replace hmr.js with a throw-on-load shim so any direct import gets a clear migration-pointing error rather than silently working or silently breaking later.
- Step 1: Write the failing test for the throw-shim
Edit packages/client/test/core-logic.test.js. Find the existing HMR Core Logic block (around lines 104-112). Replace the should test HMR module structure test with:
it('throws an informative migration error when legacy hmr.js is imported directly', async () => {
await expect(import('../src/hmr.js')).rejects.toThrow(/Coherent\.js 1\.0/);
await expect(import('../src/hmr.js')).rejects.toThrow(/coherentjs\.dev\/docs\/migration\/1\.0/);
});Leave the rest of the HMR Core Logic describe block (subsequent it blocks like should test HMR message processing logic) untouched.
- Step 2: Run the test — confirm it fails
Run: pnpm --filter @coherent.js/client run test -- test/core-logic.test.js
Expected: the new test FAILS (the import currently succeeds because hmr.js is still a real module).
- Step 3: Replace
packages/client/src/hmr.jscontent with a throw-shim
Overwrite the entire file with:
/**
* Coherent.js HMR — legacy module entrypoint
*
* REMOVED in 1.0. This file existed in beta to auto-initialize HMR on import.
* Direct imports of this path now throw immediately so callers see the
* migration instruction instead of silent failures further down the call stack.
*
* @module @coherent.js/client/hmr
*/
throw new Error(
"Coherent.js 1.0: importing '@coherent.js/client/src/hmr.js' was removed. " +
"Import { hmrClient } from '@coherent.js/client' and call hmrClient.connect() instead. " +
"See https://coherentjs.dev/docs/migration/1.0#removed-client-hmr-shim"
);- Step 4: Run the test — confirm it passes
Run: pnpm --filter @coherent.js/client run test -- test/core-logic.test.js
Expected: all tests in the file PASS, including the new throw assertion.
- Step 5: Run the full client package test suite
Run: pnpm --filter @coherent.js/client run test
Expected: green. If any other test in the client package imports ../src/hmr.js directly, it will now throw — update those imports to use ../src/hmr/index.js or the package export hmrClient.
- Step 6: Commit
git add packages/client/src/hmr.js packages/client/test/core-logic.test.js
git commit -m "$(cat <<'EOF'
refactor(client): replace legacy hmr.js with throw-on-load migration shim
Direct imports of @coherent.js/client/src/hmr.js now throw with a
migration URL. Modular @coherent.js/client/src/hmr/* is unchanged and
remains the supported API.
Part of Wave 1 demolition for v1.0 stable.
EOF
)"Task C: Remove forms deprecated SPA APIs and their tests
Files:
- Modify:
packages/forms/src/index.js(lines 23-35) - Modify:
packages/forms/src/form-builder.js(lines 572-588) - Modify:
packages/forms/build.mjs(line 10) - Delete:
packages/forms/src/forms.js - Delete:
packages/forms/src/advanced-validation.js - Delete:
packages/forms/test/forms.test.js
Why this task: Three deprecated re-exports in forms/index.js (createForm, formValidators, enhancedForm, plus the advanced-validation wildcard re-export) and the deprecated createForm alias in form-builder.js. The underlying files (forms.js, advanced-validation.js) are not imported by anything except the package's own deprecated surface and the package's own test for the deprecated surface — confirmed by external grep returning only the build manifest. Hard delete is safe.
- Step 1: Confirm no external consumers of the deprecated symbols
Run from repo root:
grep -rn "\bcreateForm\b\|\bformValidators\b\|\benhancedForm\b" \
packages examples website \
--include="*.js" --include="*.ts" 2>/dev/null \
| grep -v "/dist/" \
| grep -v "packages/forms/src/forms.js" \
| grep -v "packages/forms/src/form-builder.js" \
| grep -v "packages/forms/src/index.js" \
| grep -v "packages/forms/test/forms.test.js"Expected: empty output. If any consumer turns up, stop and update them to use createFormBuilder + hydrateForm first, then resume this task.
Run:
grep -rn "from.*['\"].*advanced-validation['\"]" \
packages examples website \
--include="*.js" --include="*.ts" 2>/dev/null \
| grep -v "/dist/" \
| grep -v "packages/forms/src/index.js"Expected: empty output (only forms/build.mjs will reference it, and Step 5 handles that).
- Step 2: Write the failing test for removed exports
Create packages/forms/test/wave1-removal.test.js:
import { describe, it, expect } from 'vitest';
describe('Wave 1: removed deprecated forms exports', () => {
it('does not export createForm from @coherent.js/forms', async () => {
const mod = await import('../src/index.js');
expect(mod.createForm).toBeUndefined();
});
it('does not export formValidators from @coherent.js/forms', async () => {
const mod = await import('../src/index.js');
expect(mod.formValidators).toBeUndefined();
});
it('does not export enhancedForm from @coherent.js/forms', async () => {
const mod = await import('../src/index.js');
expect(mod.enhancedForm).toBeUndefined();
});
it('still exports the modern createFormBuilder and hydrateForm', async () => {
const mod = await import('../src/index.js');
expect(typeof mod.createFormBuilder).toBe('function');
expect(typeof mod.hydrateForm).toBe('function');
});
});- Step 3: Run the test — confirm 3 of 4 fail
Run: pnpm --filter @coherent.js/forms run test -- test/wave1-removal.test.js
Expected: the 3 toBeUndefined tests FAIL; the modern-export test PASSES.
- Step 4: Replace
packages/forms/src/index.jscontent
Overwrite the file with:
/**
* Coherent.js Forms
*
* SSR + Hydration form system
*
* @module forms
*/
// SERVER-SIDE: Build forms with validation metadata
export { FormBuilder, createFormBuilder, buildForm } from './form-builder.js';
// CLIENT-SIDE: Hydrate server-rendered forms
export { hydrateForm } from './form-hydration.js';
// SHARED: Validators (used by both server and client)
export { validators, FormValidator, createValidator, validate } from './validation.js';
export * from './validators.js';
// 1.0: removed deprecated SPA exports (createForm, formValidators, enhancedForm,
// advanced-validation). See docs/migration/1.0#removed-forms-spa-apis.- Step 5: Remove the deprecated
createFormalias frompackages/forms/src/form-builder.js
Delete lines 572-588 (the block starting with /** Create a form with UI builder capabilities (for SSR rendering) and ending with the closing } of createForm). The export createFormBuilder immediately above stays.
Verify the next function declaration (Quick form builder helper) is still intact and the file ends cleanly.
- Step 6: Delete the deprecated source files
Run:
rm packages/forms/src/forms.js
rm packages/forms/src/advanced-validation.js- Step 7: Delete the test file for the removed surface
Run:
rm packages/forms/test/forms.test.js- Step 8: Update
packages/forms/build.mjsto drop the deleted input
Open packages/forms/build.mjs. Find line 10 ('src/advanced-validation.js') inside the inputs array. Remove that line. Fix trailing comma if needed.
- Step 9: Run the wave1-removal test — confirm all 4 pass
Run: pnpm --filter @coherent.js/forms run test -- test/wave1-removal.test.js
Expected: 4/4 PASS.
- Step 10: Run the full forms package test suite
Run: pnpm --filter @coherent.js/forms run test
Expected: green. form-builder.test.js and validators.test.js exercise APIs we kept and should still pass.
- Step 11: Run the package build to confirm no stale references
Run: pnpm --filter @coherent.js/forms run build
Expected: green. No Cannot find module errors for the deleted files.
- Step 12: Commit
git add packages/forms/src/index.js \
packages/forms/src/form-builder.js \
packages/forms/build.mjs \
packages/forms/test/wave1-removal.test.js
git rm packages/forms/src/forms.js \
packages/forms/src/advanced-validation.js \
packages/forms/test/forms.test.js
git commit -m "$(cat <<'EOF'
refactor(forms): remove deprecated SPA form APIs
Removes createForm, formValidators, enhancedForm public exports plus the
advanced-validation wildcard re-export. Deletes the underlying
forms.js and advanced-validation.js source files and the forms.test.js
suite that exercised them. The deprecated createForm alias in
form-builder.js is also removed in favor of createFormBuilder.
No external consumers found in packages/, examples/, or website/.
Part of Wave 1 demolition for v1.0 stable.
EOF
)"Task D: Strip unsubstantiated marketing claims from README
Files:
- Modify:
README.md
Why this task: The README claims "42.7% performance improvement over traditional OOP" and "95%+ cache hit rates" as headline framework properties. Per the spec, these get removed because the OOP comparison requires maintaining a benchmark fixture nobody is going to maintain, and the cache hit rate is workload-dependent (not a property of the framework). Keeping unverifiable claims undermines the credibility of the verifiable ones (bundle size, tree-shake reduction).
This task does not require a code test — README is documentation. The verification is visual inspection plus a grep that the strings are gone.
- Step 1: Edit
README.md
Make the following edits exactly:
- Delete line 14:
- **🏗️ 42.7% performance improvement** over traditional OOP - Delete line 28:
- **LRU Caching**: Automatic performance optimization with 95%+ cache hit ratesReplace with:- **LRU Caching**: Component-level caching with configurable LRU eviction - In the benchmarks table (around lines 87-95), delete the entire row:
| Cache Hit Rate | **95%+** | 70%+ | - Around line 254, delete the line:
- ✅ **Architecture**: 42.7% improvement over traditional OOP
- Step 2: Verify all four strings are gone
Run:
grep -nE "42\.7%|95\%\+" README.mdExpected: empty output. If anything remains, repeat Step 1 on the missed line.
- Step 3: Visual smoke check
Run: head -100 README.md
Confirm the headline bullet list, the benchmarks table, and the "Production-Ready" section all read sensibly after the deletions (no orphaned bullets, no broken table borders, no dangling "and" connectives).
- Step 4: Commit
git add README.md
git commit -m "$(cat <<'EOF'
docs(readme): remove unsubstantiated perf claims (42.7% vs OOP, 95% cache hit)
The "42.7% vs OOP" claim requires maintaining a benchmark fixture that
does not exist; the "95%+ cache hit rate" is workload-dependent, not a
property of the framework. Both are removed to leave the README only
asserting numbers the CI perf gates (Wave 3) will actually defend.
Part of Wave 1 demolition for v1.0 stable.
EOF
)"Task E: Repo cruft cleanup
Files:
- Delete:
.DS_Store(root),website/.DS_Store - Delete:
output.txt - Modify:
.gitignore(add.DS_Store,output.txt)
Why this task: Untracked-but-not-ignored junk files sit in the working tree forever and clutter every git status. None of these are tracked, but they keep coming back. Updating .gitignore prevents recurrence.
- Step 1: Confirm none are tracked
Run from repo root: git ls-files | grep -E '\.DS_Store$|^output\.txt[[[COHERENT_CONTENT_PLACEHOLDER]]]#39;
Expected: empty output. If anything is tracked, stop and use git rm rather than plain rm.
- Step 2: Delete the local files
Run:
rm -f .DS_Store website/.DS_Store output.txt
find . -name ".DS_Store" -not -path "./node_modules/*" -delete- Step 3: Add entries to
.gitignore
Open .gitignore. After the existing .idea/ line (around line 38), add:
# OS cruft
.DS_Store
Thumbs.db
# Local debug dumps
output.txt- Step 4: Verify clean working tree state
Run: git status --short
Expected output includes the .gitignore modification and nothing else from this task. No .DS_Store or output.txt should appear as untracked.
- Step 5: Commit
git add .gitignore
git commit -m "$(cat <<'EOF'
chore: ignore .DS_Store and local debug dumps
Removes accumulated .DS_Store files and an old output.txt debug dump
from the working tree, and updates .gitignore to keep them out.
Part of Wave 1 demolition for v1.0 stable.
EOF
)"Task F: Wave 1 verification commit + CHANGELOG entry
Files:
- Modify:
CHANGELOG.md(add Unreleased entry)
Why this task: After five surgical commits, run the full quality gate end-to-end to prove trunk is green, and record a CHANGELOG entry so the work is visible in the release pipeline.
- Step 1: Full test suite
Run: pnpm test
Expected: green. Any failure is a regression introduced by Wave 1 — diagnose before continuing. Common suspects: a test in another package that imports a removed name (we grepped, but grep can miss dynamic imports or template strings).
- Step 2: Lint
Run: pnpm lint
Expected: 0 warnings (project policy is --max-warnings=0).
- Step 3: Typecheck
Run: pnpm typecheck
Expected: green.
- Step 4: Full build
Run: pnpm build
Expected: green. All 26 packages still build (none of the deletions broke a downstream package's imports).
- Step 5: Add an Unreleased entry to
CHANGELOG.md
Open CHANGELOG.md. Find the ## [Unreleased] heading (currently empty per the file). Below it, add:
### Removed
- **BREAKING (client):** Removed `legacyHydrate`, `hydrateAll`, `hydrateBySelector`, `enableClientEvents`, `makeHydratable`, `autoHydrate`, `registerEventHandler` from `@coherent.js/client` public exports. Use `hydrate()` instead. See [migration guide](https://coherentjs.dev/docs/migration/1.0#removed-legacy-hydration).
- **BREAKING (client):** `@coherent.js/client/src/hmr.js` direct imports now throw a migration error. Import `{ hmrClient }` from `@coherent.js/client` instead.
- **BREAKING (forms):** Removed `createForm`, `formValidators`, `enhancedForm`, and `advanced-validation` exports from `@coherent.js/forms`. Use `createFormBuilder` + `hydrateForm` instead.
### Changed
- **docs (readme):** Removed "42.7% improvement over OOP" and "95%+ cache hit rate" claims. The first required an unmaintained benchmark fixture; the second is workload-dependent and was misleading as a framework-level property.
### Maintenance
- `.gitignore` now covers `.DS_Store` and local debug dumps.- Step 6: Commit
git add CHANGELOG.md
git commit -m "$(cat <<'EOF'
docs(changelog): record Wave 1 demolition
Adds Unreleased entries for the legacy hydration export removals,
hmr.js throw-shim, forms deprecated SPA API removals, README claim
cleanup, and gitignore additions.
Closes Wave 1 of v1.0 stable hardening. Trunk is green and ready for
Wave 2 (package consolidation).
EOF
)"- Step 7: Final confirmation
Run: git log --oneline -10
Expected: 6 new commits on top of the spec commit, each with a clear conventional-commit prefix (refactor, docs, chore).
Run: git status
Expected: only the pre-existing package.json modification from earlier in the session (unrelated to Wave 1). Nothing else dirty.
Post-Wave-1 Handoff
Wave 1 is done. Next plans to write (each gets its own document):
- Wave 2 — Restructure: 26 → 12 packages, including the final deletion of
client/src/hydration.js(which becomes safe onceruntimeis dropped). Drafted after Wave 1 merges so the file/import map reflects post-cleanup reality. - Wave 3 — Lockdown:
exportsfield audit,@internalsweep, API surface snapshot tests,experimental_prefix pass, perf CI gates. - Wave 4 — Browser parity: HMR dev-server WebSocket implementation in
cli, Playwright E2E suite, VS Code marketplace publish. - Wave 5 — Release: Migration guide finalization,
1.0.0-rc.1tag, soak period,1.0.0tag.
Reasoning for plan-per-wave: each wave produces shippable trunk and the post-cleanup state materially changes the file/import topology for the next wave. Writing all five plans up front would generate stale instructions by Wave 3.